1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "sci/sci.h"
24 #include "sci/engine/kernel.h"
25 #include "sci/engine/script.h"
26 #include "sci/engine/state.h"
27 #include "sci/engine/features.h"
28 #include "sci/engine/script_patches.h"
29 #ifdef ENABLE_SCI32
30 #include "sci/engine/guest_additions.h"
31 #endif
32 
33 #include "common/util.h"
34 
35 namespace Sci {
36 
37 // IMPORTANT:
38 // every patch entry needs the following:
39 //  - script number (pretty obvious)
40 //
41 //  - apply count
42 //     specifies the number of times a patch is supposed to get applied.
43 //     Most of the time, it should be 1.
44 //
45 //  - magicDWORD + magicOffset
46 //     please ALWAYS put 0 for those two. Both will get filled out at runtime by the patcher.
47 //
48 //  - signature data (is used to identify certain script code, that needs patching)
49 //     every signature needs to contain SIG_MAGICDWORD once.
50 //      The following 4 bytes after SIG_MAGICDWORD - which don't have to be fixed, you may for example
51 //      use SIG_SELECTOR16, will get used to quickly search for a partly match before verifying that
52 //      the whole signature actually matches. If it's not included, the script patcher will error() out
53 //      right when loading up the game.
54 //     If selector-IDs are included, please use SIG_SELECTOR16 + SIG_SELECTOR8 [1]. Simply
55 //      specify the selector that way, so that the patcher will search for the specific
56 //      selector instead of looking for a hardcoded value. Selectors may not be the same
57 //      between game versions.
58 //     For UINT16s either use SIG_UINT16 or SIG_SELECTOR16.
59 //      Macintosh versions of SCI games are using BE ordering instead of LE since SCI1.1 for UINT16s in scripts
60 //      By using those 2 commands, it's possible to make patches work for PC and Mac versions of the same game.
61 //     You may also skip bytes by using the SIG_ADDTOOFFSET command
62 //     Every signature data needs to get terminated using SIGNATURE_END
63 //
64 //  - patch data (is used for actually patching scripts)
65 //     When a match is found, the patch data will get applied.
66 //     Patch data is similar to signature data. Just use PATCH_SELECTOR16 + PATCH_SELECTOR8 [1]
67 //      for patching in selectors.
68 //     There are also patch specific commands.
69 //     Those are PATCH_GETORIGINALBYTE, which fetches a byte from the original script
70 //      and PATCH_GETORIGINALBYTEADJUST, which does the same but gets a second value
71 //      from the uint16 array and uses that value to adjust the original byte.
72 //     Every patch data needs to get terminated using PATCH_END
73 //
74 //  - and please always add a comment about why the patch was done and what's causing issues.
75 //     If possible make sure, that the patch works on localized (or just different) game versions
76 //      as well in case those need patching too.
77 //
78 // [1] - selectors need to get specified in selectorTable[] and ScriptPatcherSelectors-enum
79 //        before they can get used using the SIG_SELECTORx and PATCH_SELECTORx commands.
80 //        You have to use the exact same order in both the table and the enum, otherwise
81 //        it won't work.
82 //        ATTENTION: selectors will only work here, when they are also in SelectorCache (selector.h)
83 
84 static const char *const selectorNameTable[] = {
85 	"cycles",       // system selector
86 	"seconds",      // system selector
87 	"init",         // system selector
88 	"dispose",      // system selector
89 	"new",          // system selector
90 	"curEvent",     // system selector
91 	"disable",      // system selector
92 	"doit",         // system selector
93 	"show",         // system selector
94 	"x",            // system selector
95 	"cel",          // system selector
96 	"setMotion",    // system selector
97 	"overlay",      // system selector
98 	"setPri",       // system selector - for setting priority
99 	"play",         // system selector
100 	"number",       // system selector
101 	"setScript",    // system selector
102 	"setCycle",     // system selector
103 	"setStep",      // system selector
104 	"cycleSpeed",   // system selector
105 	"handsOff",     // system selector
106 	"handsOn",      // system selector
107 	"type",         // system selector
108 	"localize",     // Freddy Pharkas
109 	"roomFlags",    // Iceman
110 	"put",          // Police Quest 1 VGA
111 	"approachVerbs", // Police Quest 1 VGA, QFG4
112 	"newRoom",      // Police Quest 3, GK1
113 	"changeState",  // Quest For Glory 1 VGA, QFG4
114 	"hide",         // Quest For Glory 1 VGA, QFG4
115 	"say",          // Quest For Glory 1 VGA, QFG4
116 	"script",       // Quest For Glory 1 VGA
117 	"solvePuzzle",  // Quest For Glory 3
118 	"curIcon",      // Quest For Glory 3, QFG4
119 	"curInvIcon",   // Quest For Glory 3, QFG4
120 	"timesShownID", // Space Quest 1 VGA
121 	"startText",    // King's Quest 6 CD / Laura Bow 2 CD for audio+text support
122 	"startAudio",   // King's Quest 6 CD / Laura Bow 2 CD for audio+text support
123 	"modNum",       // King's Quest 6 CD / Laura Bow 2 CD for audio+text support
124 	"has",          // King's Quest 6, GK1
125 	"modeless",     // King's Quest 6 CD
126 	"cycler",       // Space Quest 4 / system selector
127 	"setCel",       // Space Quest 4, Phant2, GK1
128 	"addToPic",     // Space Quest 4
129 	"stop",         // Space Quest 4
130 	"canControl",   // Space Quest 4
131 	"looper",       // Space Quest 4
132 	"nMsgType",     // Space Quest 4
133 	"doVerb",       // Space Quest 4
134 	"setRegions",   // Space Quest 4
135 	"setSpeed",     // Space Quest 5, QFG4
136 	"loop",         // Laura Bow 1 Colonel's Bequest, QFG4
137 	"setLoop",      // Laura Bow 1 Colonel's Bequest, QFG4
138 	"ignoreActors", // Laura Bow 1 Colonel's Bequest
139 	"setVol",       // Laura Bow 2 CD
140 	"at",           // Longbow, QFG4
141 	"owner",        // Longbow, QFG4
142 	"delete",       // EcoQuest 1
143 	"size",         // EcoQuest 1
144 	"signal",       // EcoQuest 1, GK1
145 	"obstacles",    // EcoQuest 1, QFG4
146 	"handleEvent",  // EcoQuest 2, Shivers
147 #ifdef ENABLE_SCI32
148 	"newWith",      // SCI2 array script
149 	"posn",         // SCI2 benchmarking script
150 	"printLang",    // GK2
151 	"view",         // RAMA benchmarking, GK1, QFG4
152 	"fade",         // Shivers
153 	"test",         // Torin
154 	"get",          // Torin, GK1
155 	"normalize",    // GK1
156 	"set",          // Torin
157 	"clear",        // Torin
158 	"masterVolume", // SCI2 master volume reset
159 	"data",         // Phant2, QFG4
160 	"format",       // Phant2
161 	"setSize",      // Phant2
162 	"iconV",        // Phant2
163 	"update",       // Phant2
164 	"xOff",         // Phant2
165 	"fore",         // KQ7
166 	"back",         // KQ7
167 	"font",         // KQ7
168 	"setHeading",   // KQ7
169 	"setScale",     // LSL6hires, QFG4
170 	"setScaler",    // LSL6hires, QFG4
171 	"readWord",     // LSL7, Phant1, Torin
172 	"points",       // PQ4
173 	"select",       // PQ4
174 	"addObstacle",  // QFG4
175 	"handle",       // RAMA
176 	"saveFilePtr",  // RAMA
177 	"priority",     // RAMA
178 	"plane",        // RAMA
179 	"state",        // RAMA
180 	"getSubscriberObj", // RAMA
181 	"advanceCurIcon", // QFG4
182 	"amount",       // QFG4
183 	"claimed",      // QFG4
184 	"cue",          // QFG4
185 	"getCursor",    // QFG4
186 	"heading",      // QFG4
187 	"moveSpeed",    // QFG4
188 	"register",     // QFG4
189 	"sayMessage",   // QFG4
190 	"setCursor",    // QFG4
191 	"setLooper",    // QFG4
192 	"useStamina",   // QFG4
193 	"value",        // QFG4
194 #endif
195 	NULL
196 };
197 
198 enum ScriptPatcherSelectors {
199 	SELECTOR_cycles = 0,
200 	SELECTOR_seconds,
201 	SELECTOR_init,
202 	SELECTOR_dispose,
203 	SELECTOR_new,
204 	SELECTOR_curEvent,
205 	SELECTOR_disable,
206 	SELECTOR_doit,
207 	SELECTOR_show,
208 	SELECTOR_x,
209 	SELECTOR_cel,
210 	SELECTOR_setMotion,
211 	SELECTOR_overlay,
212 	SELECTOR_setPri,
213 	SELECTOR_play,
214 	SELECTOR_number,
215 	SELECTOR_setScript,
216 	SELECTOR_setCycle,
217 	SELECTOR_setStep,
218 	SELECTOR_cycleSpeed,
219 	SELECTOR_handsOff,
220 	SELECTOR_handsOn,
221 	SELECTOR_type,
222 	SELECTOR_localize,
223 	SELECTOR_roomFlags,
224 	SELECTOR_put,
225 	SELECTOR_approachVerbs,
226 	SELECTOR_newRoom,
227 	SELECTOR_changeState,
228 	SELECTOR_hide,
229 	SELECTOR_say,
230 	SELECTOR_script,
231 	SELECTOR_solvePuzzle,
232 	SELECTOR_curIcon,
233 	SELECTOR_curInvIcon,
234 	SELECTOR_timesShownID,
235 	SELECTOR_startText,
236 	SELECTOR_startAudio,
237 	SELECTOR_modNum,
238 	SELECTOR_has,
239 	SELECTOR_modeless,
240 	SELECTOR_cycler,
241 	SELECTOR_setCel,
242 	SELECTOR_addToPic,
243 	SELECTOR_stop,
244 	SELECTOR_canControl,
245 	SELECTOR_looper,
246 	SELECTOR_nMsgType,
247 	SELECTOR_doVerb,
248 	SELECTOR_setRegions,
249 	SELECTOR_setSpeed,
250 	SELECTOR_loop,
251 	SELECTOR_setLoop,
252 	SELECTOR_ignoreActors,
253 	SELECTOR_setVol,
254 	SELECTOR_at,
255 	SELECTOR_owner,
256 	SELECTOR_delete,
257 	SELECTOR_size,
258 	SELECTOR_signal,
259 	SELECTOR_obstacles,
260 	SELECTOR_handleEvent
261 #ifdef ENABLE_SCI32
262 	,
263 	SELECTOR_newWith,
264 	SELECTOR_posn,
265 	SELECTOR_printLang,
266 	SELECTOR_view,
267 	SELECTOR_fade,
268 	SELECTOR_test,
269 	SELECTOR_get,
270 	SELECTOR_normalize,
271 	SELECTOR_set,
272 	SELECTOR_clear,
273 	SELECTOR_masterVolume,
274 	SELECTOR_data,
275 	SELECTOR_format,
276 	SELECTOR_setSize,
277 	SELECTOR_iconV,
278 	SELECTOR_update,
279 	SELECTOR_xOff,
280 	SELECTOR_fore,
281 	SELECTOR_back,
282 	SELECTOR_font,
283 	SELECTOR_setHeading,
284 	SELECTOR_setScale,
285 	SELECTOR_setScaler,
286 	SELECTOR_readWord,
287 	SELECTOR_points,
288 	SELECTOR_select,
289 	SELECTOR_addObstacle,
290 	SELECTOR_handle,
291 	SELECTOR_saveFilePtr,
292 	SELECTOR_priority,
293 	SELECTOR_plane,
294 	SELECTOR_state,
295 	SELECTOR_getSubscriberObj,
296 	SELECTOR_advanceCurIcon,
297 	SELECTOR_amount,
298 	SELECTOR_claimed,
299 	SELECTOR_cue,
300 	SELECTOR_getCursor,
301 	SELECTOR_heading,
302 	SELECTOR_moveSpeed,
303 	SELECTOR_register,
304 	SELECTOR_sayMessage,
305 	SELECTOR_setCursor,
306 	SELECTOR_setLooper,
307 	SELECTOR_useStamina,
308 	SELECTOR_value
309 #endif
310 };
311 
312 #ifdef ENABLE_SCI32
313 // It is not possible to change the directory for ScummVM save games, so disable
314 // the "change directory" button in the standard save dialogue
315 static const uint16 sci2ChangeDirSignature[] = {
316 	0x72, SIG_ADDTOOFFSET(+2), // lofsa changeDirI
317 	0x4a, SIG_UINT16(0x0004),  // send 4
318 	SIG_MAGICDWORD,
319 	0x36,                      // push
320 	0x35, 0xf7,                // ldi $f7
321 	0x12,                      // and
322 	0x36,                      // push
323 	SIG_END
324 };
325 
326 static const uint16 sci2ChangeDirPatch[] = {
327 	PATCH_ADDTOOFFSET(+3),    // lofsa changeDirI
328 	PATCH_ADDTOOFFSET(+3),    // send 4
329 	PATCH_ADDTOOFFSET(+1),    // push
330 	0x35, 0x00,               // ldi 0
331 	PATCH_END
332 };
333 
334 // Save game script hardcodes the maximum number of save games to 20, but
335 // this is an artificial constraint that does not apply to ScummVM
336 static const uint16 sci2NumSavesSignature1[] = {
337 	SIG_MAGICDWORD,
338 	0x8b, 0x02,                    // lsl local[2]
339 	0x35, 0x14,                    // ldi 20
340 	0x22,                          // lt?
341 	SIG_END
342 };
343 
344 static const uint16 sci2NumSavesPatch1[] = {
345 	PATCH_ADDTOOFFSET(+2),         // lsl local[2]
346 	0x35, 0x63,                    // ldi 99
347 	PATCH_END
348 };
349 
350 static const uint16 sci2NumSavesSignature2[] = {
351 	SIG_MAGICDWORD,
352 	0x8b, 0x02,                    // lsl local[2]
353 	0x35, 0x14,                    // ldi 20
354 	0x1a,                          // eq?
355 	SIG_END
356 };
357 
358 static const uint16 sci2NumSavesPatch2[] = {
359 	PATCH_ADDTOOFFSET(+2),         // lsl local[2]
360 	0x35, 0x63,                    // ldi 99
361 	PATCH_END
362 };
363 
364 // Phantasmagoria & SQ6 try to initialize the first entry of an int16 array
365 // using an empty string, which is not valid (it should be a number)
366 static const uint16 sci21IntArraySignature[] = {
367 	0x38, SIG_SELECTOR16(newWith), // pushi newWith
368 	0x7a,                          // push2
369 	0x39, 0x04,                    // pushi $4
370 	0x72, SIG_ADDTOOFFSET(+2),     // lofsa string ""
371 	SIG_MAGICDWORD,
372 	0x36,                          // push
373 	0x51, 0x0b,                    // class IntArray
374 	0x4a, 0x08,                    // send $8
375 	SIG_END
376 };
377 
378 static const uint16 sci21IntArrayPatch[] = {
379 	PATCH_ADDTOOFFSET(+6),      // push $b9; push2; pushi $4
380 	0x76,                       // push0
381 	0x34, PATCH_UINT16(0x0001), // ldi 0001 (waste bytes)
382 	PATCH_END
383 };
384 
385 // Most SCI32 games have a video performance benchmarking loop at the
386 // beginning of the game. Running this benchmark with calls to
387 // `OSystem::updateScreen` will often cause the benchmark to return a low value,
388 // which causes games to disable some visual effects. Running without calls to
389 // `OSystem::updateScreen` on any reasonably modern CPU will cause the benchmark
390 // to overflow, leading to randomly disabled effects. This patch changes the
391 // benchmarking code to always return the game's maximum speed value.
392 //
393 // Applies to at least: GK1 floppy, PQ4 floppy, PQ4CD, LSL6hires, Phant1,
394 // Shivers, SQ6
395 static const uint16 sci2BenchmarkSignature[] = {
396 	SIG_MAGICDWORD,
397 	0x38, SIG_SELECTOR16(init),  // pushi init
398 	0x76,                        // push0
399 	0x38, SIG_SELECTOR16(posn),  // pushi posn
400 	SIG_END
401 };
402 
403 static const uint16 sci2BenchmarkPatch[] = {
404 	0x7a,                         // push2
405 	0x38, SIG_UINT16(64908),      // pushi 64908
406 	0x76,                         // push0
407 	0x43, 0x03, SIG_UINT16(0x04), // callk DisposeScript[3], 4
408 	0x48,                         // ret
409 	PATCH_END
410 };
411 
412 // The init code that runs in many SCI32 games unconditionally resets the music
413 // volume, but the game should always use the volume stored in ScummVM.
414 // Applies to at least: LSL6hires, MGDX, PQ:SWAT, QFG4
415 static const uint16 sci2VolumeResetSignature[] = {
416 	SIG_MAGICDWORD,
417 	0x38, SIG_SELECTOR16(masterVolume), // pushi masterVolume
418 	0x78,                               // push1
419 	0x39, SIG_ADDTOOFFSET(+1),          // pushi [default volume]
420 	0x81, 0x01,                         // lag global[1]
421 	0x4a, SIG_UINT16(0x0006),           // send 6
422 	SIG_END
423 };
424 
425 static const uint16 sci2VolumeResetPatch[] = {
426 	0x32, PATCH_UINT16(0x0008),         // jmp 8 [past volume reset]
427 	PATCH_END
428 };
429 
430 // At least Gabriel Knight 1 and Police Quest 4 floppy have a broken Str::strip inside script 64918.
431 // The code never passes over the actual string to kStringTrim, so that would not work and also trigger
432 // a signature mismatch.
433 // Localized version of Police Quest 4 were also affected.
434 // Gabriel Knight although affected doesn't seem to ever call the code, so there is no reason to patch it.
435 // Police Quest 4 CD got this fixed.
436 static const uint16 sci2BrokenStrStripSignature[] = {
437 	SIG_MAGICDWORD,
438 	0x85, 0x06,                         // lat temp[6]
439 	0x31, 0x10,                         // bnt [jump to code that passes 2 parameters]
440 	0x38, SIG_UINT16(0x00c2),           // pushi 00c2 (callKernel)
441 	0x38, SIG_UINT16(0x0003),           // pushi 03
442 	0x39, 0x0e,                         // pushi 0e
443 	0x8d, 0x0b,                         // lst temp[0b]
444 	0x36,                               // push
445 	0x54, SIG_UINT16(0x000a),           // self 0a
446 	0x33, 0x0b,                         // jmp [ret]
447 	// 2 parameter code
448 	0x38, SIG_UINT16(0x00c2),           // pushi 00c2
449 	0x7a,                               // push2
450 	0x39, 0x0e,                         // pushi 0e
451 	0x8d, 0x0b,                         // lst temp[0b]
452 	0x54, SIG_UINT16(0x0008),           // self 08
453 	SIG_END
454 };
455 
456 static const uint16 sci2BrokenStrStripPatch[] = {
457 	PATCH_ADDTOOFFSET(+2),
458 	0x85, 0x06,                         // lat temp[6] (once more]
459 	PATCH_ADDTOOFFSET(+3),              // jump over pushi callKernel
460 	0x39, 0x04,                         // pushi 04
461 	0x39, 0x0e,                         // pushi 0e
462 	// Attention: data is 0x14 in PQ4 CD, in floppy it's 0x12
463 	0x67, 0x12,                         // pTos data (pass actual data)
464 	0x8d, 0x0b,                         // lst temp[0b]
465 	0x36,                               // push
466 	0x54, PATCH_UINT16(0x000c),         // self 0c
467 	0x48,                               // ret
468 	PATCH_END
469 };
470 
471 // Torin/LSL7-specific version of sci2NumSavesSignature1/2
472 // Applies to at least: English CD
473 static const uint16 torinLarry7NumSavesSignature[] = {
474 	SIG_MAGICDWORD,
475 	0x36,       // push
476 	0x35, 0x14, // ldi 20
477 	0x20,       // ge?
478 	SIG_END
479 };
480 
481 static const uint16 torinLarry7NumSavesPatch[] = {
482 	PATCH_ADDTOOFFSET(+1), // push
483 	0x35, 0x63,            // ldi 99
484 	PATCH_END
485 };
486 
487 #endif
488 
489 // ===========================================================================
490 // Conquests of Camelot
491 // At the bazaar in Jerusalem, it's possible to see a girl taking a shower.
492 //  If you get too close, you get warned by the father - if you don't get away,
493 //  he will kill you.
494 // Instead of walking there manually, it's also possible to enter "look window"
495 //  and ego will automatically walk to the window. It seems that this is something
496 //  that wasn't properly implemented, because instead of getting killed, you will
497 //  get an "Oops" message in Sierra SCI.
498 //
499 // This is caused by peepingTom in script 169 not getting properly initialized.
500 // peepingTom calls the object behind global[b9h]. This global variable is
501 //  properly initialized when walking there manually (method fawaz::doit).
502 // When you instead walk there automatically (method fawaz::handleEvent), that
503 //  global isn't initialized, which then results in the Oops-message in Sierra SCI
504 //  and an error message in ScummVM/SCI.
505 //
506 // We fix the script by patching in a jump to the proper code inside fawaz::doit.
507 // Responsible method: fawaz::handleEvent
508 // Fixes bug: #6402
509 static const uint16 camelotSignaturePeepingTom[] = {
510 	0x72, SIG_MAGICDWORD, SIG_UINT16(0x077e), // lofsa fawaz <-- start of proper initializion code
511 	0xa1, 0xb9,                      // sag global[b9h]
512 	SIG_ADDTOOFFSET(+571),           // ...
513 	0x39, 0x7a,                      // pushi 7a <-- initialization code when walking automatically
514 	0x78,                            // push1
515 	0x7a,                            // push2
516 	0x38, SIG_UINT16(0x00a9),        // pushi 00a9 - script 169
517 	0x78,                            // push1
518 	0x43, 0x02, 0x04,                // callk ScriptID
519 	0x36,                            // push
520 	0x81, 0x00,                      // lag global[0]
521 	0x4a, 0x06,                      // send 06
522 	0x32, SIG_UINT16(0x0520),        // jmp [end of fawaz::handleEvent]
523 	SIG_END
524 };
525 
526 static const uint16 camelotPatchPeepingTom[] = {
527 	PATCH_ADDTOOFFSET(+576),
528 	0x32, PATCH_UINT16(0xfdbd),      // jmp [to fawaz::doit] (properly init peepingTom code)
529 	PATCH_END
530 };
531 
532 // If the butcher's daughter in room 62 closes her window while Arthur interacts
533 //  with the relic merchant then the game locks up. The script daughterAppears
534 //  attempts to dispose the script peepingTom, but it does so by clearing ego's
535 //  script no matter what it is, which breaks the game if the script is buyRelic
536 //  or one of the other handsOff merchant scripts.
537 //
538 // We fix this by calling peepingTom:dispose instead of clearing ego's script.
539 //  As this is an earlier SCI game prior to Script:dispose clearing the client
540 //  property, we need to do that ourselves in peepingTom:doit when Arthur turns
541 //  away from the window, or else peepingTom:client will still point to ego
542 //  after disposal, and the subsequent peepingTom:dispose will end ego's script.
543 //
544 // Applies to: All versions
545 // Responsible methods: daughterAppears:changeState(6), peepingTom:doit
546 // Fixes bug: #11025
547 static const uint16 camelotSignatureRelicMerchantLockup1[] = {
548 	0x39, SIG_SELECTOR8(setScript),     // pushi setScript
549 	0x78,                               // push1
550 	0x76,                               // push0
551 	0x81, SIG_MAGICDWORD, 0x00,         // lag 00
552 	0x4a, 0x06,                         // send 06 [ ego setScript: 0 ]
553 	0x3a,                               // toss
554 	SIG_END
555 };
556 
557 static const uint16 camelotPatchRelicMerchantLockup1[] = {
558 	0x39, PATCH_SELECTOR8(dispose),     // pushi dispose
559 	0x76,                               // push0
560 	0x72, PATCH_UINT16(0x01f3),         // lofsa peepingTom
561 	0x4a, 0x04,                         // send 04 [ peepingTom dispose: ]
562 	PATCH_END
563 };
564 
565 static const uint16 camelotSignatureRelicMerchantLockup2[] = {
566 	0x39, SIG_SELECTOR8(setScript),     // pushi setScript
567 	0x78,                               // push1
568 	0x76,                               // push0
569 	0x81, SIG_MAGICDWORD, 0x00,         // lag 00
570 	0x4a, 0x06,                         // send 06 [ ego setScript: 0 ]
571 	0x48,                               // ret
572 	SIG_END
573 };
574 
575 static const uint16 camelotPatchRelicMerchantLockup2[] = {
576 	0x39, PATCH_SELECTOR8(dispose),     // pushi dispose
577 	0x76,                               // push0
578 	0x54, 0x04,                         // self 04 [ self dispose: ]
579 	0x76,                               // push0
580 	0x69, 0x08,                         // sTop client [ client = 0 ]
581 	PATCH_END
582 };
583 
584 // The hunter in room 11 doesn't award soul points if you buy his furs with the
585 //  "buy furs" command first and "pay" second. Two points are awarded if these
586 //  commands are entered in the opposite order.
587 //
588 // We fix this by adding the missing function call to award the soul points as
589 //  Sierra did in later versions. Fortunately, the GiveMoney script contains
590 //  redundant code that can be replaced as it is occurs later in the method.
591 //
592 // Applies to: PC only
593 // Responsible method: GiveMoney:changeState(3)
594 // Fixes bug: #11027
595 static const uint16 camelotSignatureHunterMissingPoints[] = {
596 	SIG_MAGICDWORD,
597 	0x30, SIG_UINT16(0x0020),           // bnt 0020 [ matches PC only ]
598 	0x35, 0x00,                         // ldi 00
599 	0xa3, 0x00,                         // sal 00 [ local0 = 0 ]
600 	// unnecessary code which is repeated later in the method
601 	0x89, 0xdd,                         // lsg dd
602 	0x81, 0x84,                         // lag 84
603 	0x02,                               // add
604 	0xa1, 0xdd,                         // sag dd [ global221 += global132 ]
605 	0x35, 0x00,                         // ldi 00
606 	0xa1, 0x84,                         // sag 84 [ global132 = 0 ]
607 	SIG_END
608 };
609 
610 static const uint16 camelotPatchHunterMissingPoints[] = {
611 	PATCH_ADDTOOFFSET(+7),
612 	0x38, PATCH_UINT16(0x0003),         // pushi 0003
613 	0x38, PATCH_UINT16(0x00f5),         // pushi 00f5 [ point flag 245 ]
614 	0x7a,                               // push2 [ soul points ]
615 	0x7a,                               // push2 [ +2 points ]
616 	0x45, 0x0a, 0x06,                   // callb proc0_10 06 [ +2 soul points ]
617 	PATCH_END
618 };
619 
620 // When giving away the mule in room 56, the merchant's first long message is
621 //  immediately replaced by the next. mo:handleEvent displays the first and
622 //  starts the script getMule, which proceeds to display its own messages
623 //  without waiting.
624 //
625 // We fix this by adding code to getMule to wait for the merchant's message to
626 //  complete if you gave away the mule and a message is on screen.
627 //
628 // Applies to: All versions
629 // Responsible method: getMule:changeState(4)
630 // Fixes bug: #11026
631 static const uint16 camelotSignatureGiveMuleMessage[] = {
632 	0x30, SIG_UINT16(0x0023),           // bnt 0023 [ state 4 ]
633 	SIG_ADDTOOFFSET(+0x20),
634 	SIG_MAGICDWORD,
635 	0x32, SIG_UINT16(0x0239),           // jmp 0239 [ end of method ]
636 	0x3c,                               // dup
637 	0x35, 0x04,                         // ldi 04
638 	0x1a,                               // eq?
639 	0x30, SIG_UINT16(0x0016),           // bnt 0016 [ state 5 ]
640 	0x83, 0x02,                         // lal 02   [ gave away mule? ]
641 	0x30, SIG_UINT16(0x000a),           // bnt 000a [ skip state 14 if mule was sold ]
642 	0x39, SIG_SELECTOR8(changeState),   // pushi changeState
643 	0x78,                               // push1
644 	0x39, 0x0e,                         // pushi 0e
645 	0x54, 0x06,                         // self 06 [ self changeState: 14 ]
646 	0x32, SIG_UINT16(0x0223),           // jmp 0223 [ end of method ]
647 	0x35, 0x01,                         // ldi 01
648 	0x65, 0x10,                         // aTop cycles [ cycles = 1 ]
649 	0x32, SIG_UINT16(0x021c),           // jmp 021c [ end of method ]
650 	SIG_END
651 };
652 
653 static const uint16 camelotPatchGiveMuleMessage[] = {
654 	0x30, PATCH_UINT16(0x0020),         // bnt 0020 [ state 4 ]
655 	PATCH_ADDTOOFFSET(+0x20),
656 	0x3c,                               // dup
657 	0x35, 0x04,                         // ldi 04
658 	0x1a,                               // eq?
659 	0x31, 0x1a,                         // bnt 1a [ state 5 ]
660 	0x83, 0x02,                         // lal 02 [ gave away mule? ]
661 	0x31, 0x13,                         // bnt 13 [ skip state 14 if mule was sold ]
662 	0x35, 0x0d,                         // ldi 0d
663 	0x65, 0x0a,                         // aTop state [ state = 13 ]
664 	0x38, PATCH_UINT16(0x0121),         // pushi talkCue [ same value in all versions ]
665 	0x78,                               // push1
666 	0x7c,                               // pushSelf
667 	0x38, PATCH_UINT16(0x0122),         // pushi tS1 [ same value in all versions ]
668 	0x76,                               // push0
669 	0x81, 0x6f,                         // lag 6f
670 	0x4a, 0x0a,                         // send 0a [ tObj talkCue: self tS1? ]
671 	0x2f, 0x03,                         // bt 03   [ don't set cycles if message on screen ]
672 	0x78,                               // push1
673 	0x69, 0x10,                         // sTop cycles [ cycles = 1 ]
674 	PATCH_END
675 };
676 
677 // In Fatima's house in room 64, "look room" and "look trap" respond with the
678 //  wrong messages due to testing the wrong flag. Flag 162 is set when falling
679 //  through the trap door and alters responses the next time in the room, but
680 //  the script tests flag 137 instead, which is set when entering the room.
681 //
682 // Sierra fixed the first flag test in Amiga and Atari ST but not the second, so
683 //  this patch is applied only once to those versions and twice to PC.
684 //
685 // Applies to: All versions
686 // Responsible method: Rm64:handleEvent
687 // Fixes bug: #11028
688 static const uint16 camelotSignatureFatimaRoomMessages[] = {
689 	0x78,                               // push1
690 	0x38, SIG_MAGICDWORD,               // pushi 0089 [ flag 137, always true ]
691 	      SIG_UINT16(0x0089),
692 	0x45, 0x09, 0x02,                   // callb proc0_9 02 [ is flag 137 set? ]
693 	SIG_END
694 };
695 
696 static const uint16 camelotPatchFatimaRoomMessages[] = {
697 	PATCH_ADDTOOFFSET(+1),
698 	0x38, PATCH_UINT16(0x00a2),         // pushi 00a2 [ flag 162, set by trap ]
699 	PATCH_END
700 };
701 
702 // Sheathing the sword by pressing F8 while entering or exiting a room breaks
703 //  the game by placing ego in an invalid state that allows walking through
704 //  obstacles and prevents room changes. This affects rooms that subclass eRoom
705 //  in areas that allow walking with sword drawn such as the monk's ruins and
706 //  the desert. This is most likely to occur while battling the monk.
707 //
708 // eRoom walks ego in and out of rooms in handsOff mode. It sets ego:illegalBits
709 //  to 0, enables ignoreActors, and sets a motion that cues when complete to
710 //  restore ego's properties. Sheathing the sword interrupts the motion, which
711 //  prevents eRoom:cue, and prematurely restores control to the user with ego in
712 //  the temporary state. There are almost no restrictions on sheathing because
713 //  it's used when input is disabled and during several handsOff scenes.
714 //
715 // We fix this by preventing sword scripts from starting when an eRoom is in the
716 //  middle of controlling ego. eRoom tracks this with the comingIn and goingOut
717 //  properties and we require that both be cleared to execute a sword command.
718 //
719 // Applies to: All versions
720 // Responsible method: ARTHUR:doit
721 // Fixes bug: #11042
722 static const uint16 camelotSignatureSwordSheathing[] = {
723 	SIG_MAGICDWORD,
724 	0x89, 0x7d,                         // lsg 7d [ sword-command ]
725 	0x3c,                               // dup
726 	0x35, 0x01,                         // ldi 01
727 	0x1a,                               // eq? [ sword-command == 1 (draw) ]
728 	0x30, SIG_UINT16(0x0013),           // bnt 0013
729 	0x39, SIG_SELECTOR8(setScript),     // pushi setScript
730 	0x78,                               // push1
731 	0x7a,                               // push2
732 	0x38, SIG_UINT16(0x038e),           // pushi 910d
733 	0x76,                               // push0
734 	0x43, 0x02, 0x04,                   // callk ScriptID 04 [ ScriptID 910 0 (DrawSword) ]
735 	0x36,                               // push
736 	0x81, 0x00,                         // lag 00
737 	0x4a, 0x06,                         // send 06 [ ego setScript: DrawSword ]
738 	0x32, SIG_UINT16(0x0085),           // jmp 0085 [ end of switch ]
739 	0x3c,                               // dup
740 	0x35, 0x02,                         // ldi 02
741 	0x1a,                               // eq? [ sword-command == 2 (sheathe) ]
742 	0x30, SIG_UINT16(0x0013),           // bnt 0013
743 	0x39, SIG_SELECTOR8(setScript),     // pushi setScript
744 	0x78,                               // push1
745 	0x7a,                               // push2
746 	0x38, SIG_UINT16(0x038e),           // pushi 910d
747 	0x78,                               // push1
748 	0x43, 0x02, 0x04,                   // callk ScriptID 04 [ ScriptID 910 1 (SheatheSword) ]
749 	0x36,                               // push
750 	0x81, 0x00,                         // lag 00
751 	0x4a, 0x06,                         // send 06 [ ego setScript: SheatheSword ]
752 	0x32, SIG_UINT16(0x006b),           // jmp 006b [ end of switch ]
753 	0x3c,                               // dup
754 	0x35, 0x03,                         // ldi 03
755 	0x1a,                               // eq? [ sword-command == 3 (parry) ]
756 	0x30, SIG_UINT16(0x0013),           // bnt 0013
757 	0x39, SIG_SELECTOR8(setScript),     // pushi setScript
758 	0x78,                               // push1
759 	0x7a,                               // push2
760 	0x38, SIG_UINT16(0x0390),           // pushi 912d
761 	0x78,                               // push1
762 	0x43, 0x02, 0x04,                   // callk ScriptID 04 [ ScriptID 912 1 (DoParry) ]
763 	SIG_ADDTOOFFSET(+15),
764 	0x81, 0x7c,                         // lag 7c [ start of sword-command == 0 handler ]
765 	SIG_ADDTOOFFSET(+72),
766 	0x3a,                               // toss [ end of switch ]
767 	SIG_END
768 };
769 
770 static const uint16 camelotPatchSwordSheathing[] = {
771 	0x81, 0x7d,                         // lag 7d [ sword-command ]
772 	0x31, 0x53,                         // bnt 53 [ sword-command == 0 handler ]
773 	0x39, 0x03,                         // pushi 03
774 	0x20,                               // ge? [ 3 >= sword-command ]
775 	0x31, 0x3e,                         // bnt 3e [ exit if sword-command > 3 ]
776 	0x7a,                               // push2
777 	0x89, 0x02,                         // lsg 02
778 	0x38, PATCH_UINT16(0x014b),         // pushi comingIn [ same value in all versions ]
779 	0x43, 0x07, 0x04,                   // callk RespondsTo 04 [ RespondsTo currentRoom comingIn ]
780 	0x31, 0x14,                         // bnt 14 [ skip eRoom checks if room isn't an eRoom ]
781 	0x38, PATCH_UINT16(0x014b),         // pushi comingIn
782 	0x76,                               // push0
783 	0x81, 0x02,                         // lag 02
784 	0x4a, 0x04,                         // send 04 [ currentRoom comingIn? ]
785 	0x2f, 0x29,                         // bt 29   [ skip sword scripts if entering room ]
786 	0x38, PATCH_UINT16(0x014c),         // pushi goingOut [ same value in all versions ]
787 	0x76,                               // push0
788 	0x81, 0x02,                         // lag 02
789 	0x4a, 0x04,                         // send 04 [ currentRoom goingOut? ]
790 	0x2f, 0x1f,                         // bt 1f   [ skip sword scripts if exiting room ]
791 	0x39, PATCH_SELECTOR8(setScript),   // pushi setScript
792 	0x78,                               // push1
793 	0x7a,                               // push2
794 	0x81, 0x7d,                         // lag 7d
795 	0x7a,                               // push2
796 	0x20,                               // ge? [ 2 >= sword-command ]
797 	0x31, 0x05,                         // bnt 05
798 	0x38, PATCH_UINT16(0x038e),         // pushi 910d
799 	0x33, 0x03,                         // jmp 03
800 	0x38, PATCH_UINT16(0x0390),         // pushi 912d
801 	0x81, 0x7d,                         // lag 7d
802 	0x78,                               // push1
803 	0x1c,                               // ne? [ sword-command != 1 ]
804 	0x36,                               // push
805 	0x43, 0x02, 0x04,                   // callk ScriptID 04 [ ScriptID (sword-command <= 2) ? 910 : 912, (sword-command != 1) ]
806 	0x36,                               // push
807 	0x81, 0x00,                         // lag 00
808 	0x4a, 0x06,                         // send 06 [ ego setScript: sword-script ]
809 	0x48,                               // ret
810 	PATCH_ADDTOOFFSET(+89),
811 	0x48,                               // ret [ remove toss since dup instructions were removed ]
812 	PATCH_END
813 };
814 
815 // When Arthur's sword is drawn, ARTHUR:doit calls kGetEvent a second time on
816 //  each cycle to test if a shift key is pressed, causing input events to be
817 //  frequently dropped. This is similar to Freddy Pharkas and QFG1VGA where this
818 //  technique just happened to usually work in Sierra's interpreter. We fix this
819 //  in the same way by using the current event instead of consuming a new one.
820 //
821 // Applies to: All versions
822 // Responsible method: ARTHUR:doit
823 // Fixes bug: #11269
824 static const uint16 camelotSignatureSwordEvents[] = {
825 	0x30, SIG_MAGICDWORD,               // bnt 0045
826 	      SIG_UINT16(0x0045),
827 	0x39, SIG_SELECTOR8(new),           // pushi new
828 	0x76,                               // push0
829 	0x51, 0x07,                         // class Event
830 	0x4a, 0x04,                         // send 04 [ Event new: ]
831 	0xa5, 0x01,                         // sat 01 [ temp1 = Event new: ]
832 	SIG_ADDTOOFFSET(+53),
833 	0x39, SIG_SELECTOR8(dispose),       // pushi dispose
834 	0x76,                               // push0
835 	0x85, 0x01,                         // lat 01
836 	0x4a, 0x04,                         // send 04 [ temp1 dispose: ]
837 	SIG_END
838 };
839 
840 static const uint16 camelotPatchSwordEvents[] = {
841 	0x31, 0x46,                         // bnt 46
842 	0x38, PATCH_SELECTOR16(curEvent),   // pushi curEvent
843 	0x76,                               // push0
844 	0x51, 0x30,                         // class User [ User: curEvent ]
845 	PATCH_ADDTOOFFSET(+57),
846 	0x33, 0x05,                         // jmp 05 [ don't dispose event ]
847 	PATCH_END
848 };
849 
850 //         script, description,                                       signature                             patch
851 static const SciScriptPatcherEntry camelotSignatures[] = {
852 	{ true,     0, "fix sword sheathing",                          1, camelotSignatureSwordSheathing,       camelotPatchSwordSheathing },
853 	{ true,     0, "fix sword events",                             1, camelotSignatureSwordEvents,          camelotPatchSwordEvents },
854 	{ true,    11, "fix hunter missing points",                    1, camelotSignatureHunterMissingPoints,  camelotPatchHunterMissingPoints },
855 	{ true,    62, "fix peepingTom Sierra bug",                    1, camelotSignaturePeepingTom,           camelotPatchPeepingTom },
856 	{ true,    64, "fix Fatima room messages",                     2, camelotSignatureFatimaRoomMessages,   camelotPatchFatimaRoomMessages },
857 	{ true,   158, "fix give mule message",                        1, camelotSignatureGiveMuleMessage,      camelotPatchGiveMuleMessage },
858 	{ true,   169, "fix relic merchant lockup (1/2)",              1, camelotSignatureRelicMerchantLockup1, camelotPatchRelicMerchantLockup1 },
859 	{ true,   169, "fix relic merchant lockup (2/2)",              1, camelotSignatureRelicMerchantLockup2, camelotPatchRelicMerchantLockup2 },
860 	SCI_SIGNATUREENTRY_TERMINATOR
861 };
862 
863 // ===========================================================================
864 // stayAndHelp::changeState (0) is called when ego swims to the left or right
865 //  boundaries of room 660. Normally a textbox is supposed to get on screen
866 //  but the call is wrong, so not only do we get an error message the script
867 //  is also hanging because the cue won't get sent out
868 //  This also happens in sierra sci
869 // Applies to at least: PC-CD
870 // Responsible method: stayAndHelp::changeState
871 // Fixes bug: #5107
872 static const uint16 ecoquest1SignatureStayAndHelp[] = {
873 	0x3f, 0x01,                      // link 01
874 	0x87, 0x01,                      // lap param[1]
875 	0x65, 0x14,                      // aTop state
876 	0x36,                            // push
877 	0x3c,                            // dup
878 	0x35, 0x00,                      // ldi 00
879 	0x1a,                            // eq?
880 	0x31, 0x1c,                      // bnt [next state]
881 	0x76,                            // push0
882 	0x45, 0x01, 0x00,                // callb [export 1 of script 0], 00 (switching control off)
883 	SIG_MAGICDWORD,
884 	0x38, SIG_UINT16(0x0122),        // pushi 0122
885 	0x78,                            // push1
886 	0x76,                            // push0
887 	0x81, 0x00,                      // lag global[0]
888 	0x4a, 0x06,                      // send 06 - call ego::setMotion(0)
889 	0x39, SIG_SELECTOR8(init),       // pushi init
890 	0x39, 0x04,                      // pushi 04
891 	0x76,                            // push0
892 	0x76,                            // push0
893 	0x39, 0x17,                      // pushi 17
894 	0x7c,                            // pushSelf
895 	0x51, 0x82,                      // class EcoNarrator
896 	0x4a, 0x0c,                      // send 0c - call EcoNarrator::init(0, 0, 23, self) (BADLY BROKEN!)
897 	0x33,                            // jmp [end]
898 	SIG_END
899 };
900 
901 static const uint16 ecoquest1PatchStayAndHelp[] = {
902 	0x87, 0x01,                      // lap param[1]
903 	0x65, 0x14,                      // aTop state
904 	0x36,                            // push
905 	0x2f, 0x22,                      // bt [next state] (this optimization saves 6 bytes)
906 	0x39, 0x00,                      // pushi 0 (wasting 1 byte here)
907 	0x45, 0x01, 0x00,                // callb [export 1 of script 0], 00 (switching control off)
908 	0x38, PATCH_UINT16(0x0122),      // pushi 0122
909 	0x78,                            // push1
910 	0x76,                            // push0
911 	0x81, 0x00,                      // lag global[0]
912 	0x4a, 0x06,                      // send 06 - call ego::setMotion(0)
913 	0x39, PATCH_SELECTOR8(init),     // pushi init
914 	0x39, 0x06,                      // pushi 06
915 	0x39, 0x02,                      // pushi 02 (additional 2 bytes)
916 	0x76,                            // push0
917 	0x76,                            // push0
918 	0x39, 0x17,                      // pushi 17
919 	0x7c,                            // pushSelf
920 	0x38, PATCH_UINT16(0x0280),      // pushi 280 (additional 3 bytes)
921 	0x51, 0x82,                      // class EcoNarrator
922 	0x4a, 0x10,                      // send 10 - call EcoNarrator::init(2, 0, 0, 23, self, 640)
923 	PATCH_END
924 };
925 
926 // Giving the oily shell to Superfluous when he's out of the mask runs the
927 //  wrong animation and skips messages in the CD version. Sierra modified
928 //  getInOilyShell for the CD version by adding a new state to the beginning
929 //  but forgot to increment the state numbers passed to self:changeState to
930 //  their new values, causing the script to change to the wrong states.
931 //
932 // We fix this by incrementing the state numbers passed to self:changeState.
933 //
934 // Applies to: PC CD
935 // Responsible method: getInOilyShell:changeState
936 // Fixes bug #10881
937 static const uint16 ecoquest1SignatureGiveOilyShell[] = {
938 	0x30, SIG_UINT16(0x000a),       // bnt 000a
939 	0x38, SIG_UINT16(0x0090),       // pushi changeState [ hard coded for CD ]
940 	0x78,                           // push1
941 	0x7a,                           // push2 [ state 2 ]
942 	0x54, SIG_MAGICDWORD, 0x06,     // self 06
943 	0x32, SIG_UINT16(0x0195),       // jmp 0195
944 	SIG_ADDTOOFFSET(+209),
945 	0x39, 0x08,                     // pushi 08 [ state 8 ]
946 	SIG_ADDTOOFFSET(+16),
947 	0x39, 0x0b,                     // pushi 0b [ state 11 ]
948 	SIG_END
949 };
950 
951 static const uint16 ecoquest1PatchGiveOilyShell[] = {
952 	0x31, 0x0b,                     // bnt 0b
953 	0x38, PATCH_UINT16(0x0090),     // pushi changeState [ hard coded for CD ]
954 	0x78,                           // push1
955 	0x39, 0x03,                     // pushi 03 [ state 3 ]
956 	PATCH_ADDTOOFFSET(+214),
957 	0x39, 0x09,                     // pushi 09 [ state 9 ]
958 	PATCH_ADDTOOFFSET(+16),
959 	0x39, 0x0c,                     // pushi 0c [ state 12 ]
960 	PATCH_END
961 };
962 
963 // Reading the prophecy scroll in the CD version breaks messages in at least
964 //  rooms 100 and 120. scrollScript:init overwrites the global that holds the
965 //  noun for the room's message tuples. This global was added in the CD version
966 //  and is set by most rooms during initialization. This pattern was mistakenly
967 //  applied to scrollScript which isn't a room and doesn't depend on the global.
968 //
969 // We fix this by skipping the problematic code which overwrites the global.
970 //
971 // Applies to: PC CD
972 // Responsible method: scrollScript:init
973 // Fixes bug #10883
974 static const uint16 ecoquest1SignatureProphecyScroll[] = {
975 	SIG_MAGICDWORD,
976 	0x35, 0x01,                     // ldi 01
977 	0xa1, 0xfa,                     // sag fa [ global250 = 1 ]
978 	SIG_END
979 };
980 
981 static const uint16 ecoquest1PatchProphecyScroll[] = {
982 	0x33, 0x02,                     // jmp 02 [ don't set global250 ]
983 	PATCH_END
984 };
985 
986 // The empty apartments have several broken messages in the CD version due to
987 //  not setting the global that holds the current room's noun, so we set it.
988 //
989 // Applies to: PC CD
990 // Responsible method: rm220:init
991 // Fixes bug #10903
992 static const uint16 ecoquest1SignatureEmptyApartmentMessages[] = {
993     SIG_MAGICDWORD,
994     0x54, 0x0c,                     // self 0c [ self setRegions: 51, addObstacle: ... ]
995     0x39, SIG_SELECTOR8(init),      // pushi init
996     0x76,                           // push0
997     0x59, 0x01,                     // &rest 01 [ unused by ApartmentRoom:init ]
998     0x57, 0x96, 0x04,               // super ApartmentRoom 04 [ super init: &rest ]
999     SIG_END
1000 };
1001 
1002 static const uint16 ecoquest1PatchEmptyApartmentMessages[] = {
1003     0x35, 0x01,                     // ldi 01 [ the room's noun ]
1004     PATCH_ADDTOOFFSET(+3),
1005     0xa1, 0xfa,                     // sag fa [ global250 = 1 ]
1006     0x57, 0x96, 0x10,               // super ApartmentRoom 10 [ combine self and super ]
1007     PATCH_END
1008 };
1009 
1010 // The temple has a complex script bug in the CD version which can crash the
1011 //  interpreter when solving the mosaic puzzle after loading a game that was
1012 //  saved during the puzzle. The bug causes invalid memory access which locks up
1013 //  Sierra's interpreter and can cause ours to fail an assertion.
1014 //
1015 // Room 140 has three insets and a conch shell in the middle. This room's script
1016 //  was significantly changed in the CD version and transition animations were
1017 //  added. Due to these changes the shell no longer renders beneath the insets
1018 //  and so Sierra added code to hide the shell while they're displayed.
1019 //  Unfortunately this code is incorrect and leaves the game in a state that's
1020 //  unsafe to save. The shell is removed from the cast when showing an inset and
1021 //  then shell:init is called when hiding. This leaves shell:underBits pointing
1022 //  to hunk memory while temporarily not a member of the cast. Hunk memory isn't
1023 //  persisted in saved games but underBits' values are. SCI games handle this in
1024 //  Game:replay by clearing the underBits of cast members when restoring. Saving
1025 //  while the puzzle is displayed causes shell:underBits' stale hunk value to
1026 //  survive restoring. Solving the puzzle adds the shell back to the cast via
1027 //  init followed by a call to kAnimate that accesses the potentially stale
1028 //  shell:underBits. If the hunk segment id upon restoring in ScummVM is the
1029 //  same as when saved then this out of bounds access will fail an assertion.
1030 //
1031 // We fix this by fully disposing the shell when showing an inset so that its
1032 //  resources are cleaned up and it's safe to save the game. In order to do this
1033 //  without changing the animation effect we set shell's disposal flag and then
1034 //  immediately call shell:delete. This is equivalent to shell:dispose but
1035 //  prevents hiding the shell before the transition animation takes place.
1036 //
1037 // Applies to: PC CD
1038 // Responsible methods: MosaicWall:doVerb, localproc_2ab6 in script 140
1039 // Fixes bug #10884
1040 static const uint16 ecoquest1SignatureMosaicPuzzleFix[] = {
1041 	0x36,                           // push     [ conchShell:owner ]
1042 	0x34, SIG_UINT16(0x008c),       // ldi 008c [ room number ]
1043 	0x1a,                           // eq?      [ is conchShell owned by room 140? ]
1044 	0x30, SIG_UINT16(0x000b),       // bnt 000b [ no shell to hide ]
1045 	SIG_MAGICDWORD,
1046 	0x39, SIG_SELECTOR8(delete),    // pushi delete
1047 	0x78,                           // push1
1048 	0x72, SIG_UINT16(0x056a),       // lofsa shell
1049 	0x36,                           // push
1050 	0x81, 0x05,                     // lag 05
1051 	0x4a, 0x06,                     // send 06 [ cast delete: shell ]
1052 	SIG_END
1053 };
1054 
1055 static const uint16 ecoquest1PatchMosaicPuzzleFix[] = {
1056 	0x89, 0x0b,                     // lsg 0b [ current room number, saves 2 bytes ]
1057 	0x1a,                           // eq?    [ is conchShell owned by room 140? ]
1058 	0x31, 0x0e,                     // bnt 0e [ no shell to hide, save a byte ]
1059 	0x39, PATCH_SELECTOR8(signal),  // pushi signal
1060 	0x78,                           // push1
1061 	0x38, PATCH_UINT16(0xc014),     // pushi c014 [ kSignalDisposeMe | shell:signal ]
1062 	0x39, PATCH_SELECTOR8(delete),  // pushi delete
1063 	0x76,                           // push0
1064 	0x72, PATCH_UINT16(0x056a),     // lofsa shell
1065 	0x4a, 0x0a,                     // send 0a [ shell signal: c014, delete ]
1066 	PATCH_END
1067 };
1068 
1069 // The column puzzle in room 160 can be put in a state that can't be completed.
1070 //  This is a bug in the original that affects all versions.
1071 //
1072 // The puzzle consists of nine columns that must be rotated to their correct
1073 //  positions in the correct order. As each column is solved it is locked. When
1074 //  leaving the room the puzzle state is saved to globals but this state is
1075 //  insufficient to recreate the puzzle. The game saves column positions but not
1076 //  lock states. Instead it infers lock states from positions when restoring but
1077 //  this is inaccurate because columns can be put in their correct positions out
1078 //  of order. If the player leaves the room while all columns are in their
1079 //  correct positions but before the puzzle is solved then all columns will be
1080 //  locked when returning and the game can't be completed.
1081 //
1082 // The proper solution would be to save and restore lock states but it would be
1083 //  impractical to patch in that functionality while retaining save game
1084 //  compatibility. Instead we patch the loop that reinitializes lock states to
1085 //  skip the last column so that it's always unlocked and the player can't get
1086 //  stuck. This code only runs when the puzzle isn't solved and should never
1087 //  have been able to lock the last column.
1088 //
1089 // Applies to: All Floppy and CD versions
1090 // Responsible method: Local procedure 5 in script 160
1091 // Fixes bug #10885
1092 static const uint16 ecoquest1SignatureColumnPuzzleFix[] = {
1093 	0x39, SIG_SELECTOR8(size),      // pushi size
1094 	0x76,                           // push0
1095 	0x72, SIG_ADDTOOFFSET(+2),      // lofsa columnList [ columns in solution order ]
1096 	0x4a, 0x04,                     // send 04 [ columnList:size ]
1097 	0x22,                           // lt? [ temp0 < columnList:size (9) ]
1098 	0x30, SIG_ADDTOOFFSET(+2),      // bnt [ end of method ]
1099 	0x39, SIG_MAGICDWORD,           // pushi cel
1100 	      SIG_SELECTOR8(cel),
1101 	0x76,                           // push0
1102 	0x39, SIG_SELECTOR8(at),        // pushi at
1103 	SIG_END
1104 };
1105 
1106 static const uint16 ecoquest1PatchColumnPuzzleFix[] = {
1107 	0x34, PATCH_UINT16(0x0008),     // ldi 0008 [ only initialize 8 of 9 columns ]
1108 	0x32, PATCH_UINT16(0x0002),     // jmp 0002
1109 	PATCH_END
1110 };
1111 
1112 // The ocean cliffs that border rooms 320 and 321 aren't displayed in the CD
1113 //  version. Instead they are drawn above the visible area and on more screens
1114 //  than they should. This also occurs in the original.
1115 //
1116 // Cliff views 325 and 326 have y displacements greater than 127 in the floppy
1117 //  versions. In the CD version these offsets were changed to zero. Sierra
1118 //  attempted to compensate for this by adding rows of empty pixels to the views
1119 //  but it appears that someone mistook the unsigned offsets for negative values
1120 //  and added the wrong number of rows to the wrong side of the views, causing
1121 //  the cliffs to be drawn 256 pixels higher than normal.
1122 //
1123 // The ocean scripts were changed to use different techniques for adding and
1124 //  removing the cliffs but this introduced more errors. Room 321 reinitializes
1125 //  the cliffs instead of disposing them, causing them to be redrawn on the
1126 //  wrong screens, and room 320 disposes the eastern cliffs instead of western.
1127 //
1128 // We fix the cliffs by adjusting their positions by 256 and disposing of them
1129 //  in room 321. We leave room 320's incorrect cliff disposal in place since
1130 //  both are automatically disposed of when that room's pic changes.
1131 //
1132 // Applies to: PC CD
1133 // Responsible methods: Heap in scripts 320 and 321, toEast:changeState, toWest:changeState
1134 // Fixes bug #10893
1135 static const uint16 ecoquest1SignatureSouthCliffsPosition[] = {
1136 	SIG_MAGICDWORD,
1137 	SIG_UINT16(0x0095),             // easternCliffs:x = 149
1138 	SIG_UINT16(0x0033),             // easternCliffs:y = 51
1139 	SIG_ADDTOOFFSET(+88),
1140 	SIG_UINT16(0x0004),             // westernCliffs:x = 4
1141 	SIG_UINT16(0x0014),             // westernCliffs:y = 20
1142 	SIG_END
1143 };
1144 
1145 static const uint16 ecoquest1PatchSouthCliffsPosition[] = {
1146 	PATCH_ADDTOOFFSET(+2),
1147 	PATCH_UINT16(0x0133),           // easternCliffs:y = 307
1148 	PATCH_ADDTOOFFSET(+90),
1149 	PATCH_UINT16(0x0114),           // westernCliffs:y = 276
1150 	PATCH_END
1151 };
1152 
1153 static const uint16 ecoquest1SignatureNorthCliffsPosition[] = {
1154 	SIG_MAGICDWORD,
1155 	SIG_UINT16(0x00eb),             // easternCliffs:x = 236
1156 	SIG_UINT16(0x0038),             // easternCliffs:y = 56
1157 	SIG_ADDTOOFFSET(+88),
1158 	SIG_UINT16(0x0000),             // westernCliffs:x = 0
1159 	SIG_UINT16(0x0032),             // westernCliffs:y = 50
1160 	SIG_END
1161 };
1162 
1163 static const uint16 ecoquest1PatchNorthCliffsPosition[] = {
1164 	PATCH_ADDTOOFFSET(+2),
1165 	PATCH_UINT16(0x0138),           // easternCliffs:y = 312
1166 	PATCH_ADDTOOFFSET(+90),
1167 	PATCH_UINT16(0x0132),           // westernCliffs:y = 306
1168 	PATCH_END
1169 };
1170 
1171 static const uint16 ecoquest1SignatureNorthCliffsDisposal[] = {
1172 	0x39, SIG_SELECTOR8(init),          // pushi init
1173 	0x76,                               // push0
1174 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa easternCliffs or westernCliffs
1175 	0x4a, SIG_MAGICDWORD, 0x04,         // send 04 [ cliffs init: ]
1176 	0x38, SIG_SELECTOR16(obstacles),    // pushi obstacles
1177 	SIG_END
1178 };
1179 
1180 static const uint16 ecoquest1PatchNorthCliffsDisposal[] = {
1181 	0x39, PATCH_SELECTOR8(dispose),     // pushi dispose
1182 	PATCH_END
1183 };
1184 
1185 // The Spanish version of EcoQuest accidentally shipped with temporary test code
1186 //  that breaks the game when entering Olympia's apartment. (room 226)
1187 //
1188 // A message box's position was localized in the Spanish version. This message
1189 //  occurs after saving Olympia by pumping bleach out of the window. To test
1190 //  this change, a developer added code to forcibly run the usePump script upon
1191 //  entering the room, but then forgot to remove it. This breaks the puzzle and
1192 //  locks up the game upon re-entering the room.
1193 //
1194 // We fix this by disabling the test code that should not have been shipped.
1195 //
1196 // Applies to: Spanish PC Floppy
1197 // Responsible method: rm226:init
1198 // Fixes bug #10900
1199 static const uint16 ecoquest1SignatureBleachPumpTest[] = {
1200 	SIG_MAGICDWORD,
1201 	0x78,                               // push1
1202 	0x39, 0x35,                         // pushi 35
1203 	0x46, SIG_UINT16(0x0333),           // calle proc819_3 [ set recycled-bleach flag ]
1204 	      SIG_UINT16(0x0003), 0x02,
1205 	0x38, SIG_SELECTOR16(setScript),    // pushi setScript
1206 	0x78,                               // push1
1207 	0x72, SIG_UINT16(0x0a44),           // lofsa usePump
1208 	0x36,                               // push
1209 	0x54, 0x06,                         // self 06 [ self setScript: usePump ]
1210 	SIG_END
1211 };
1212 
1213 static const uint16 ecoquest1PatchBleachPumpTest[] = {
1214 	0x32, PATCH_UINT16(0x0010),         // jmp 0010 [ skip test code ]
1215 	PATCH_END
1216 };
1217 
1218 //          script, description,                                      signature                                 patch
1219 static const SciScriptPatcherEntry ecoquest1Signatures[] = {
1220 	{  true,   140, "CD: mosaic puzzle fix",                       2, ecoquest1SignatureMosaicPuzzleFix,        ecoquest1PatchMosaicPuzzleFix },
1221 	{  true,   160, "CD: give superfluous oily shell",             1, ecoquest1SignatureGiveOilyShell,          ecoquest1PatchGiveOilyShell },
1222 	{  true,   160, "CD/Floppy: column puzzle fix",                1, ecoquest1SignatureColumnPuzzleFix,        ecoquest1PatchColumnPuzzleFix },
1223 	{  true,   220, "CD: empty apartment messages",                1, ecoquest1SignatureEmptyApartmentMessages, ecoquest1PatchEmptyApartmentMessages },
1224 	{  true,   226, "Spanish: disable bleach pump test",           1, ecoquest1SignatureBleachPumpTest,         ecoquest1PatchBleachPumpTest },
1225 	{  true,   320, "CD: south cliffs position",                   1, ecoquest1SignatureSouthCliffsPosition,    ecoquest1PatchSouthCliffsPosition },
1226 	{  true,   321, "CD: north cliffs position",                   1, ecoquest1SignatureNorthCliffsPosition,    ecoquest1PatchNorthCliffsPosition },
1227 	{  true,   321, "CD: north cliffs disposal",                   2, ecoquest1SignatureNorthCliffsDisposal,    ecoquest1PatchNorthCliffsDisposal },
1228 	{  true,   660, "CD: bad messagebox and freeze",               1, ecoquest1SignatureStayAndHelp,            ecoquest1PatchStayAndHelp },
1229 	{  true,   816, "CD: prophecy scroll",                         1, ecoquest1SignatureProphecyScroll,         ecoquest1PatchProphecyScroll },
1230 	SCI_SIGNATUREENTRY_TERMINATOR
1231 };
1232 
1233 // ===========================================================================
1234 // doMyThing::changeState (2) is supposed to remove the initial text on the
1235 //  ecorder. This is done by reusing temp-space, that was filled on state 1.
1236 //  this worked in sierra sci just by accident. In our sci, the temp space
1237 //  is resetted every time, which means the previous text isn't available
1238 //  anymore. We have to patch the code because of that.
1239 // Fixes bug: #4993
1240 static const uint16 ecoquest2SignatureEcorder[] = {
1241 	0x31, 0x22,                      // bnt [next state]
1242 	0x39, 0x0a,                      // pushi 0a
1243 	0x5b, 0x04, 0x1e,                // lea temp[1e]
1244 	0x36,                            // push
1245 	SIG_MAGICDWORD,
1246 	0x39, 0x64,                      // pushi 64
1247 	0x39, 0x7d,                      // pushi 7d
1248 	0x39, 0x32,                      // pushi 32
1249 	0x39, 0x66,                      // pushi 66
1250 	0x39, 0x17,                      // pushi 17
1251 	0x39, 0x69,                      // pushi 69
1252 	0x38, SIG_UINT16(0x2631),        // pushi 2631
1253 	0x39, 0x6a,                      // pushi 6a
1254 	0x39, 0x64,                      // pushi 64
1255 	0x43, 0x1b, 0x14,                // callk Display
1256 	0x35, 0x0a,                      // ldi 0a
1257 	0x65, 0x20,                      // aTop ticks
1258 	0x33,                            // jmp [end]
1259 	SIG_ADDTOOFFSET(+1),             // [skip 1 byte]
1260 	0x3c,                            // dup
1261 	0x35, 0x03,                      // ldi 03
1262 	0x1a,                            // eq?
1263 	0x31,                            // bnt [end]
1264 	SIG_END
1265 };
1266 
1267 static const uint16 ecoquest2PatchEcorder[] = {
1268 	0x2f, 0x02,                      // bt [to pushi 7]
1269 	0x3a,                            // toss
1270 	0x48,                            // ret
1271 	0x38, PATCH_UINT16(0x0007),      // pushi 7d (parameter count) (waste 1 byte)
1272 	0x39, 0x0b,                      // pushi 11d (FillBoxAny)
1273 	0x39, 0x1d,                      // pushi 29d
1274 	0x39, 0x73,                      // pushi 115d
1275 	0x39, 0x5e,                      // pushi 94d
1276 	0x38, PATCH_UINT16(0x00d7),      // pushi 215d
1277 	0x78,                            // push1 (visual screen)
1278 	0x38, PATCH_UINT16(0x0017),      // pushi 23d (color) (waste 1 byte)
1279 	0x43, 0x6c, 0x0e,                // callk Graph
1280 	0x38, PATCH_UINT16(0x0005),      // pushi 5d (parameter count) (waste 1 byte)
1281 	0x39, 0x0c,                      // pushi 12d (UpdateBox)
1282 	0x39, 0x1d,                      // pushi 29d
1283 	0x39, 0x73,                      // pushi 115d
1284 	0x39, 0x5e,                      // pushi 94d
1285 	0x38, PATCH_UINT16(0x00d7),      // pushi 215d
1286 	0x43, 0x6c, 0x0a,                // callk Graph
1287 	PATCH_END
1288 };
1289 
1290 // Same patch as above for the ecorder introduction.
1291 // Two workarounds are needed for this patch in workarounds.cpp (when calling
1292 // kGraphFillBoxAny and kGraphUpdateBox), as there isn't enough space to patch
1293 // the function otherwise.
1294 // Fixes bug: #6467
1295 static const uint16 ecoquest2SignatureEcorderTutorial[] = {
1296 	0x30, SIG_UINT16(0x0023),        // bnt [next state]
1297 	0x39, 0x0a,                      // pushi 0a
1298 	0x5b, 0x04, 0x1f,                // lea temp[1f]
1299 	0x36,                            // push
1300 	SIG_MAGICDWORD,
1301 	0x39, 0x64,                      // pushi 64
1302 	0x39, 0x7d,                      // pushi 7d
1303 	0x39, 0x32,                      // pushi 32
1304 	0x39, 0x66,                      // pushi 66
1305 	0x39, 0x17,                      // pushi 17
1306 	0x39, 0x69,                      // pushi 69
1307 	0x38, SIG_UINT16(0x2631),        // pushi 2631
1308 	0x39, 0x6a,                      // pushi 6a
1309 	0x39, 0x64,                      // pushi 64
1310 	0x43, 0x1b, 0x14,                // callk Display
1311 	0x35, 0x1e,                      // ldi 1e
1312 	0x65, 0x20,                      // aTop ticks
1313 	0x32,                            // jmp [end]
1314 	// 2 extra bytes, jmp offset
1315 	SIG_END
1316 };
1317 
1318 static const uint16 ecoquest2PatchEcorderTutorial[] = {
1319 	0x31, 0x23,                      // bnt [next state] (save 1 byte)
1320 	// The parameter count below should be 7, but we're out of bytes
1321 	// to patch! A workaround has been added because of this
1322 	0x78,                            // push1 (parameter count)
1323 	//0x39, 0x07,                    // pushi 7d (parameter count)
1324 	0x39, 0x0b,                      // pushi 11d (FillBoxAny)
1325 	0x39, 0x1d,                      // pushi 29d
1326 	0x39, 0x73,                      // pushi 115d
1327 	0x39, 0x5e,                      // pushi 94d
1328 	0x38, PATCH_UINT16(0x00d7),      // pushi 215d
1329 	0x78,                            // push1 (visual screen)
1330 	0x39, 0x17,                      // pushi 23d (color)
1331 	0x43, 0x6c, 0x0e,                // callk Graph
1332 	// The parameter count below should be 5, but we're out of bytes
1333 	// to patch! A workaround has been added because of this
1334 	0x78,                            // push1 (parameter count)
1335 	//0x39, 0x05,                    // pushi 5d (parameter count)
1336 	0x39, 0x0c,                      // pushi 12d (UpdateBox)
1337 	0x39, 0x1d,                      // pushi 29d
1338 	0x39, 0x73,                      // pushi 115d
1339 	0x39, 0x5e,                      // pushi 94d
1340 	0x38, PATCH_UINT16(0x00d7),      // pushi 215d
1341 	0x43, 0x6c, 0x0a,                // callk Graph
1342 	// We are out of bytes to patch at this point,
1343 	// so we skip 494 (0x1ee) bytes to reuse this code:
1344 	// ldi 1e
1345 	// aTop 20
1346 	// jmp 030e (jump to end)
1347 	0x32, PATCH_UINT16(0x01ee),      // jmp 494d
1348 	PATCH_END
1349 };
1350 
1351 // Clicking an icon during the icon bar tutorial in room 100 sends messages to
1352 //  an uninitialized temporary variable. This is supposed to be the dispatched
1353 //  Event object that's passed around earlier in the call stack. In Sierra's
1354 //  interpreter that's what happened to be at this location and it worked.
1355 //
1356 // We fix this by using the global variable that stores the current Event object
1357 //  instead of the uninitialized temp variable that accidentally points to it.
1358 //  User:handleEvent sets this global before dispatching an event.
1359 //
1360 // Applies to: All versions
1361 // Responsible methods: iconWalk:select, iconLook:select, ...
1362 // Fixes bug: #11081
1363 static const uint16 ecoquest2SignatureIconBarTutorial[] = {
1364 	0x7a,                               // push2
1365 	0x38, SIG_SELECTOR16(handleEvent),  // pushi handleEvent
1366 	SIG_MAGICDWORD,
1367 	0x8d, 0x01,                         // lst 01  [ uninitialized ]
1368 	0x4a, 0x08,                         // send 08 [ EventHandler firstTrue: handleEvent temp1 ]
1369 	SIG_END
1370 };
1371 
1372 static const uint16 ecoquest2PatchIconBarTutorial[] = {
1373 	PATCH_ADDTOOFFSET(+4),
1374 	0x89, 0x18,                         // lsg 18 [ current event ]
1375 	PATCH_END
1376 };
1377 
1378 // The electronic organizer and password paper reappear in room 500 after they
1379 //  fall into the water when entering the canoe. rm500:init only tests if these
1380 //  items are in inventory. It should have also tested the canoe flag like room
1381 //  530 does to prevent the vacuum from reappearing.
1382 //
1383 // We fix this by only adding an item to the room if its InvI:owner is zero.
1384 //  This is initially zero, then set to ego when getting an item, and finally
1385 //  set to negative one when the item is removed from inventory.
1386 //
1387 // Applies to: All versions
1388 // Responsible method: rm500:init
1389 // Fixes bug: #11135
1390 static const uint16 ecoquest2SignatureRoom500Items[] = {
1391 	0x38, SIG_ADDTOOFFSET(+2),          // pushi test
1392 	0x78,                               // push1
1393 	SIG_MAGICDWORD,
1394 	0x39, 0x0b,                         // pushi 0b
1395 	0x81, 0x96,                         // lag 96
1396 	0x4a, 0x06,                         // send 06 [ cibolaFlags test: 11 ]
1397 	0xa5, 0x00,                         // sat 00
1398 	0x38, SIG_ADDTOOFFSET(+2),          // pushi test
1399 	0x78,                               // push1
1400 	0x39, 0x04,                         // pushi 04
1401 	0x81, 0x96,                         // lag 96
1402 	0x4a, 0x06,                         // send 06 [ cibolaFlags test: 4 ]
1403 	0xa5, 0x01,                         // sat 01
1404 	0x38, SIG_ADDTOOFFSET(+2),          // pushi test
1405 	0x78,                               // push1
1406 	0x39, 0x17,                         // pushi 17
1407 	0x81, 0x96,                         // lag 96
1408 	0x4a, 0x06,                         // send 06 [ cibolaFlags test: 23 ]
1409 	0xa5, 0x02,                         // sat 02
1410 	0x38, SIG_ADDTOOFFSET(+2),          // pushi test
1411 	SIG_ADDTOOFFSET(+636),
1412 	0x38, SIG_SELECTOR16(has),          // pushi has
1413 	0x78,                               // push1
1414 	0x39, 0x15,                         // pushi 15
1415 	0x81, 0x00,                         // lag 00
1416 	0x4a, 0x06,                         // send 06 [ ego has: 21 ]
1417 	0x18,                               // not
1418 	0x31, 0x13,                         // bnt 13 [ don't initialize theOrganizer ]
1419 	SIG_ADDTOOFFSET(+236),
1420 	0x38, SIG_SELECTOR16(has),          // pushi has
1421 	0x78,                               // push1
1422 	0x39, 0x0b,                         // pushi 0b
1423 	0x81, 0x00,                         // lag 00
1424 	0x4a, 0x06,                         // send 06 [ ego has: 11 ]
1425 	0x18,                               // not
1426 	0x30, SIG_UINT16(0x0058),           // bnt 0058 [ don't initialize paper ]
1427 	SIG_END
1428 };
1429 
1430 static const uint16 ecoquest2PatchRoom500Items[] = {
1431 	0x39, PATCH_SELECTOR8(at),          // pushi at
1432 	0x3c,                               // dup [ push at, saves 1 byte ]
1433 	0x78,                               // push1
1434 	0x39, 0x15,                         // pushi 15
1435 	0x38, PATCH_GETORIGINALUINT16(+1),  // pushi test
1436 	0x3c,                               // dup [ push test, saves 2 bytes ]
1437 	0x3c,                               // dup [ push test, saves 2 bytes ]
1438 	0x3c,                               // dup [ push test, saves 2 bytes ]
1439 	0x78,                               // push1
1440 	0x39, 0x0b,                         // pushi 0b
1441 	0x81, 0x96,                         // lag 96
1442 	0x4a, 0x06,                         // send 06 [ cibolaFlags test: 11 ]
1443 	0xa5, 0x00,                         // sat 00
1444 	0x78,                               // push1
1445 	0x39, 0x04,                         // pushi 04
1446 	0x81, 0x96,                         // lag 96
1447 	0x4a, 0x06,                         // send 06 [ cibolaFlags test: 4 ]
1448 	0xa5, 0x01,                         // sat 01
1449 	0x78,                               // push1
1450 	0x39, 0x17,                         // pushi 17
1451 	0x81, 0x96,                         // lag 96
1452 	0x4a, 0x06,                         // send 06 [ cibolaFlags test: 23 ]
1453 	0xa5, 0x02,                         // sat 02
1454 	PATCH_ADDTOOFFSET(+636),
1455 	0x81, 0x09,                         // lag 09
1456 	0x4a, 0x06,                         // send 06 [ Inv at: 21 ]
1457 	0x38, PATCH_SELECTOR16(owner),      // pushi owner
1458 	0x76,                               // push0
1459 	0x4a, 0x04,                         // send 04 [ organizer owner? ]
1460 	0x78,                               // push1
1461 	0x2f, 0x13,                         // bt 13 [ don't initialize theOrganizer ]
1462 	PATCH_ADDTOOFFSET(+236),
1463 	0x39, 0x0b,                         // pushi 0b
1464 	0x81, 0x09,                         // lag 09
1465 	0x4a, 0x06,                         // send 06 [ Inv at: 11 ]
1466 	0x38, PATCH_SELECTOR16(owner),      // pushi owner
1467 	0x76,                               // push0
1468 	0x4a, 0x04,                         // send 04 [ password owner? ]
1469 	0x2f, 0x58,                         // bt 58 [ don't initialize paper ]
1470 	PATCH_END
1471 };
1472 
1473 // The Ecorder cursor only highlights over one of the four Victoria lilies, even
1474 //  though they all respond to Ecorder clicks. Each lily has a doit method that
1475 //  highlights the cursor but three of them never execute because they are
1476 //  added to the room pic. This removes them from the cast and prevents doit.
1477 //
1478 // We fix this by removing the addToPic calls so the lilies remain in the cast.
1479 //
1480 // Applies to: All versions
1481 // Responsible methods: lilly1:init, lilly2:init, lilly3:init
1482 // Fixes bug: #5552
1483 static const uint16 ecoquest2SignatureEcorderLily[] = {
1484 	0x38, SIG_MAGICDWORD,               // pushi addToPic
1485 	      SIG_SELECTOR16(addToPic),
1486 	0x76,                               // push0
1487 	0x54, 0x04,                         // self 04 [ self addToPic: ]
1488 	SIG_END
1489 };
1490 
1491 static const uint16 ecoquest2PatchEcorderLily[] = {
1492 	0x32, PATCH_UINT16(0x0003),         // jmp 0003
1493 	PATCH_END
1494 };
1495 
1496 //          script, description,                                        signature                          patch
1497 static const SciScriptPatcherEntry ecoquest2Signatures[] = {
1498 	{  true,     0, "icon bar tutorial",                            10, ecoquest2SignatureIconBarTutorial, ecoquest2PatchIconBarTutorial },
1499 	{  true,    50, "initial text not removed on ecorder",           1, ecoquest2SignatureEcorder,         ecoquest2PatchEcorder },
1500 	{  true,   333, "initial text not removed on ecorder tutorial",  1, ecoquest2SignatureEcorderTutorial, ecoquest2PatchEcorderTutorial },
1501 	{  true,   500, "room 500 items reappear",                       1, ecoquest2SignatureRoom500Items,    ecoquest2PatchRoom500Items },
1502 	{  true,   702, "ecorder not highlighting lilies",               3, ecoquest2SignatureEcorderLily,     ecoquest2PatchEcorderLily },
1503 	SCI_SIGNATUREENTRY_TERMINATOR
1504 };
1505 
1506 // ===========================================================================
1507 // Fan-made games
1508 // Attention: Try to make script patches as specific as possible
1509 
1510 // CascadeQuest::autosave in script 994 is called various times to auto-save.
1511 //  It uses a fixed slot (999) for this purpose. This doesn't work in ScummVM,
1512 //  because we do not let scripts save directly into specific slots, but
1513 //  instead use virtual slots / detect scripts wanting to create a new slot.
1514 // We patch the code to use slot 99 instead. kSaveGame also checks for Cascade
1515 //  Quest, and if slot 99 is asked for, it will then use the actual slot 0,
1516 //  which is the official ScummVM auto-save slot.
1517 //
1518 // Responsible method: CascadeQuest::autosave
1519 // Fixes bug: #7007
1520 static const uint16 fanmadeSignatureCascadeQuestFixAutoSaving[] = {
1521 	SIG_MAGICDWORD,
1522 	0x38, SIG_UINT16(0x03e7),        // pushi 3E7 (999d) -> save game slot 999
1523 	0x74, SIG_UINT16(0x06f8),        // lofss "AutoSave"
1524 	0x89, 0x1e,                      // lsg global[1e]
1525 	0x43, 0x2d, 0x08,                // callk SaveGame
1526 	SIG_END
1527 };
1528 
1529 static const uint16 fanmadePatchCascadeQuestFixAutoSaving[] = {
1530 	0x38, PATCH_UINT16((SAVEGAMEID_OFFICIALRANGE_START - 1)), // fix slot
1531 	PATCH_END
1532 };
1533 
1534 // EventHandler::handleEvent in Demo Quest has a bug, and it jumps to the
1535 // wrong address when an incorrect word is typed, therefore leading to an
1536 // infinite loop. This script bug was not apparent in SSCI, probably because
1537 // event handling was slightly different there, so it was never discovered.
1538 // Fixes bug: #5120
1539 static const uint16 fanmadeSignatureDemoQuestInfiniteLoop[] = {
1540 	0x38, SIG_UINT16(0x004c),        // pushi 004c
1541 	0x39, 0x00,                      // pushi 00
1542 	0x87, 0x01,                      // lap param[1]
1543 	0x4b, 0x04,                      // send 04
1544 	SIG_MAGICDWORD,
1545 	0x18,                            // not
1546 	0x30, SIG_UINT16(0x002f),        // bnt 002f  [06a5]    --> jmp ffbc  [0664] --> BUG! infinite loop
1547 	SIG_END
1548 };
1549 
1550 static const uint16 fanmadePatchDemoQuestInfiniteLoop[] = {
1551 	PATCH_ADDTOOFFSET(+10),
1552 	0x30, PATCH_UINT16(0x0032),      // bnt 0032  [06a8] --> pushi 004c
1553 	PATCH_END
1554 };
1555 
1556 //          script, description,                                      signature                                  patch
1557 static const SciScriptPatcherEntry fanmadeSignatures[] = {
1558 	{  true,   994, "Cascade Quest: fix auto-saving",              1, fanmadeSignatureCascadeQuestFixAutoSaving, fanmadePatchCascadeQuestFixAutoSaving },
1559 	{  true,   999, "Demo Quest: infinite loop on typo",           1, fanmadeSignatureDemoQuestInfiniteLoop,     fanmadePatchDemoQuestInfiniteLoop },
1560 	SCI_SIGNATUREENTRY_TERMINATOR
1561 };
1562 
1563 // ===========================================================================
1564 
1565 // WORKAROUND
1566 // Freddy Pharkas intro screen
1567 // Sierra used inner loops for the scaling of the 2 title views.
1568 // Those inner loops don't call kGameIsRestarting, which is why
1569 // we do not update the screen and we also do not throttle.
1570 //
1571 // This patch fixes this and makes it work.
1572 // Applies to at least: English PC-CD
1573 // Responsible method: sTownScript::changeState(1), sTownScript::changeState(3) (script 110)
1574 static const uint16 freddypharkasSignatureIntroScaling[] = {
1575 	0x38, SIG_ADDTOOFFSET(+2),       // pushi (setLoop) (009b for PC CD)
1576 	0x78,                            // push1
1577 	SIG_ADDTOOFFSET(+1),             // push0 for first code, push1 for second code
1578 	0x38, SIG_ADDTOOFFSET(+2),       // pushi (setStep) (0143 for PC CD)
1579 	0x7a,                            // push2
1580 	0x39, 0x05,                      // pushi 05
1581 	0x3c,                            // dup
1582 	0x72, SIG_ADDTOOFFSET(+2),       // lofsa (view)
1583 	SIG_MAGICDWORD,
1584 	0x4a, 0x1e,                      // send 1e
1585 	0x35, 0x0a,                      // ldi 0a
1586 	0xa3, 0x02,                      // sal local[2]
1587 	// start of inner loop
1588 	0x8b, 0x02,                      // lsl local[2]
1589 	SIG_ADDTOOFFSET(+43),            // skip almost all of inner loop
1590 	0xa3, 0x02,                      // sal local[2]
1591 	0x33, 0xcf,                      // jmp [inner loop start]
1592 	SIG_END
1593 };
1594 
1595 static const uint16 freddypharkasPatchIntroScaling[] = {
1596 	// remove setLoop(), objects in heap are already prepared, saves 5 bytes
1597 	0x38,
1598 	PATCH_GETORIGINALUINT16(+6),     // pushi (setStep)
1599 	0x7a,                            // push2
1600 	0x39, 0x05,                      // pushi 05
1601 	0x3c,                            // dup
1602 	0x72,
1603 	PATCH_GETORIGINALUINT16(+13),    // lofsa (view)
1604 	0x4a, 0x18,                      // send 18 - adjusted
1605 	0x35, 0x0a,                      // ldi 0a
1606 	0xa3, 0x02,                      // sal local[2]
1607 	// start of new inner loop
1608 	0x39, 0x00,                      // pushi 00
1609 	0x43, 0x2c, 0x00,                // callk GameIsRestarting (add this to trigger our speed throttler)
1610 	PATCH_ADDTOOFFSET(+47),          // skip almost all of inner loop
1611 	0x33, 0xca,                      // jmp [inner loop start]
1612 	PATCH_END
1613 };
1614 
1615 // PointsSound::check waits for a signal. If no signal is received, it'll call
1616 //   kDoSound(0x0d) which is a dummy in sierra sci. ScummVM and will use acc
1617 //   (which is not set by the dummy) to trigger sound disposal. This somewhat
1618 //   worked in sierra sci because the sample was already playing in the sound
1619 //   driver. In our case, that would also stop the sample from playing, so we
1620 //   patch it out. The "score" code is already buggy and sets volume to 0 when
1621 //   playing.
1622 // Applies to at least: English PC-CD
1623 // Responsible method: PointsSound::check in script 0
1624 // Fixes bug: #5059
1625 static const uint16 freddypharkasSignatureScoreDisposal[] = {
1626 	0x67, 0x32,                      // pTos 32 (selector theAudCount)
1627 	0x78,                            // push1
1628 	SIG_MAGICDWORD,
1629 	0x39, 0x0d,                      // pushi 0d
1630 	0x43, 0x75, 0x02,                // callk DoAudio
1631 	0x1c,                            // ne?
1632 	0x31,                            // bnt [skip disposal]
1633 	SIG_END
1634 };
1635 
1636 static const uint16 freddypharkasPatchScoreDisposal[] = {
1637 	0x34, PATCH_UINT16(0x0000),      // ldi 0000
1638 	0x34, PATCH_UINT16(0x0000),      // ldi 0000
1639 	0x34, PATCH_UINT16(0x0000),      // ldi 0000
1640 	PATCH_END
1641 };
1642 
1643 // In script 235, rm235::init and sEnterFrom500 disable icon 7+8 of iconbar (CD
1644 //  only). When picking up the canister after placing it down, the scripts will
1645 //  disable all the other icons. This results in IconBar::disable doing endless
1646 //  loops even in sierra sci because there is no enabled icon left. We remove
1647 //  the disabling of icon 8 (which is help). This fixes the issue.
1648 // Applies to at least: English PC-CD
1649 // Responsible method: rm235::init and sEnterFrom500::changeState
1650 // Fixes bug: #5245
1651 static const uint16 freddypharkasSignatureCanisterHang[] = {
1652 	0x38, SIG_SELECTOR16(disable),   // pushi disable
1653 	0x7a,                            // push2
1654 	SIG_MAGICDWORD,
1655 	0x39, 0x07,                      // pushi 07
1656 	0x39, 0x08,                      // pushi 08
1657 	0x81, 0x45,                      // lag global[45]
1658 	0x4a, 0x08,                      // send 08 (call IconBar::disable(7, 8))
1659 	SIG_END
1660 };
1661 
1662 static const uint16 freddypharkasPatchCanisterHang[] = {
1663 	PATCH_ADDTOOFFSET(+3),
1664 	0x78,                            // push1
1665 	PATCH_ADDTOOFFSET(+2),
1666 	0x33, 0x00,                      // jmp 0 (waste 2 bytes)
1667 	PATCH_ADDTOOFFSET(+3),
1668 	0x06,                            // send 06 (call IconBar::disable(7))
1669 	PATCH_END
1670 };
1671 
1672 // In script 215, lowerLadder::doit and highLadder::doit actually process
1673 //   keyboard presses when the ladder is on the screen in that room. They
1674 //   strangely also call kGetEvent. Because the main User::doit also calls
1675 //   kGetEvent, it's pure luck, where the event will hit. It's the same issue
1676 //   as in QfG1VGA. If you turn DOSBox to max cycles and click around for ego,
1677 //   sometimes clicks also won't get registered. Strangely it's not nearly
1678 //   as bad as in our sci, but these differences may be caused by timing.
1679 //   We just reuse the active event, thus removing the duplicate kGetEvent
1680 //   call.
1681 // Applies to at least: English PC-CD, German Floppy, English Mac
1682 // Responsible method: lowerLadder::doit and highLadder::doit
1683 // Fixes bug: #5060
1684 static const uint16 freddypharkasSignatureLadderEvent[] = {
1685 	0x39, SIG_MAGICDWORD,
1686 	SIG_SELECTOR8(new),              // pushi new
1687 	0x76,                            // push0
1688 	0x38, SIG_SELECTOR16(curEvent),  // pushi curEvent
1689 	0x76,                            // push0
1690 	0x81, 0x50,                      // lag global[50]
1691 	0x4a, 0x04,                      // send 04 - read User::curEvent
1692 	0x4a, 0x04,                      // send 04 - call curEvent::new
1693 	0xa5, 0x00,                      // sat temp[0]
1694 	0x38, SIG_SELECTOR16(localize),  // pushi localize
1695 	0x76,                            // push0
1696 	0x4a, 0x04,                      // send 04 (call curEvent::localize)
1697 	SIG_END
1698 };
1699 
1700 static const uint16 freddypharkasPatchLadderEvent[] = {
1701 	0x34, 0x00, 0x00,                // ldi 0000 (waste 3 bytes, overwrites first 2 pushes)
1702 	PATCH_ADDTOOFFSET(+8),
1703 	0xa5, 0x00,                      // sat temp[0] (waste 2 bytes, overwrites 2nd send)
1704 	PATCH_ADDTOOFFSET(+2),
1705 	0x34, 0x00, 0x00,                // ldi 0000
1706 	0x34, 0x00, 0x00,                // ldi 0000 (waste 6 bytes, overwrites last 3 opcodes)
1707 	PATCH_END
1708 };
1709 
1710 // In the Macintosh version of Freddy Pharkas, kRespondsTo is broken for
1711 // property selectors. They hacked the script to work around the issue,
1712 // so we revert the script back to using the values of the DOS script.
1713 // Applies to at least: English Mac
1714 // Responsible method: publicfpInv::drawInvWindow in script 15
1715 static const uint16 freddypharkasSignatureMacInventory[] = {
1716 	SIG_MAGICDWORD,
1717 	0x39, 0x23,                      // pushi 23
1718 	0x39, 0x74,                      // pushi 74
1719 	0x78,                            // push1
1720 	0x38, SIG_UINT16(0x0174),        // pushi 0174 (on mac it's actually 0x01, 0x74)
1721 	0x85, 0x15,                      // lat temp[15]
1722 	SIG_END
1723 };
1724 
1725 static const uint16 freddypharkasPatchMacInventory[] = {
1726 	0x39, 0x02,                      // pushi 02 (now matches the DOS version)
1727 	PATCH_ADDTOOFFSET(+23),
1728 	0x39, 0x04,                      // pushi 04 (now matches the DOS version)
1729 	PATCH_END
1730 };
1731 
1732 // WORKAROUND
1733 // FPFP Mac has an easter egg with a script bug that accidentally works in
1734 //  Sierra's interpreter. Clicking Talk on a small part of the mine in room 270
1735 //  triggers it. The script macThing plays macSound and waits for it to finish,
1736 //  but macSound:loop is set to -1, indicating that it should loop forever.
1737 //  ScummVM loops the sound and so macThing never advances to the next state and
1738 //  the user never regains control. Sierra's interpreter cues the script after
1739 //  the first play and doesn't loop the sound, despite macSound:loop.
1740 //
1741 // We work around this by setting macSound:loop correctly on the heap so that it
1742 //  only plays once and macThing proceeds.
1743 //
1744 // Applies to: Mac Floppy
1745 // Responsible method: Heap in script 270
1746 // Fixes bug #7065
1747 static const uint16 freddypharkasSignatureMacEasterEgg[] = {
1748 	SIG_MAGICDWORD,                 // macSound
1749 	SIG_UINT16(0x0b89),             // number = 2953
1750 	SIG_UINT16(0x007f),             // vol = 127
1751 	SIG_UINT16(0x0000),             // priority = 0
1752 	SIG_UINT16(0xffff),             // loop = -1 [ loop sound forever ]
1753 	SIG_END
1754 };
1755 
1756 static const uint16 freddypharkasPatchMacEasterEgg[] = {
1757 	PATCH_ADDTOOFFSET(+6),
1758 	PATCH_UINT16(0x0001),           // loop = 1 [ play sound once ]
1759 	PATCH_END
1760 };
1761 
1762 // FPFP Mac is missing view 844 of Hop Singh leaving town, breaking the scene.
1763 //  This occurs when going to the desert (room 200) after the restaurant closes
1764 //  but before act 3 ends. This would also crash the original so we just disable
1765 //  this minor optional scene.
1766 //
1767 // Applies to: Mac Floppy
1768 // Responsible method: rm200:init
1769 // Fixes bug #10954
1770 static const uint16 freddypharkasSignatureMacHopSingh[] = {
1771 	0x89, 0x77,                     // lsg 77
1772 	0x35, 0x13,                     // ldi 13
1773 	0x1a,                           // eq? [ did restaurant just close? ]
1774 	0x31, 0x46,                     // bnt 46 [ skip hop singh scene ]
1775 	SIG_ADDTOOFFSET(+0x41),
1776 	SIG_MAGICDWORD,
1777 	0x72, 0x01, 0xd0,               // lofsa hopSingh [ hard-coded big endian for mac ]
1778 	0x4a, 0x20,                     // send 20 [ hopSingh init: ... setScript: sLeaveTown ]
1779 	SIG_END
1780 };
1781 
1782 static const uint16 freddypharkasPatchMacHopSingh[] = {
1783 	0x33, 0x4b,                     // jmp 4b [ always skip hop singh scene ]
1784 	PATCH_END
1785 };
1786 
1787 // At the start of act 4 the church key is removed from inventory but reappears
1788 //  in the church door. The door script attempts to prevent this by not drawing
1789 //  the key in act 4 but the verb handler is missing this check. Looking at the
1790 //  door in act 4 still brings up the inset with the key. Sierra fixed this in
1791 //  Mac but forgot to include the fix in the CD version a year later.
1792 //
1793 // We fix this by replacing a duplicate inventory check with an act 4 check so
1794 //  that the key no longer appears in the inset and can't be picked up again.
1795 //
1796 // Applies to: PC Floppy, PC CD
1797 // Responsible method: inDoorInset:init
1798 // Fixes bug #10975
1799 static const uint16 freddypharkasSignatureChurchKey[] = {
1800 	SIG_MAGICDWORD,
1801 	0x76,                           // push0
1802 	0x59, 0x01,                     // &rest 01
1803 	0x57, SIG_ADDTOOFFSET(+1), 0x04,// super Inset 04 [ super: init &rest ]
1804 	0x38, SIG_SELECTOR16(has),      // pushi has
1805 	0x78,                           // push1
1806 	0x39, 0x06,                     // pushi 06
1807 	0x81, 0x00,                     // lag 00
1808 	0x4a, 0x06,                     // send 06 [ ego has: 6 (church key) ]
1809 	SIG_END
1810 };
1811 
1812 static const uint16 freddypharkasPatchChurchKey[] = {
1813 	PATCH_ADDTOOFFSET(+6),
1814 	0x89, 0x78,                     // lsg 78 [ act number ]
1815 	0x35, 0x04,                     // ldi 04
1816 	0x20,                           // ge?
1817 	0x33, 0x03,                     // jmp 03
1818 	PATCH_END
1819 };
1820 
1821 // After leaving the desk letter in the grave, the letter reappears in the desk.
1822 //  The desk script only checks if the letter is in inventory. Sierra started to
1823 //  fix this in the CD version by setting a new flag but forgot to check it.
1824 //
1825 // We fix this by testing Letter's owner, if -1 then it is in the grave.
1826 //
1827 // Applies to: All versions
1828 // Responsible method: deskDrawer:doVerb(1)
1829 // Fixes bug #10975
1830 static const uint16 freddypharkasSignatureDeskLetter[] = {
1831 	SIG_MAGICDWORD,
1832 	0x30, SIG_UINT16(0x0055),       // bnt 0055
1833 	0x38, SIG_SELECTOR16(has),      // pushi has
1834 	0x78,                           // push1
1835 	0x39, 0x1f,                     // pushi 1f
1836 	0x81, 0x00,                     // lag 00
1837 	0x4a, 0x06,                     // send 06 [ ego has: 31 (Letter) ]
1838 	0x18,                           // not
1839 	0x31, 0x1f,                     // bnt 1f
1840 	0x78,                           // push1
1841 	0x39, 0x31,                     // pushi 31
1842 	0x45, 0x02, 0x02,               // callb proc0_2 02 [ is flag 49 set? ]
1843 	0x31, 0x17,                     // bnt 17
1844 	0x38, SIG_ADDTOOFFSET(+2),      // pushi stopUpd
1845 	0x76,                           // push0
1846 	0x81, 0x00,                     // lag 00
1847 	0x4a, 0x04,                     // send 04 [ ego stopUpd: (optimization) ]
1848 	0x38, SIG_ADDTOOFFSET(+2),      // pushi setInset
1849 	0x78,                           // push1
1850 	0x72, SIG_UINT16(0x0522),       // lofsa inLetterInset
1851 	0x36,                           // push
1852 	0x81, 0x02,                     // lag 02
1853 	0x4a, 0x06,                     // send 06  [ rm610 setInset: inLetterInset ]
1854 	0x32, SIG_UINT16(0x008f),       // jmp 008f [ end of method ]
1855 	0x78,                           // push1
1856 	0x39, 0x31,                     // pushi 31
1857 	0x45, 0x02, 0x02,               // callb proc0_2 02 [ is flag 49 set? ]
1858 	0x31, 0x11,                     // bnt 11 [ drawer is closed ]
1859 	SIG_END
1860 };
1861 
1862 static const uint16 freddypharkasPatchDeskLetter[] = {
1863 	0x31, 0x56,                     // bnt 56
1864 	0x78,                           // push1
1865 	0x39, 0x31,                     // pushi 31
1866 	0x45, 0x02, 0x02,               // callb proc0_2 02 [ is flag 49 set? ]
1867 	0x31, 0x3e,                     // bnt 3e  [ drawer is closed ]
1868 	0x38, PATCH_SELECTOR16(has),    // pushi has
1869 	0x78,                           // push1
1870 	0x39, 0x1f,                     // pushi 1f
1871 	0x81, 0x00,                     // lag 00
1872 	0x4a, 0x06,                     // send 06 [ ego has: 31 (Letter) ]
1873 	0x2f, 0x21,                     // bt 21   [ drawer is open and empty ]
1874 	0x39, PATCH_SELECTOR8(at),      // pushi at
1875 	0x78,                           // push1
1876 	0x39, 0x1f,                     // pushi 1f
1877 	0x81, 0x09,                     // lag 09
1878 	0x4a, 0x06,                     // send 06 [ fpInv at: 31 (Letter) ]
1879 	0x38, PATCH_SELECTOR16(owner),  // pushi owner
1880 	0x76,                           // push0
1881 	0x4a, 0x04,                     // send 04 [ Letter owner? ]
1882 	0x39, 0xff,                     // pushi ff
1883 	0x1a,                           // eq?
1884 	0x2f, 0x0d,                     // bt 0d   [ drawer is open and empty ]
1885 	0x38, PATCH_GETORIGINALUINT16(+33), // pushi setInset
1886 	0x78,                           // push1
1887 	0x74, PATCH_UINT16(0x0522),     // lofss inLetterInset
1888 	0x81, 0x02,                     // lag 02
1889 	0x4a, 0x06,                     // send 06 [ rm610 setInset: inLetterInset ]
1890 	0x3a,                           // toss
1891 	0x48,                           // ret
1892 	PATCH_END
1893 };
1894 
1895 //          script, description,                                      signature                            patch
1896 static const SciScriptPatcherEntry freddypharkasSignatures[] = {
1897 	{  true,     0, "CD: score early disposal",                    1, freddypharkasSignatureScoreDisposal, freddypharkasPatchScoreDisposal },
1898 	{  true,    15, "Mac: broken inventory",                       1, freddypharkasSignatureMacInventory,  freddypharkasPatchMacInventory },
1899 	{  true,   110, "intro scaling workaround",                    2, freddypharkasSignatureIntroScaling,  freddypharkasPatchIntroScaling },
1900 	{  false,  200, "Mac: skip broken hop singh scene",            1, freddypharkasSignatureMacHopSingh,   freddypharkasPatchMacHopSingh },
1901 	{  true,   235, "CD: canister pickup hang",                    3, freddypharkasSignatureCanisterHang,  freddypharkasPatchCanisterHang },
1902 	{  true,   270, "Mac: easter egg hang",                        1, freddypharkasSignatureMacEasterEgg,  freddypharkasPatchMacEasterEgg },
1903 	{  true,   310, "church key reappears",                        1, freddypharkasSignatureChurchKey,     freddypharkasPatchChurchKey },
1904 	{  true,   320, "ladder event issue",                          2, freddypharkasSignatureLadderEvent,   freddypharkasPatchLadderEvent },
1905 	{  true,   610, "desk letter reappears",                       1, freddypharkasSignatureDeskLetter,    freddypharkasPatchDeskLetter },
1906 	SCI_SIGNATUREENTRY_TERMINATOR
1907 };
1908 
1909 // ===========================================================================
1910 
1911 // During Bridge, Declarer_Second_NT:think performs a bitwise or against an
1912 //  object due to a script typo. This operation is supposed to be against
1913 //  (bridgeHand:highCard):rank but instead it's against bridgeHand:highCard.
1914 //  ThirdSeat_Trump:think has a correct version of this. Declarer_Second_NT must
1915 //  have just been missing the word "rank" in the original script.
1916 //
1917 // We fix this by inserting the missing rank code. To make room we remove the
1918 //  call to self:checkFinCard. It's called immediately before this patch and its
1919 //  result is stored in local1 so we just use that. Hoyle5 also has this bug.
1920 //
1921 // Applies to at least: English PC
1922 // Responsible method: Declarer_Second_NT:think
1923 // Fixes bug #11163
1924 static const uint16 hoyle4SignatureBridgeArithmetic[] = {
1925 	0x36,                               // push [ bridgeHand:highCard ]
1926 	0x34, SIG_UINT16(0x0f00),           // ldi 0f00
1927 	0x14,                               // or [ error: bridgeHand:highCard is an object ]
1928 	0x36,                               // push
1929 	0x63, 0x42,                         // pToa pard
1930 	0x4a, 0x08,                         // send 08 [ pard hasCard: theSuitLead (bridgeHand:highCard | 0f00) ]
1931 	SIG_MAGICDWORD,
1932 	0x2f, 0x1d,                         // bt 1d
1933 	0x83, 0x03,                         // lal 03
1934 	0x2f, 0x19,                         // bt 19
1935 	0x38, SIG_ADDTOOFFSET(+2),          // pushi rank
1936 	0x76,                               // push0
1937 	0x38, SIG_ADDTOOFFSET(+2),          // pushi checkFinCard
1938 	0x78,                               // push1
1939 	0x67, 0x48,                         // pTos theSuitLead
1940 	0x54, 0x06,                         // self 06 [ self checkFinCard: theSuitLead ]
1941 	SIG_END
1942 };
1943 
1944 static const uint16 hoyle4PatchBridgeArithmetic[] = {
1945 	0x38, PATCH_GETORIGINALUINT16(+17), // pushi rank
1946 	0x76,                               // push0
1947 	0x4a, 0x04,                         // send 04 [ bridgeHand:highCard rank? ]
1948 	0x36,                               // push
1949 	0x34, PATCH_UINT16(0x0f00),         // ldi 0f00
1950 	0x14,                               // or
1951 	0x36,                               // push
1952 	0x63, 0x42,                         // pToa pard
1953 	0x4a, 0x08,                         // send 08 [ pard hasCard: theSuitLead ((bridgeHand:highCard):rank | 0f00) ]
1954 	0x2f, 0x17,                         // bt 17
1955 	0x83, 0x03,                         // lal 03
1956 	0x2f, 0x13,                         // bt 13
1957 	0x38, PATCH_GETORIGINALUINT16(+17), // pushi rank
1958 	0x76,                               // push0
1959 	0x83, 0x01,                         // lal 01 [ set to "self checkFinCard: theSuitLead" earlier ]
1960 	PATCH_END
1961 };
1962 
1963 //          script, description,                                      signature                         patch
1964 static const SciScriptPatcherEntry hoyle4Signatures[] = {
1965 	{  true,   733, "bridge arithmetic against object ",           1, hoyle4SignatureBridgeArithmetic,  hoyle4PatchBridgeArithmetic },
1966 	SCI_SIGNATUREENTRY_TERMINATOR
1967 };
1968 
1969 #ifdef ENABLE_SCI32
1970 #pragma mark -
1971 #pragma mark Hoyle 5
1972 
1973 // Several scripts in Hoyle5 contain a subroutine which spins on kGetTime until
1974 // a certain number of ticks elapse. Since this wastes CPU and makes ScummVM
1975 // unresponsive, the kWait kernel function (which was removed in SCI2) is
1976 // reintroduced for Hoyle5, and the spin subroutines are patched here to call
1977 // that function instead.
1978 // Applies to at least: English Demo
1979 static const uint16 hoyle5SignatureSpinLoop[] = {
1980 	SIG_MAGICDWORD,
1981 	0x76,                         // push0
1982 	0x43, 0x79, SIG_UINT16(0x00), // callk GetTime, $0
1983 	0x36,                         // push
1984 	0x87, 0x01,                   // lap param[1]
1985 	0x02,                         // add
1986 	0xa5, 0x00,                   // sat temp[0]
1987 	SIG_END
1988 };
1989 
1990 static const uint16 hoyle5PatchSpinLoop[] = {
1991 	0x78,                                     // push1
1992 	0x8f, 0x01,                               // lsp param[1]
1993 	0x43, kScummVMWaitId, PATCH_UINT16(0x02), // callk Wait, $2
1994 	0x48,                                     // ret
1995 	PATCH_END
1996 };
1997 
1998 // While playing Old Maid (room 200), a repeated typo in the game script
1999 // means that `setScale` is called accidentally instead of `setScaler`.
2000 // In SSCI this did not do much because the first argument happened to be
2001 // smaller than the y-position of `ego`, but in ScummVM the first argument is
2002 // larger and so a debug message "y value less than vanishingY" is displayed.
2003 // This is the same issue as with LSL6 hires.
2004 static const uint16 hoyle5SetScaleSignature[] = {
2005 	SIG_MAGICDWORD,
2006 	0x38, SIG_SELECTOR16(setScale), // pushi setScale ($14b)
2007 	0x38, SIG_UINT16(0x05),         // pushi 5
2008 	0x51, 0x2c,                     // class Scaler
2009 	SIG_END
2010 };
2011 
2012 static const uint16 hoyle5PatchSetScale[] = {
2013 	0x38, PATCH_SELECTOR16(setScaler), // pushi setScaler ($14f)
2014 	PATCH_END
2015 };
2016 
2017 // There are two derived collections of Hoyle Classic Games:
2018 // 1) The Hoyle Children's Collection, which includes the following games:
2019 // - Crazy Eights (script 100)
2020 // - Old Maid (script 200)
2021 // - Checkers (script 1200)
2022 // 2) Hoyle Bridge, which includes the following games:
2023 // - Bridge (script 700)
2024 // In these two collections, the scripts for the other games have been removed.
2025 // Choosing any other game than the above results in a "No script found" error.
2026 // The original game did not show the game selection screen, as there were
2027 // direct shortucts to each game.
2028 // Since we do show the game selection screen, we remove all the games
2029 // which from the ones below, which are not included in each version:
2030 // - Crazy Eights (script 100)
2031 // - Old Maid (script 200)
2032 // - Hearts (script 300)
2033 // - Gin Rummy (script 400)
2034 // - Cribbage (script 500)
2035 // - Klondike / Solitaire (script 600)
2036 // - Bridge (script 700)
2037 // - Poker (script 1100)
2038 // - Checkers (script 1200)
2039 // - Backgammon (script 1300)
2040 static const uint16 hoyle5SignatureCrazyEights[] = {
2041 	SIG_MAGICDWORD,
2042 	0x38, 0x8e, 0x00,      // pushi 008e
2043 	0x76,                  // push0
2044 	0x38, 0xf0, 0x02,      // pushi 02f0
2045 	0x76,                  // push0
2046 	0x72, 0x9c, 0x01,      // lofsa chooseCrazy8s
2047 	0x4a, 0x08, 0x00,      // send  0008
2048 	SIG_END
2049 };
2050 
2051 static const uint16 hoyle5SignatureOldMaid[] = {
2052 	SIG_MAGICDWORD,
2053 	0x38, 0x8e, 0x00,      // pushi 008e
2054 	0x76,                  // push0
2055 	0x38, 0xf0, 0x02,      // pushi 02f0
2056 	0x76,                  // push0
2057 	0x72, 0x2c, 0x02,      // lofsa chooseOldMaid
2058 	0x4a, 0x08, 0x00,      // send  0008
2059 	SIG_END
2060 };
2061 
2062 static const uint16 hoyle5SignatureHearts[] = {
2063 	SIG_MAGICDWORD,
2064 	0x38, 0x8e, 0x00,      // pushi 008e
2065 	0x76,                  // push0
2066 	0x38, 0xf0, 0x02,      // pushi 02f0
2067 	0x76,                  // push0
2068 	0x72, 0xdc, 0x03,      // lofsa chooseHearts
2069 	0x4a, 0x08, 0x00,      // send  0008
2070 	SIG_END
2071 };
2072 
2073 static const uint16 hoyle5SignatureGinRummy[] = {
2074 	SIG_MAGICDWORD,
2075 	0x38, 0x8e, 0x00,      // pushi 008e
2076 	0x76,                  // push0
2077 	0x38, 0xf0, 0x02,      // pushi 02f0
2078 	0x76,                  // push0
2079 	0x72, 0xbc, 0x02,      // lofsa chooseGinRummy
2080 	0x4a, 0x08, 0x00,      // send  0008
2081 	SIG_END
2082 };
2083 
2084 static const uint16 hoyle5SignatureCribbage[] = {
2085 	SIG_MAGICDWORD,
2086 	0x38, 0x8e, 0x00,      // pushi 008e
2087 	0x76,                  // push0
2088 	0x38, 0xf0, 0x02,      // pushi 02f0
2089 	0x76,                  // push0
2090 	0x72, 0x4c, 0x03,      // lofsa chooseCribbage
2091 	0x4a, 0x08, 0x00,      // send  0008
2092 	SIG_END
2093 };
2094 
2095 static const uint16 hoyle5SignatureKlondike[] = {
2096 	SIG_MAGICDWORD,
2097 	0x38, 0x8e, 0x00,      // pushi 008e
2098 	0x76,                  // push0
2099 	0x38, 0xf0, 0x02,      // pushi 02f0
2100 	0x76,                  // push0
2101 	0x72, 0xfc, 0x04,      // lofsa chooseKlondike
2102 	0x4a, 0x08, 0x00,      // send  0008
2103 	SIG_END
2104 };
2105 
2106 static const uint16 hoyle5SignatureBridge[] = {
2107 	SIG_MAGICDWORD,
2108 	0x38, 0x8e, 0x00,      // pushi 008e
2109 	0x76,                  // push0
2110 	0x38, 0xf0, 0x02,      // pushi 02f0
2111 	0x76,                  // push0
2112 	0x72, 0x6c, 0x04,      // lofsa chooseBridge
2113 	0x4a, 0x08, 0x00,      // send  0008
2114 	SIG_END
2115 };
2116 
2117 static const uint16 hoyle5SignaturePoker[] = {
2118 	SIG_MAGICDWORD,
2119 	0x38, 0x8e, 0x00,      // pushi 008e
2120 	0x76,                  // push0
2121 	0x38, 0xf0, 0x02,      // pushi 02f0
2122 	0x76,                  // push0
2123 	0x72, 0x8c, 0x05,      // lofsa choosePoker
2124 	0x4a, 0x08, 0x00,      // send  0008
2125 	SIG_END
2126 };
2127 
2128 static const uint16 hoyle5SignatureCheckers[] = {
2129 	SIG_MAGICDWORD,
2130 	0x38, 0x8e, 0x00,      // pushi 008e
2131 	0x76,                  // push0
2132 	0x38, 0xf0, 0x02,      // pushi 02f0
2133 	0x76,                  // push0
2134 	0x72, 0x1c, 0x06,      // lofsa chooseCheckers
2135 	0x4a, 0x08, 0x00,      // send  0008
2136 	SIG_END
2137 };
2138 
2139 static const uint16 hoyle5SignatureBackgammon[] = {
2140 	SIG_MAGICDWORD,
2141 	0x38, 0x8e, 0x00,      // pushi 008e
2142 	0x76,                  // push0
2143 	0x38, 0xf0, 0x02,      // pushi 02f0
2144 	0x76,                  // push0
2145 	0x72, 0xac, 0x06,      // lofsa chooseBackgammon
2146 	0x4a, 0x08, 0x00,      // send  0008
2147 	SIG_END
2148 };
2149 
2150 static const uint16 hoyle5PatchDisableGame[] = {
2151 	0x35, 0x00,                      // ldi 00
2152 	0x35, 0x00,                      // ldi 00
2153 	0x35, 0x00,                      // ldi 00
2154 	0x35, 0x00,                      // ldi 00
2155 	0x35, 0x00,                      // ldi 00
2156 	0x35, 0x00,                      // ldi 00
2157 	0x35, 0x00,                      // ldi 00
2158 	PATCH_END
2159 };
2160 
2161 // During Bridge, Declarer_Second_NT:think performs a bitwise or against an
2162 //  object due to a script typo. This script bug is also in Hoyle4, see its
2163 //  patch notes above for more detail.
2164 //
2165 // Applies to at least: English PC
2166 // Responsible method: Declarer_Second_NT:think
2167 // Fixes bug #11173
2168 static const uint16 hoyle5SignatureBridgeArithmetic[] = {
2169 	0x36,                               // push [ bridgeHand:highCard ]
2170 	0x34, SIG_UINT16(0x0f00),           // ldi 0f00
2171 	0x14,                               // or [ error: bridgeHand:highCard is an object ]
2172 	0x36,                               // push
2173 	0x63, 0x44,                         // pToa pard
2174 	0x4a, SIG_UINT16(0x0008),           // send 08 [ pard hasCard: theSuitLead (bridgeHand:highCard | 0f00) ]
2175 	0x2f, 0x26,                         // bt 26
2176 	0x7e, SIG_ADDTOOFFSET(+2),          // line
2177 	SIG_MAGICDWORD,
2178 	0x83, 0x03,                         // lal 03
2179 	0x2f, 0x1f,                         // bt 1f
2180 	0x7e, SIG_ADDTOOFFSET(+2),          // line
2181 	0x38,                               // pushi rank
2182 	SIG_END
2183 };
2184 
2185 static const uint16 hoyle5PatchBridgeArithmetic[] = {
2186 	0x38, PATCH_GETORIGINALUINT16(+24), // pushi rank
2187 	0x76,                               // push0
2188 	0x4a, PATCH_UINT16(0x0004),         // send 04 [ bridgeHand:highCard rank? ]
2189 	0x38, PATCH_UINT16(0x0f00),         // pushi 0f00
2190 	0x14,                               // or
2191 	0x36,                               // push
2192 	0x63, 0x44,                         // pToa pard
2193 	0x4a, PATCH_UINT16(0x0008),         // send 08 [ pard hasCard: theSuitLead ((bridgeHand:highCard):rank | 0f00) ]
2194 	0x2f, 0x20,                         // bt 20
2195 	0x83, 0x03,                         // lal 03
2196 	0x2f, 0x1c,                         // bt 1c
2197 	PATCH_END
2198 };
2199 
2200 //          script, description,                                      signature                         patch
2201 static const SciScriptPatcherEntry hoyle5Signatures[] = {
2202 	{  true,     3, "remove kGetTime spin",                        1, hoyle5SignatureSpinLoop,          hoyle5PatchSpinLoop },
2203 	{  true,    23, "remove kGetTime spin",                        1, hoyle5SignatureSpinLoop,          hoyle5PatchSpinLoop },
2204 	{  true,   200, "fix setScale calls",                         11, hoyle5SetScaleSignature,          hoyle5PatchSetScale },
2205 	{  true,   500, "remove kGetTime spin",                        1, hoyle5SignatureSpinLoop,          hoyle5PatchSpinLoop },
2206 	{  true, 64937, "remove kGetTime spin",                        1, hoyle5SignatureSpinLoop,          hoyle5PatchSpinLoop },
2207 	{  true, 64908, "disable video benchmarking",                  1, sci2BenchmarkSignature,           sci2BenchmarkPatch },
2208 	{  true,   733, "bridge arithmetic against object ",           1, hoyle5SignatureBridgeArithmetic,  hoyle5PatchBridgeArithmetic },
2209 	// This entry has been placed so that the broken Poker game is disabled. This game uses an external DLL, PENGIN16.DLL,
2210 	// which is invoked via kWinDLL. We need to reverse the logic in PENGIN16.DLL and call it directly, in order to get this
2211 	// game to work properly. Until then, this game entry will be disabled.
2212 	{  true,   975, "disable Poker",                               1, hoyle5SignaturePoker,             hoyle5PatchDisableGame },
2213 	{  true, 64990, "increase number of save games (1/2)",         1, sci2NumSavesSignature1,           sci2NumSavesPatch1 },
2214 	{  true, 64990, "increase number of save games (2/2)",         1, sci2NumSavesSignature2,           sci2NumSavesPatch2 },
2215 	{  true, 64990, "disable change directory button",             1, sci2ChangeDirSignature,           sci2ChangeDirPatch },
2216 	SCI_SIGNATUREENTRY_TERMINATOR
2217 };
2218 
2219 //          script, description,                                      signature                         patch
2220 static const SciScriptPatcherEntry hoyle5ChildrensCollectionSignatures[] = {
2221 	{  true,     3, "remove kGetTime spin",                        1, hoyle5SignatureSpinLoop,          hoyle5PatchSpinLoop },
2222 	{  true,    23, "remove kGetTime spin",                        1, hoyle5SignatureSpinLoop,          hoyle5PatchSpinLoop },
2223 	{  true,   200, "fix setScale calls",                         11, hoyle5SetScaleSignature,          hoyle5PatchSetScale },
2224 	{  true,   500, "remove kGetTime spin",                        1, hoyle5SignatureSpinLoop,          hoyle5PatchSpinLoop },
2225 	{  true, 64937, "remove kGetTime spin",                        1, hoyle5SignatureSpinLoop,          hoyle5PatchSpinLoop },
2226 	{  true, 64908, "disable video benchmarking",                  1, sci2BenchmarkSignature,           sci2BenchmarkPatch },
2227 	{  true,   975, "disable Gin Rummy",                           1, hoyle5SignatureGinRummy,          hoyle5PatchDisableGame },
2228 	{  true,   975, "disable Cribbage",                            1, hoyle5SignatureCribbage,          hoyle5PatchDisableGame },
2229 	{  true,   975, "disable Klondike",                            1, hoyle5SignatureKlondike,          hoyle5PatchDisableGame },
2230 	{  true,   975, "disable Bridge",                              1, hoyle5SignatureBridge,            hoyle5PatchDisableGame },
2231 	{  true,   975, "disable Poker",                               1, hoyle5SignaturePoker,             hoyle5PatchDisableGame },
2232 	{  true,   975, "disable Hearts",                              1, hoyle5SignatureHearts,            hoyle5PatchDisableGame },
2233 	{  true,   975, "disable Backgammon",                          1, hoyle5SignatureBackgammon,        hoyle5PatchDisableGame },
2234 	SCI_SIGNATUREENTRY_TERMINATOR
2235 };
2236 
2237 //          script, description,                                      signature                         patch
2238 static const SciScriptPatcherEntry hoyle5BridgeSignatures[] = {
2239 	{  true,     3, "remove kGetTime spin",                        1, hoyle5SignatureSpinLoop,          hoyle5PatchSpinLoop },
2240 	{  true,    23, "remove kGetTime spin",                        1, hoyle5SignatureSpinLoop,          hoyle5PatchSpinLoop },
2241 	{  true,   500, "remove kGetTime spin",                        1, hoyle5SignatureSpinLoop,          hoyle5PatchSpinLoop },
2242 	{  true, 64937, "remove kGetTime spin",                        1, hoyle5SignatureSpinLoop,          hoyle5PatchSpinLoop },
2243 	{  true, 64908, "disable video benchmarking",                  1, sci2BenchmarkSignature,           sci2BenchmarkPatch },
2244 	{  true,   733, "bridge arithmetic against object ",           1, hoyle5SignatureBridgeArithmetic,  hoyle5PatchBridgeArithmetic },
2245 	{  true,   975, "disable Gin Rummy",                           1, hoyle5SignatureGinRummy,          hoyle5PatchDisableGame },
2246 	{  true,   975, "disable Cribbage",                            1, hoyle5SignatureCribbage,          hoyle5PatchDisableGame },
2247 	{  true,   975, "disable Klondike",                            1, hoyle5SignatureKlondike,          hoyle5PatchDisableGame },
2248 	{  true,   975, "disable Poker",                               1, hoyle5SignaturePoker,             hoyle5PatchDisableGame },
2249 	{  true,   975, "disable Hearts",                              1, hoyle5SignatureHearts,            hoyle5PatchDisableGame },
2250 	{  true,   975, "disable Backgammon",                          1, hoyle5SignatureBackgammon,        hoyle5PatchDisableGame },
2251 	{  true,   975, "disable Crazy Eights",                        1, hoyle5SignatureCrazyEights,       hoyle5PatchDisableGame },
2252 	{  true,   975, "disable Old Maid",                            1, hoyle5SignatureOldMaid,           hoyle5PatchDisableGame },
2253 	{  true,   975, "disable Checkers",                            1, hoyle5SignatureCheckers,          hoyle5PatchDisableGame },
2254 	{  true, 64990, "increase number of save games (1/2)",         1, sci2NumSavesSignature1,           sci2NumSavesPatch1 },
2255 	{  true, 64990, "increase number of save games (2/2)",         1, sci2NumSavesSignature2,           sci2NumSavesPatch2 },
2256 	{  true, 64990, "disable change directory button",             1, sci2ChangeDirSignature,           sci2ChangeDirPatch },
2257 	SCI_SIGNATUREENTRY_TERMINATOR
2258 };
2259 
2260 #pragma mark -
2261 #pragma mark Gabriel Knight 1
2262 
2263 // `daySixBeignet::changeState(4)` is called when the cop goes outside. It sets
2264 // cycles to 220. This is a CPU-speed dependent value and not usually enough
2265 // time to get to the door, so patch it to 22 seconds.
2266 //
2267 // Applies to at least: English PC-CD, German PC-CD, English Mac
2268 static const uint16 gk1Day6PoliceBeignetSignature1[] = {
2269 	0x35, 0x04,                    // ldi 4
2270 	0x1a,                          // eq?
2271 	0x30, SIG_ADDTOOFFSET(+2),     // bnt [next state check]
2272 	0x38, SIG_SELECTOR16(dispose), // pushi dispose
2273 	0x76,                          // push0
2274 	0x72, SIG_ADDTOOFFSET(+2),     // lofsa deskSarg
2275 	0x4a, SIG_UINT16(0x04),        // send 4
2276 	SIG_MAGICDWORD,
2277 	0x34, SIG_UINT16(0xdc),        // ldi 220
2278 	0x65, SIG_ADDTOOFFSET(+1),     // aTop cycles ($1a for PC, $1c for Mac)
2279 	0x32,                          // jmp [end]
2280 	SIG_END
2281 };
2282 
2283 static const uint16 gk1Day6PoliceBeignetPatch1[] = {
2284 	PATCH_ADDTOOFFSET(+16),
2285 	0x34, PATCH_UINT16(0x16),                   // ldi 22
2286 	0x65, PATCH_GETORIGINALBYTEADJUST(+20, +2), // aTop seconds ($1c for PC, $1e for Mac)
2287 	PATCH_END
2288 };
2289 
2290 // On day 6 when the cop is outside for the beignet, walking through the
2291 // swinging door will also reset the puzzle timer so the player has 200 cycles
2292 // to get through the area before the cop returns. This is a CPU-speed
2293 // dependent value and not usually enough time to get to the door, so we patch
2294 // it to 20 seconds instead.
2295 //
2296 // Applies to at least: English PC-CD, German PC-CD, English Mac
2297 // Responsible method: sInGateWithPermission::changeState(0)
2298 // Fixes bug: #9805
2299 static const uint16 gk1Day6PoliceBeignetSignature2[] = {
2300 	0x72, SIG_ADDTOOFFSET(+2),    // lofsa daySixBeignet
2301 	0x1a,                         // eq?
2302 	0x31, 0x0d,                   // bnt [skip set cycles]
2303 	0x38, SIG_SELECTOR16(cycles), // pushi cycles
2304 	0x78,                         // push1
2305 	SIG_MAGICDWORD,
2306 	0x38, SIG_UINT16(0xc8),       // pushi 200
2307 	0x72,                         // lofsa
2308 	SIG_END
2309 };
2310 
2311 static const uint16 gk1Day6PoliceBeignetPatch2[] = {
2312 	PATCH_ADDTOOFFSET(+6),
2313 	0x38, PATCH_SELECTOR16(seconds), // pushi seconds
2314 	0x78,                            // push1
2315 	0x38, PATCH_UINT16(0x14),        // pushi 20
2316 	PATCH_END
2317 };
2318 
2319 // `sargSleeping::changeState(8)` is called when the cop falls asleep and sets
2320 // the puzzle timer to 220 cycles. This is CPU-speed dependent and not usually
2321 // enough time to get to the door, so patch it to 22 seconds instead.
2322 //
2323 // Applies to at least: English PC-CD, German PC-CD, English Mac
2324 static const uint16 gk1Day6PoliceSleepSignature[] = {
2325 	0x35, 0x08,                // ldi 8
2326 	0x1a,                      // eq?
2327 	0x31, SIG_ADDTOOFFSET(+1), // bnt [next state check]
2328 	SIG_MAGICDWORD,
2329 	0x34, SIG_UINT16(0xdc),    // ldi 220
2330 	0x65, SIG_ADDTOOFFSET(+1), // aTop cycles ($1a for PC, $1c for Mac)
2331 	0x32,                      // jmp [end]
2332 	SIG_END
2333 };
2334 
2335 static const uint16 gk1Day6PoliceSleepPatch[] = {
2336 	PATCH_ADDTOOFFSET(+5),
2337 	0x34, PATCH_UINT16(0x16),                  // ldi 22
2338 	0x65, PATCH_GETORIGINALBYTEADJUST(+9, +2), // aTop seconds (1c for PC, 1e for Mac)
2339 	PATCH_END
2340 };
2341 
2342 // At the start of day 5, when the player already has the veve but still needs
2343 // to get the drum book, the drum book dialogue with Grace is played twice in
2344 // a row, and then the veve dialogue gets played again even though it was
2345 // already played during day 4.
2346 //
2347 // The duplicate drum book dialogue happens because it is triggered once in
2348 // `GetTheVeve::changeState(0)` and then again in `GetTheVeve::changeState(11)`.
2349 // The re-run of the veve dialogue happens because the game gives the player
2350 // the drum book in `GetTheVeVe::changeState(1)`, then *after* doing so, checks
2351 // if the player has the drum book and runs the veve dialogue if so.
2352 //
2353 // We fix both of these issues by skipping the has-drum-book check if the player
2354 // just got the drum book in 'GetTheVeve::changeState(1)'.
2355 // Doing this causes the game to jump from state 1 to state 12, which bypasses
2356 // the duplicate drum book dialogue in state 11, as well as the veve dialogue
2357 // trigger in the has-drum-book check.
2358 //
2359 // More notes: The veve newspaper item is inventory 9. The drum book is
2360 //             inventory 14. The flag for veve research is 36, the flag for drum
2361 //             research is 73.
2362 //
2363 // Special thanks, credits and kudos to sluicebox on IRC, who did a ton of
2364 // research on this and even found this game bug originally.
2365 //
2366 // Applies to at least: English PC-CD, German PC-CD
2367 static const uint16 gk1Day5DrumBookDialogueSignature[] = {
2368 	0x31, 0x0b,                         // bnt [skip giving player drum book code]
2369 	0x38, SIG_SELECTOR16(get),          // pushi get ($200)
2370 	0x78,                               // push1
2371 	SIG_MAGICDWORD,
2372 	0x39, 0x0e,                         // pushi $e
2373 	0x81, 0x00,                         // lag global[0]
2374 	0x4a, SIG_UINT16(0x06),             // send 6 - GKEgo::get($e)
2375 	// end of giving player drum book code
2376 	0x38, SIG_SELECTOR16(has),          // pushi has ($202)
2377 	0x78,                               // push1
2378 	0x39, 0x0e,                         // pushi $e
2379 	0x81, 0x00,                         // lag global[0]
2380 	0x4a, SIG_UINT16(0x06),             // send 6 - GKEgo::has($e)
2381 	0x18,                               // not
2382 	0x30, SIG_UINT16(0x25),             // bnt [veve newspaper code]
2383 	SIG_END
2384 };
2385 
2386 static const uint16 gk1Day5DrumBookDialoguePatch[] = {
2387 	0x31, 0x0d,                         // bnt [skip giving player drum book code] adjusted
2388 	PATCH_ADDTOOFFSET(+11),             // skip give player drum book original code
2389 	0x33, 0x0d,                         // jmp [over the check inventory for drum book code]
2390 	// check inventory for drum book
2391 	0x38, PATCH_SELECTOR16(has),        // pushi has ($202)
2392 	0x78,                               // push1
2393 	0x39, 0x0e,                         // pushi $e
2394 	0x81, 0x00,                         // lag global[0]
2395 	0x4a, PATCH_UINT16(0x0006),         // send 6 - GKEgo::has($e)
2396 	0x2f, 0x23,                         // bt [veve newspaper code] (adjusted, saves 2 bytes)
2397 	PATCH_END
2398 };
2399 
2400 // When Gabriel goes to the phone, the script softlocks at
2401 // `startOfDay5::changeState(32)`.
2402 //
2403 // Applies to at least: English PC-CD, German PC-CD, English Mac
2404 static const uint16 gk1Day5PhoneFreezeSignature[] = {
2405 	0x4a,                             // send ...
2406 	SIG_MAGICDWORD, SIG_UINT16(0x0c), // ... $c
2407 	0x35, 0x03,                       // ldi 3
2408 	0x65, SIG_ADDTOOFFSET(+1),        // aTop cycles
2409 	0x32, SIG_ADDTOOFFSET(+2),        // jmp [end]
2410 	0x3c,                             // dup
2411 	0x35, 0x21,                       // ldi $21
2412 	SIG_END
2413 };
2414 
2415 static const uint16 gk1Day5PhoneFreezePatch[] = {
2416 	PATCH_ADDTOOFFSET(+3),                     // send $c
2417 	0x35, 0x06,                                // ldi 6
2418 	0x65, PATCH_GETORIGINALBYTEADJUST(+6, +6), // aTop ticks
2419 	PATCH_END
2420 };
2421 
2422 // When Gabriel is grabbing a vine, his saying "I can't believe I'm doing
2423 // this..." is cut off. We change it so the scripts wait for the audio.
2424 //
2425 // This is not supposed to be applied to the Floppy version.
2426 //
2427 // Applies to at least: English PC-CD, German PC-CD, Spanish PC-CD
2428 // Responsible method: vineSwing::changeState(1)
2429 // Fixes bug: #9820
2430 static const uint16 gk1Day9VineSwingSignature[] = {
2431 	0x38, SIG_UINT16(0x0004),         // pushi $4
2432 	0x51, 0x17,                       // class CT
2433 	0x36,                             // push
2434 	0x39, 0x0b,                       // pushi $b
2435 	0x78,                             // push1
2436 	0x7c,                             // pushSelf
2437 	0x81, 0x00,                       // lag global[$0]
2438 	0x4a, SIG_UINT16(0x0020),         // send $20
2439 	0x38, SIG_SELECTOR16(setMotion),  // pushi setMotion
2440 	0x78,                             // push1
2441 	0x76,                             // push0
2442 	0x72, SIG_UINT16(0x0412),         // lofsa guard1
2443 	0x4a, SIG_UINT16(0x0006),         // send $6
2444 	0x38, SIG_SELECTOR16(say),        // pushi say
2445 	0x38, SIG_UINT16(0x0004),         // pushi $4
2446 	SIG_MAGICDWORD,
2447 	0x39, 0x07,                       // pushi $7
2448 	0x39, 0x08,                       // pushi $8
2449 	0x39, 0x10,                       // pushi $10
2450 	0x78,                             // push1
2451 	0x81, 0x5b,                       // lag global[$5b]
2452 	0x4a, SIG_UINT16(0x000c),         // send $c
2453 	SIG_END
2454 };
2455 
2456 static const uint16 gk1Day9VineSwingPatch[] = {
2457 	0x38, PATCH_UINT16(0x0003),         // pushi $3
2458 	0x51, 0x17,                         // class CT
2459 	0x36,                               // push
2460 	0x39, 0x0b,                         // pushi $b
2461 	0x78,                               // push1
2462 	0x81, 0x00,                         // lag global[$0]
2463 	0x4a, PATCH_UINT16(0x001e),         // send $20
2464 	0x38, PATCH_SELECTOR16(setMotion),  // pushi setMotion
2465 	0x78,                               // push1
2466 	0x76,                               // push0
2467 	0x72, PATCH_UINT16(0x0412),         // lofsa guard1
2468 	0x4a, PATCH_UINT16(0x0006),         // send $6
2469 	0x38, PATCH_SELECTOR16(say),        // pushi say
2470 	0x38, PATCH_UINT16(0x0005),         // pushi $5
2471 	0x39, 0x07,                         // pushi $7
2472 	0x39, 0x08,                         // pushi $8
2473 	0x39, 0x10,                         // pushi $10
2474 	0x78,                               // push1
2475 	0x7c,                               // pushSelf
2476 	0x81, 0x5b,                         // lag global[$5b]
2477 	0x4a, PATCH_UINT16(0x000e),         // send $c
2478 	PATCH_END
2479 };
2480 
2481 // The mummies on day 9 move without animating if ego exits to the north before
2482 //  the first one finishes standing. This also occurs in Sierra's interpreter.
2483 //
2484 // The 12 outer rooms of the African mound all take place in room 710, which
2485 //  reinitializes its contents on each room change. Each room's mummy is guard1
2486 //  repositioned with a different view. When the mummies come to life,
2487 //  keyWorks:changeState(6) starts guard1's standing animation after which
2488 //  state 7 initializes guard1 for chasing ego. Ego however can leave before
2489 //  guard1 finishes standing, preventing state 7 from occurring. The script for
2490 //  exiting to the north assumes state 7 has run, otherwise guard1 remains on
2491 //  the wrong view with no cycler or looper in subsequent rooms.
2492 //
2493 // This bug is due to the script rightWay only partially initializing guard1 for
2494 //  chasing as opposed to wrongWay and backTrack which fully initialize. We fix
2495 //  this by replacing rightWay's partial initialization with the full version
2496 //  from keyWorks state 7. There are two versions of this patch due to
2497 //  significant differences between floppy and CD versions of this script.
2498 //
2499 // This patch is not applied to the NRS versions of this script, which address
2500 //  this bug by disabling control until guard1 finishes standing, giving the
2501 //  player less time to escape.
2502 //
2503 // Applies to: All PC Floppy and CD versions. TODO: Test Mac
2504 // Responsible method: rightWay:changeState(1)
2505 // Fixes bug: #10828
2506 static const uint16 gk1MummyAnimateFloppySignature[] = {
2507 	0x39, SIG_SELECTOR8(view),          // pushi view [ full guard1 init ]
2508 	SIG_MAGICDWORD,
2509 	0x78,                               // push1
2510 	0x38, SIG_UINT16(0x02c5),           // pushi 709d
2511 	SIG_ADDTOOFFSET(+674),
2512 	0x38, SIG_SELECTOR16(setMotion),    // pushi setMotion [ partial guard1 init ]
2513 	0x38, SIG_UINT16(0x0004),           // pushi 0004
2514 	0x51, 0x70,                         // class PChase
2515 	0x36,                               // push
2516 	0x89, 0x00,                         // lsg global[0]
2517 	0x39, 0x0f,                         // pushi 0f
2518 	SIG_END
2519 };
2520 
2521 static const uint16 gk1MummyAnimateFloppyPatch[] = {
2522 	PATCH_ADDTOOFFSET(+680),
2523 	0x39, PATCH_SELECTOR8(view),        // pushi view [ waste 6 stack items to be compatible with ]
2524 	0x76,                               // push0      [  the send instruction in full guard1 init ]
2525 	0x39, PATCH_SELECTOR8(view),        // pushi view
2526 	0x76,                               // push0
2527 	0x39, PATCH_SELECTOR8(view),        // pushi view
2528 	0x39, 0x00,                         // pushi 00
2529 	0x32, PATCH_UINT16(0xfd4b),         // jmp -693d [ continue full guard1 init in keyWorks state 7 ]
2530 	PATCH_END
2531 };
2532 
2533 static const uint16 gk1MummyAnimateCDSignature[] = {
2534 	0x39, SIG_SELECTOR8(view),          // pushi view [ full guard1 init ]
2535 	SIG_MAGICDWORD,
2536 	0x78,                               // push1
2537 	0x38, SIG_UINT16(0x02c5),           // pushi 709d
2538 	SIG_ADDTOOFFSET(+750),
2539 	0x38, SIG_SELECTOR16(setMotion),    // pushi setMotion [ partial guard1 init ]
2540 	0x38, SIG_UINT16(0x0004),           // pushi 0004
2541 	0x51, 0x70,                         // class PChase
2542 	0x36,                               // push
2543 	0x89, 0x00,                         // lsg global[0]
2544 	0x39, 0x0f,                         // pushi 0f
2545 	SIG_END
2546 };
2547 
2548 static const uint16 gk1MummyAnimateCDPatch[] = {
2549 	PATCH_ADDTOOFFSET(+756),
2550 	0x39, PATCH_SELECTOR8(view),        // pushi view [ waste 6 stack items to be compatible with ]
2551 	0x76,                               // push0      [  the send instruction in full guard1 init ]
2552 	0x39, PATCH_SELECTOR8(view),        // pushi view
2553 	0x76,                               // push0
2554 	0x39, PATCH_SELECTOR8(view),        // pushi view
2555 	0x39, 0x00,                         // pushi 00
2556 	0x32, PATCH_UINT16(0xfcff),         // jmp -769d [ continue full guard1 init in keyWorks state 7 ]
2557 	PATCH_END
2558 };
2559 
2560 // In GK1, the `view` selector is used to store view numbers in some cases and
2561 // object references to Views in other cases. `Interrogation::dispose` compares
2562 // an object stored in the `view` selector with a number (which is not valid)
2563 // because its checks are in the wrong order. The check order was fixed in the
2564 // CD version, so just do what the CD version does.
2565 //
2566 // TODO: Check if English Mac is affected too and if this patch applies
2567 // Applies to at least: English Floppy
2568 static const uint16 gk1InterrogationBugSignature[] = {
2569 	SIG_MAGICDWORD,
2570 	0x65, 0x4c,                      // aTop $4c
2571 	0x67, 0x50,                      // pTos $50
2572 	0x34, SIG_UINT16(0x2710),        // ldi $2710
2573 	0x1e,                            // gt?
2574 	0x31, 0x08,                      // bnt 8 [05a0]
2575 	0x67, 0x50,                      // pTos $50
2576 	0x34, SIG_UINT16(0x2710),        // ldi $2710
2577 	0x04,                            // sub
2578 	0x65, 0x50,                      // aTop $50
2579 	0x63, 0x50,                      // pToa $50
2580 	0x31, 0x15,                      // bnt $15 [05b9]
2581 	0x39, SIG_SELECTOR8(view),       // pushi view ($e)
2582 	0x76,                            // push0
2583 	0x4a, SIG_UINT16(0x04),          // send 4
2584 	0xa5, 0x00,                      // sat temp[0]
2585 	0x38, SIG_SELECTOR16(dispose),   // pushi dispose
2586 	0x76,                            // push0
2587 	0x63, 0x50,                      // pToa $50
2588 	0x4a, SIG_UINT16(0x04),          // send 4
2589 	0x85, 0x00,                      // lat temp[0]
2590 	0x65, 0x50,                      // aTop $50
2591 	SIG_END
2592 };
2593 
2594 static const uint16 gk1InterrogationBugPatch[] = {
2595 	0x65, 0x4c,                      // aTop $4c
2596 	0x63, 0x50,                      // pToa $50
2597 	0x31, 0x15,                      // bnt $15 [05b9]
2598 	0x39, PATCH_SELECTOR8(view),     // pushi view ($e)
2599 	0x76,                            // push0
2600 	0x4a, PATCH_UINT16(0x04),        // send 4
2601 	0xa5, 0x00,                      // sat temp[0]
2602 	0x38, PATCH_SELECTOR16(dispose), // pushi dispose
2603 	0x76,                            // push0
2604 	0x63, 0x50,                      // pToa $50
2605 	0x4a, PATCH_UINT16(0x04),        // send 4
2606 	0x85, 0x00,                      // lat temp[0]
2607 	0x65, 0x50,                      // aTop $50
2608 	0x67, 0x50,                      // pTos $50
2609 	0x34, PATCH_UINT16(0x2710),      // ldi $2710
2610 	0x1e,                            // gt?
2611 	0x31, 0x08,                      // bnt 8 [05b9]
2612 	0x67, 0x50,                      // pTos $50
2613 	0x34, PATCH_UINT16(0x2710),      // ldi $2710
2614 	0x04,                            // sub
2615 	0x65, 0x50,                      // aTop $50
2616 	PATCH_END
2617 };
2618 
2619 // WORKAROUND: Script needed, because of differences in our pathfinding
2620 // algorithm.
2621 // In Madame Cazanoux's house, when Gabriel is leaving, he is placed on
2622 // the edge of the walkable area initially. This leads to a failure in
2623 // the pathfinding algorithm, and the pathfinding area is then ignored,
2624 // so Gabriel goes straight to the door by walking through the wall.
2625 // This is an edge case, which was apparently acceptable in SSCI. We
2626 // change the upper border of the walk area slightly, so that Gabriel
2627 // can be placed inside, and the pathfinding algorithm works correctly.
2628 //
2629 // Responsible method: rm280:init
2630 // Fixes bug: #9770
2631 static const uint16 gk1CazanouxPathfindingSignature[] = {
2632 	SIG_MAGICDWORD,
2633 	0x78,                            // push1 x = 1
2634 	0x38, SIG_UINT16(0x0090),        // pushi y = 144
2635 	0x38, SIG_UINT16(0x00f6),        // pushi x = 246
2636 	0x38, SIG_UINT16(0x0092),        // pushi y = 146
2637 	0x38, SIG_UINT16(0x00f2),        // pushi x = 242
2638 	0x39, 0x69,                      // pushi y = 105
2639 	0x39, 0x7c,                      // pushi x = 124
2640 	0x39, 0x68,                      // pushi y = 104
2641 	0x39, 0x56,                      // pushi x = 86
2642 	0x39, 0x6f,                      // pushi y = 111
2643 	0x39, 0x45,                      // pushi x = 69
2644 	0x39, 0x7c,                      // pushi y = 124
2645 	0x39, 0x2e,                      // pushi x = 46
2646 	0x38, SIG_UINT16(0x0081),        // pushi y = 129
2647 	SIG_END
2648 };
2649 
2650 static const uint16 gk1CazanouxPathfindingPatch[] = {
2651 	PATCH_ADDTOOFFSET(+15),
2652 	0x39, 0x7c,                      // pushi x = 124
2653 	0x39, 0x67,                      // pushi y = 103 (was 104)
2654 	PATCH_END
2655 };
2656 
2657 // GK1 english pc floppy locks up on day 10 in the honfour (room 800) when
2658 //  using the keycard on an unlocked door's keypad. This is due to mistakenly
2659 //  calling handsOff instead of handsOn. Sierra fixed this in floppy patch 1.0a
2660 //  and all other versions.
2661 //
2662 // We fix this by changing handsOff to handsOn and passing 0 as the caller
2663 //  to gkMessager:say since the script disposes itself.
2664 //
2665 // Applies to: English PC Floppy only
2666 // Responsible method: sUnlockDoor:changeState(2)
2667 // Fixes bug: #10767
2668 static const uint16 gk1HonfourUnlockDoorSignature[] = {
2669 	0x7c,                           // pushSelf
2670 	0x81, 0x5b,                     // lag global[5b]
2671 	0x4a, SIG_MAGICDWORD,           // send e [ gkMessager:say ... self ]
2672 	SIG_UINT16(0x000e),
2673 	0x38, SIG_UINT16(0x0216),       // pushi 0216 [ handsOff ]
2674 	SIG_END
2675 };
2676 
2677 static const uint16 gk1HonfourUnlockDoorPatch[] = {
2678 	0x76,                           // push0
2679 	0x81, 0x5b,                     // lag global[5b]
2680 	0x4a, PATCH_UINT16(0x000e),     // send e [ gkMessager:say ... 0 ]
2681 	0x38, PATCH_UINT16(0x0217),     // pushi 0217 [ handsOn ]
2682 	PATCH_END
2683 };
2684 
2685 // GK1 english pc floppy locks up on day 2 when using the binoculars to view
2686 //  room 410 when the artist's drawing blows away. This is particularly bad
2687 //  because when using the binoculars you can't use the mouse to access the
2688 //  control panel to restore.
2689 //
2690 // We fix this as Sierra did in later versions by not allowing the drawing to
2691 //  blow away when viewing through binoculars. To make room for this patch
2692 //  we remove initializing juggler:cycleSpeed to 6 as this is redundant.
2693 //  juggler is a Prop and Prop:cycleSpeed's initial value is 6.
2694 //
2695 // Applies to: English PC Floppy
2696 // Responsible method: neJackson:init
2697 // Fixes bug: #10797
2698 static const uint16 gk1Day2BinocularsLockupSignature[] = {
2699 	SIG_MAGICDWORD,
2700 	0x30, SIG_UINT16(0x01d6),           // bnt 01d6 [ english pc floppy 1.0 only ]
2701 	0x38, SIG_SELECTOR16(init),         // pushi init
2702 	0x76,                               // push0
2703 	0x38, SIG_SELECTOR16(cycleSpeed),   // pushi cycleSpeed
2704 	0x78,                               // push1
2705 	0x39, 0x06,                         // pushi 06
2706 	0x38, SIG_SELECTOR16(setCycle),     // pushi setCycle
2707 	0x78,                               // push1
2708 	0x51, 0x15,                         // class Fwd
2709 	0x36,                               // push
2710 	0x72, SIG_UINT16(0x02b0),           // lofsa juggler
2711 	0x4a, SIG_UINT16(0x0010),           // send 10 [ juggler: init, cycleSpeed: 6, setCycle: Fwd ]
2712 	0x38, SIG_SELECTOR16(init),         // pushi init
2713 	0x76,                               // push0
2714 	0x72, SIG_UINT16(0x0538),           // lofsa easel
2715 	0x4a, SIG_UINT16(0x0004),           // send 4 [ easel: init ]
2716 	SIG_END
2717 };
2718 
2719 static const uint16 gk1Day2BinocularsLockupPatch[] = {
2720 	PATCH_ADDTOOFFSET(+6),
2721 	0x3c,                               // dup
2722 	0x76,                               // push0
2723 	0x38, PATCH_SELECTOR16(setCycle),   // pushi setCycle
2724 	0x78,                               // push1
2725 	0x51, 0x15,                         // class Fwd
2726 	0x36,                               // push
2727 	0x72, PATCH_UINT16(0x02b0),         // lofsa juggler
2728 	0x4a, PATCH_UINT16(0x000a),         // send a [ juggler: init, setCycle Fwd ]
2729 	0x76,                               // push0
2730 	0x72, PATCH_UINT16(0x0538),         // lofsa easel
2731 	0x4a, PATCH_UINT16(0x0004),         // send 4 [ easel: init ]
2732 
2733 	0x89, 0x0c,                         // lsg global[0c] [ previous room ]
2734 	0x34, PATCH_UINT16(0x0190),         // ldi 0190 [ overlook ]
2735 	0x1c,                               // ne?
2736 	0x31, 0x09,                         // bnt 09 [ drawing doesn't blow away ]
2737 	PATCH_END
2738 };
2739 
2740 // GK1 english pc floppy has a missing-points bug on day 5 in room 240.
2741 //  Showing Mosely the veve sketch and Hartridge's notes awards 2 points
2742 //  but not if you show the notes before the veve.
2743 //  Sierra fixed this in floppy patch 1.0b and all other versions.
2744 //
2745 // We fix this by awarding 2 points when showing the veve second.
2746 //
2747 // Applies to: English PC Floppy
2748 // Responsible method: showMoselyPaper:changeState(5)
2749 // Fixes bug: #10763
2750 static const uint16 gk1Day5MoselyVevePointsSignature[] = {
2751 	0x78,                                   // push1
2752 	0x39, 0x1b,                             // pushi 1b
2753 	0x47, 0x0d, 0x00, SIG_UINT16(0x0002),   // calle [export 0 of script 13], 02 [ is flag 1b set? ]
2754 	0x30, SIG_UINT16(0x001e),               // bnt 001e [ haven't shown notes yet ]
2755 	0x78,                                   // push1
2756 	0x39, 0x1a,                             // pushi 1a
2757 	0x47, 0x0d, 0x01, SIG_UINT16(0x0002),   // calle [export 1 of script 13], 02 [ set flag 1a ]
2758 	0x38, SIG_UINT16(0x00f2),               // pushi 00f2 [ say ]
2759 	0x38, SIG_UINT16(0x0005),               // pushi 0005
2760 	0x39, 0x11,                             // pushi 11 [ noun ]
2761 	SIG_MAGICDWORD,
2762 	0x39, 0x10,                             // pushi 10 [ verb ]
2763 	0x39, 0x38,                             // pushi 38 [ cond ]
2764 	0x76,                                   // push0
2765 	0x7c,                                   // pushSelf
2766 	0x81, 0x5b,                             // lag global[5b] [ GkMessager ]
2767 	0x4a, SIG_UINT16(0x000e),               // send 000e [ GkMessager:say ]
2768 	0x32, SIG_UINT16(0x0013),               // jmp 0013
2769 	0x38, SIG_UINT16(0x00f2),               // pushi 00f2 [ say ]
2770 	SIG_END
2771 };
2772 
2773 static const uint16 gk1Day5MoselyVevePointsPatch[] = {
2774 	0x38, PATCH_UINT16(0x00f2),             // pushi 00f2 [ say ]
2775 	0x39, 0x05,                             // pushi 05
2776 	0x39, 0x11,                             // pushi 11 [ noun ]
2777 	0x39, 0x10,                             // pushi 10 [ verb ]
2778 	0x78,                                   // push1
2779 	0x39, 0x1b,                             // pushi 1b
2780 	0x47, 0x0d, 0x00, PATCH_UINT16(0x0002), // calle [export 0 of script 13], 02 [ is flag 1b set? ]
2781 	0x31, 0x20,                             // bnt 20 [ pushi 37, continue GkMessager:say ]
2782 	0x38, PATCH_UINT16(0x02fa),             // pushi 02fa [ getPoints ]
2783 	0x7a,                                   // push2
2784 	0x38, PATCH_UINT16(0xfc19),             // pushi fc19 [ no flag ]
2785 	0x7a,                                   // push2 [ 2 points ]
2786 	0x81, 0x00,                             // lag global[0]
2787 	0x4a, PATCH_UINT16(0x0008),             // send 8 [ GKEgo:getPoints -999 2 ]
2788 	0x78,                                   // push1
2789 	0x39, 0x1a,                             // pushi 1a
2790 	0x47, 0x0d, 0x01, PATCH_UINT16(0x0002), // calle [export 1 of script 13], 02 [ set flag 1a ]
2791 	0x39, 0x38,                             // pushi 38 [ cond ]
2792 	0x33, 0x09,                             // jmp 9 [ continue GkMessager:say ]
2793 	PATCH_END
2794 };
2795 
2796 // When turning on the museum's air conditioner prior to day 5 in room 260, the
2797 //  timing is off and speech is interrupted. Some parts of the sequence run at
2798 //  game speed and others don't, which at high speeds eliminates pauses between
2799 //  dialogue. Dr. John's "We have air conditioning, you see" speech is cut off
2800 //  at all speeds.
2801 //
2802 // We fix this by setting ego's speed to its default (6) during this sequence
2803 //  and waiting for the messages to complete before proceeding. flipTheSwitch
2804 //  restores ego's speed at the end of the script, even though it never sets it.
2805 //
2806 // Applies to: All CD versions
2807 // Responsible method: flipTheSwitch:changeState
2808 // Fixes bug: #11219
2809 static const uint16 gk1AirConditionerSpeechSignature[] = {
2810 	0x30, SIG_UINT16(0x0020),               // bnt 0020 [ state 1 ]
2811 	SIG_ADDTOOFFSET(+26),
2812 	0x4a, SIG_UINT16(0x000c),               // send 0c
2813 	0x32, SIG_UINT16(0x0409),               // jmp 0409 [ end of method ]
2814 	0x3c,                                   // dup
2815 	0x35, SIG_MAGICDWORD, 0x01,             // ldi 01
2816 	0x1a,                                   // eq?
2817 	0x30, SIG_UINT16(0x0056),               // bnt 0056 [ state 2 ]
2818 	SIG_ADDTOOFFSET(+24),
2819 	0x4a, SIG_UINT16(0x001a),               // send 1a [ GKEgo view: 265 ... ]
2820 	SIG_ADDTOOFFSET(+33),
2821 	0x4a, SIG_UINT16(0x000c),               // send 0c
2822 	0x32, SIG_UINT16(0x03c0),               // jmp 03c0 [ end of method ]
2823 	SIG_ADDTOOFFSET(+620),
2824 	0x7a,                                   // push2
2825 	SIG_ADDTOOFFSET(+3),
2826 	0x7c,                                   // pushSelf
2827 	0x81, 0x00,                             // lag 00
2828 	0x4a, SIG_UINT16(0x0014),               // send 14 [ GKEgo ... setCycle: End self ]
2829 	SIG_ADDTOOFFSET(+8),
2830 	0x38, SIG_UINT16(0x0004),               // pushi 0004
2831 	SIG_ADDTOOFFSET(+10),
2832 	0x4a, SIG_UINT16(0x000c),               // send 0c  [ gkMessager say: 28 8 7 4 ]
2833 	0x32, SIG_UINT16(0x012f),               // jmp 012f [ end of method ]
2834 	SIG_ADDTOOFFSET(+3),
2835 	0x38, SIG_UINT16(0x0004),               // pushi 0004
2836 	SIG_ADDTOOFFSET(+9),
2837 	0x4a, SIG_UINT16(0x000c),               // send 0c  [ gkMessager say: 28 8 8 4 ]
2838 	0x32, SIG_UINT16(0x011a),               // jmp 011a [ end of method ]
2839 	SIG_ADDTOOFFSET(+6),
2840 	0x38, SIG_SELECTOR16(stop),             // pushi stop [ stop snake sound ]
2841 	SIG_END
2842 };
2843 
2844 static const uint16 gk1AirConditionerSpeechPatch[] = {
2845 	0x30, PATCH_UINT16(0x001c),             // bnt 001c [ state 1 ]
2846 	PATCH_ADDTOOFFSET(+26),
2847 	0x33, 0x47,                             // jmp 47 [ send 0c / end of method ]
2848 	0x3c,                                   // dup
2849 	0x18,                                   // not
2850 	0x1a,                                   // eq?
2851 	0x31, 0x5c,                             // bnt 5c [ state 2 ]
2852 	0x38, PATCH_SELECTOR16(cycleSpeed),     // pushi cycleSpeed
2853 	0x78,                                   // push1
2854 	0x39, 0x06,                             // pushi 06
2855 	PATCH_ADDTOOFFSET(+24),
2856 	0x4a, PATCH_UINT16(0x0020),             // send 20 [ GKEgo cycleSpeed: 6 view: 265 ... ]
2857 	PATCH_ADDTOOFFSET(+659),
2858 	0x78,                                   // push1
2859 	PATCH_ADDTOOFFSET(+3),
2860 	0x80, PATCH_UINT16(0x0000),             // lag 0000
2861 	0x4a, PATCH_UINT16(0x0012),             // send 12 [ GKEgo ... setCycle: End ]
2862 	PATCH_ADDTOOFFSET(+8),
2863 	0x38, PATCH_UINT16(0x0005),             // pushi 0005
2864 	PATCH_ADDTOOFFSET(+10),
2865 	0x7c,                                   // pushSelf
2866 	0x4a, PATCH_UINT16(0x000e),             // send 0e [ gkMessager say: 28 8 7 4 self ]
2867 	0x33, 0x1b,                             // jmp 1b  [ stop snake sound ]
2868 	PATCH_ADDTOOFFSET(+3),
2869 	0x38, PATCH_UINT16(0x0005),             // pushi 0005
2870 	PATCH_ADDTOOFFSET(+9),
2871 	0x7c,                                   // pushSelf
2872 	0x4a, PATCH_UINT16(0x000e),             // send 0e [ gkMessager say: 28 8 8 4 self ]
2873 	0x33, 0x06,                             // jmp 06  [ stop snake sound ]
2874 	PATCH_END
2875 };
2876 
2877 // The day 5 snake attack has speed, audio, and graphics problems.
2878 //  These occur in all versions and also in Sierra's interpreter.
2879 //
2880 // Gabriel automatically walks cautiously in the darkened museum while looking
2881 //  around and saying lines, then a snake drops on him. Depending on the game's
2882 //  speed setting, the audio for "Why is it so dark in here?" is interrupted as
2883 //  much as halfway through by the next line, "Dr. John, hello?". The cautious
2884 //  walk animation runs at game speed, which can be fast, then abruptly changes
2885 //  to 10 (33%) when the snake drops, which looks off. Ego doesn't even reach
2886 //  the snake and instead stops short and warps 17 pixels to the right when the
2887 //  drop animation starts.
2888 //
2889 // We fix all of this. Initializing ego's speed to 10 solves the interrupted
2890 //  speech and inconsistent speed. It feels like this was the intended pacing.
2891 //  The snake-warping isn't a speed issue, ego's animation frames for this
2892 //  scene simply fall short of the snake's location. To fix that we start ego
2893 //  a little farther in the room and increase ego's final position so that he
2894 //  ends up directly under the snake and transitions to the drop animation
2895 //  smoothly. Finally, we initialize ego on the room's first cycle instead of
2896 //  second so that ego doesn't materialize after the room is already displayed.
2897 //
2898 // This patch works with pc floppy and cd even though they have different
2899 //  snakeAttack scripts. Floppy doesn't have speech to interrupt but it
2900 //  has the same issues.
2901 //
2902 // Applies to: All PC Floppy and CD versions. TODO: Test Mac, should apply
2903 // Responsible method: snakeAttack:changeState
2904 // Fixes bug: #10793
2905 static const uint16 gk1Day5SnakeAttackSignature1[] = {
2906 	0x65, 0x1a,                         // aTop cycles
2907 	0x32, SIG_ADDTOOFFSET(+2),          // jmp [ end of method ]
2908 	0x3c,                               // dup
2909 	0x35, 0x01,                         // ldi 1
2910 	SIG_MAGICDWORD,
2911 	0x1a,                               // eq?
2912 	0x30, SIG_UINT16(0x0048),           // bnt 0048 [ state 2 ]
2913 	0x35, 0x01,                         // ldi 1 [ free bytes ]
2914 	0x39, SIG_SELECTOR8(view),          // pushi view
2915 	0x78,                               // push1
2916 	0x38, SIG_UINT16(0x0107),           // pushi 0107
2917 	0x38, SIG_SELECTOR16(setCel),       // pushi setCel
2918 	0x78,                               // push1
2919 	0x76,                               // push0
2920 	0x38, SIG_SELECTOR16(setLoop),      // pushi setLoop
2921 	0x78,                               // push1
2922 	0x76,                               // push0
2923 	0x39, SIG_SELECTOR8(signal),        // pushi signal
2924 	0x78,                               // push1
2925 	0x39, SIG_SELECTOR8(signal),        // pushi signal
2926 	0x76,                               // push0
2927 	0x81, 0x00,                         // lag global[0]
2928 	0x4a, SIG_UINT16(0x0004),           // send 4 [ GKEgo:signal? ]
2929 	SIG_ADDTOOFFSET(+18),
2930 	0x39, 0x64,                         // pushi 64 [ initial x ]
2931 	SIG_END
2932 };
2933 
2934 static const uint16 gk1Day5SnakeAttackPatch1[] = {
2935 	0x39, PATCH_SELECTOR8(view),        // pushi view [ begin initializing ego in state 0 ]
2936 	0x78,                               // push1
2937 	0x33, 0x07,                         // jmp 07 [ continue initializing ego in state 0 ]
2938 	0x3c,                               // dup
2939 	0x18,                               // not [ acc = 1 ]
2940 	0x1a,                               // eq?
2941 	0x65, 0x1a,                         // aTop cycles [ just set cycles to 1 in state 1 ]
2942 	0x33, 0x48,                         // jmp 47 [ state 2 ]
2943 	0x38, PATCH_UINT16(0x0107),         // pushi 0107
2944 	0x39, PATCH_SELECTOR8(cel),         // pushi cel
2945 	0x78,                               // push1
2946 	0x76,                               // push0
2947 	0x38, PATCH_SELECTOR16(setLoop),    // pushi setLoop
2948 	0x78,                               // push1
2949 	0x76,                               // push0
2950 	0x39, PATCH_SELECTOR8(signal),      // pushi signal
2951 	0x78,                               // push1
2952 	0x38, PATCH_SELECTOR16(cycleSpeed), // pushi cycleSpeed
2953 	0x78,                               // push1
2954 	0x39, 0x0a,                         // pushi 0a
2955 	PATCH_ADDTOOFFSET(+5),
2956 	0x4a, PATCH_UINT16(0x000a),         // send a [ GKEgo:signal?, cycleSpeed = a ]
2957 	PATCH_ADDTOOFFSET(+18),
2958 	0x39, 0x70,                         // pushi 70 [ new initial x ]
2959 	PATCH_END
2960 };
2961 
2962 // This just changes ego's second x coordinate but unfortunately that promotes it to 16 bits
2963 static const uint16 gk1Day5SnakeAttackSignature2[] = {
2964 	SIG_MAGICDWORD,
2965 	0x39, 0x7a,                         // pushi 7a [ x for second walking loop ]
2966 	0x39, 0x7c,                         // pushi 7c
2967 	0x38, SIG_SELECTOR16(setCycle),     // pushi setCycle
2968 	0x7a,                               // push2
2969 	0x51, 0x18,                         // class End
2970 	0x36,                               // push
2971 	0x7c,                               // pushSelf
2972 	0x81, 0x00,                         // lag global[0]
2973 	0x4a, SIG_UINT16(0x0022),           // send 22
2974 	0x32, SIG_ADDTOOFFSET(+2),          // jmp [ end of method ]
2975 	SIG_END
2976 };
2977 
2978 static const uint16 gk1Day5SnakeAttackPatch2[] = {
2979 	0x38, PATCH_UINT16(0x008b),         // pushi 008b [ new x for second walking loop ]
2980 	0x39, 0x7c,                         // pushi 7c
2981 	0x38, PATCH_SELECTOR16(setCycle),   // pushi setCycle
2982 	0x7a,                               // push2
2983 	0x51, 0x18,                         // class End
2984 	0x36,                               // push
2985 	0x7c,                               // pushSelf
2986 	0x81, 0x00,                         // lag global[0]
2987 	0x4a, PATCH_UINT16(0x0022),         // send 22
2988 	0x3a,                               // toss
2989 	0x48,                               // ret
2990 	PATCH_END
2991 };
2992 
2993 // When entering the police station (room 230) sGabeEnters sets ego speed
2994 //  to 4 for the door animation but fails to restore it to the game speed
2995 //  by calling GKEgo:normalize. This leaves ego at 75% speed until doing
2996 //  something that does call normalize.
2997 //
2998 // We fix this by calling GKEgo:normalize after Gabriel finishes walking
2999 //  through the door in sGabeEnters:changeState(4). This replaces setting
3000 //  GKEgo:ignoreActors to 0 but that's okay because normalize does that.
3001 //
3002 // Applies to: All PC Floppy and CD versions. TODO: Test Mac, should apply
3003 // Responsible method: sGabeEnters:changeState(4)
3004 // Fixes bug: #10780
3005 static const uint16 gk1PoliceEgoSpeedFixSignature[] = {
3006 	0x38, SIG_MAGICDWORD,               // pushi ignoreActors
3007 	      SIG_SELECTOR16(ignoreActors),
3008 	0x78,                               // push1
3009 	0x76,                               // push0
3010 	0x81, 0x00,                         // lag global[0]
3011 	0x4a, SIG_UINT16(0x000c),           // send c [ GKEgo: ..., ignoreActors: 0 ]
3012 	SIG_END
3013 };
3014 
3015 static const uint16 gk1PoliceEgoSpeedFixPatch[] = {
3016 	0x38, PATCH_SELECTOR16(normalize),  // pushi normalize
3017 	0x39, 0x00,                         // pushi 00
3018 	0x81, 0x00,                         // lag global[0]
3019 	0x4a, PATCH_UINT16(0x000a),         // send a [ GKEgo: ..., normalize ]
3020 	PATCH_END
3021 };
3022 
3023 // When exiting the drugstore (room 250) egoExits sets ego speed to 15
3024 //  (slowest) for the door animation but fails to restore it to game
3025 //  speed by calling GKEgo:normalize. This leaves ego slow until doing
3026 //  something that does call normalize.
3027 //
3028 // We fix this by calling GKEgo:normalize after the door animation.
3029 //
3030 // Applies to: All PC Floppy and CD versions. TODO: Test Mac, should apply
3031 // Responsible method: egoExits:changeState
3032 // Fixes bug: #10780
3033 static const uint16 gk1DrugStoreEgoSpeedFixSignature[] = {
3034 	0x30, SIG_UINT16(0x003f),           // bnt 003f [ state 1 ]
3035 	SIG_ADDTOOFFSET(+60),
3036 	SIG_MAGICDWORD,
3037 	0x32, SIG_UINT16(0x0012),           // jmp 12 [ end of method ]
3038 	0x3c,                               // dup
3039 	0x35, 0x01,                         // ldi 1
3040 	0x1a,                               // eq?
3041 	0x31, 0x0c,                         // bnt c [ end of method ]
3042 	0x38, SIG_SELECTOR16(newRoom),      // pushi newRoom
3043 	0x78,                               // push1
3044 	0x38, SIG_UINT16(0x00c8),           // pushi 00c8 [ map ]
3045 	0x81, 0x02,                         // lag global[2]
3046 	0x4a, SIG_UINT16(0x0006),           // send 6 [ rm250:newRoom = map ]
3047 	0x3a,                               // toss
3048 	SIG_END
3049 };
3050 
3051 static const uint16 gk1DrugStoreEgoSpeedFixPatch[] = {
3052 	0x3a,                               // toss
3053 	0x31, 0x3d,                         // bnt 3d [ state 1 ]
3054 	PATCH_ADDTOOFFSET(+60),
3055 	0x48,                               // ret
3056 	0x38, PATCH_SELECTOR16(normalize),  // pushi normalize
3057 	0x76,                               // push0
3058 	0x81, 0x00,                         // lag global[0]
3059 	0x4a, PATCH_UINT16(0x0004),         // send 4 [ GKEgo:normalize ]
3060 	0x38, PATCH_SELECTOR16(newRoom),    // pushi newRoom
3061 	0x78,                               // push1
3062 	0x38, PATCH_UINT16(0x00c8),         // pushi 00c8 [ map ]
3063 	0x81, 0x02,                         // lag global[2]
3064 	0x4a, PATCH_UINT16(0x0006),         // send 6 [ rm250:newRoom = map ]
3065 	PATCH_END
3066 };
3067 
3068 // GK1 CD version cuts off Grace's speech when hanging up the phone on day 1.
3069 //  This is a timing issue that also occurs in the original.
3070 //
3071 // startingCartoon:changeState(12) plays Grace's final phone message but doesn't
3072 //  synchronize it with the script. Instead ego goes through a series of movements
3073 //  that advance the state while Grace is speaking. Once the sequence is complete
3074 //  Grace hangs up the phone and starts her next message which interrupts the
3075 //  previous one. There is no mechanism to make sure that Grace's message has
3076 //  first completed and so it cut offs the last one or two words. The timing only
3077 //  worked in the original on slower machines that weren't able to run the
3078 //  sequence at full speed.
3079 //
3080 // We fix this by adding a delay to startingCartoon:changeState(18) so that
3081 //  Grace's speech has time to complete. This scene occurs before game speed
3082 //  can be set and it plays at a consistent speed on ScummVM.
3083 //
3084 // This patch is only applied to CD versions. Floppies have a different script.
3085 //
3086 // Applies to: All CD versions
3087 // Responsible method: startingCartoon:changeState(18)
3088 // Fixes bug: #10787
3089 static const uint16 gk1Day1GracePhoneSignature[] = {
3090 	SIG_MAGICDWORD,
3091 	0x35, 0x12,                 // ldi 12
3092 	0x1a,                       // eq?
3093 	0x31, 0x2c,                 // bnt 2c
3094 	SIG_ADDTOOFFSET(+28),
3095 	0x38, SIG_UINT16(0x0003),   // pushi 0003
3096 	0x51, 0x69,                 // class Osc
3097 	0x36,                       // push
3098 	0x78,                       // push1
3099 	0x7c,                       // pushSelf
3100 	0x81, 0x00,                 // lag global[0]
3101 	0x4a, SIG_UINT16(0x0024),   // send 24 [ GKEgo: ... setCycle: Osc 1 self ]
3102 	0x32, SIG_ADDTOOFFSET(+2),  // jmp [ end of method ]
3103 	SIG_END
3104 };
3105 
3106 static const uint16 gk1Day1GracePhonePatch[] = {
3107 	PATCH_ADDTOOFFSET(+33),
3108 	0x7a,                       // push2
3109 	0x51, 0x69,                 // class Osc
3110 	0x36,                       // push
3111 	0x78,                       // push1
3112 	0x81, 0x00,                 // lag global[0]
3113 	0x4a, PATCH_UINT16(0x0022), // send 22 [ GKEgo: ... setCycle: Osc 1 ]
3114 
3115 	// advance to the next state in 6 seconds instead of when Gabriel finishes
3116 	//  taking a sip of coffee, which takes 2 seconds, giving Grace's speech
3117 	//  an extra 4 seconds to complete.
3118 	0x35, 0x06,                 // ldi 06
3119 	0x65, 0x1c,                 // aTop seconds
3120 
3121 	0x3a,                       // toss
3122 	0x48,                       // ret
3123 	PATCH_END
3124 };
3125 
3126 // French and Spanish CD versions contain an active debugging hotkey, ALT+N,
3127 //  which brings up a series of unskippable bug-reporting dialogs and
3128 //  eventually writes files to disk and crashes non-release builds due to
3129 //  an uninitialized read. This hotkey is always active and not hidden
3130 //  behind the game's debug mode flag so we just patch it out.
3131 //
3132 // Applies to: French and Spanish PC CD
3133 // Responsible method: GK:handleEvent
3134 // Fixes bug: #10781
3135 static const uint16 gk1SysLoggerHotKeySignature[] = {
3136 	SIG_MAGICDWORD,
3137 	0x34, SIG_UINT16(0x3100),       // ldi 3100 [ ALT+N ]
3138 	0x1a,                           // eq?
3139 	0x31,                           // bnt
3140 	SIG_END
3141 };
3142 
3143 static const uint16 gk1SysLoggerHotKeyPatch[] = {
3144 	PATCH_ADDTOOFFSET(+4),
3145 	0x33,                           // jmp
3146 	PATCH_END
3147 };
3148 
3149 // After interrogating Gran in room 380, the room is re-initialized incorrectly.
3150 //  Clicking on objects while seated causes Gabriel to briefly flicker into
3151 //  standing and other frames. After standing, the knitting basket can be walked
3152 //  through. These are script bugs which also occur in Sierra's interpreter.
3153 //
3154 // Ego is initialized incorrectly by rm380:init when returning from interrogation
3155 //  (room 50). Several properties are wrong and it's bad luck that it works as
3156 //  well as it does or Sierra would have noticed. For comparison, the scripts
3157 //  egoEnters and sitDown do it correctly. rm380:init first initializes ego for
3158 //  walking and then applies only some of the properties for sitting in the chair.
3159 //
3160 // This leaves ego in a walking/sitting state with several problems:
3161 //  - signal flag kSignalDoesntTurn isn't set
3162 //  - cycler is set to StopWalk instead of none
3163 //  - loop/cel is set to 2 0 instead of 0 5
3164 //
3165 // rm380:init sets ego's loop/cel to 0 5 (Gabriel sitting) but the unexpected
3166 //  StopWalk immediately changes this to 2 0 (Gabriel starts talking) which went
3167 //  unnoticed because those two frames are similar. This is why Gabriel's hand
3168 //  is slightly raised when returning from interrogation. The flickering is due
3169 //  to ego attempting to turn to face items while sitting due to kSignalDoesntTurn
3170 //  not being set.
3171 //
3172 // We fix the flickering by passing a second parameter to GKEgo:setLoop which
3173 //  causes kSignalDoesntTurn to be set, preventing ego from attempting to face
3174 //  objects being clicked, just as egoEnters and sitDown do. We fix the knitting
3175 //  basket by adding its obstacle polygon to the room even when returning from
3176 //  interrogation, which Sierra forgot to do.
3177 //
3178 // Applies to: All PC Floppy and CD versions. TODO: Test Mac, should apply
3179 // Responsible method: rm380:init
3180 // Fixes bug: #9760, #10707
3181 static const uint16 gk1GranRoomInitSignature[] = {
3182 	0x38, SIG_SELECTOR16(setCel),       // pushi setCel
3183 	0x78,                               // push1
3184 	0x39, 0x05,                         // pushi 05
3185 	0x38, SIG_SELECTOR16(setLoop),      // pushi setLoop
3186 	0x78,                               // push1
3187 	0x76,                               // push0 [ loop: 0 ]
3188 	0x38, SIG_SELECTOR16(init),         // pushi init
3189 	0x76,                               // push0
3190 	0x38, SIG_SELECTOR16(posn),         // pushi posn
3191 	SIG_MAGICDWORD,
3192 	0x7a,                               // push2
3193 	0x38, SIG_UINT16(0x00af),           // pushi 00af
3194 	0x39, 0x75,                         // pushi 75
3195 	0x81, 0x00,                         // lag global[0]
3196 	0x4a, SIG_UINT16(0x001e),           // send 1e [ GKEgo: ... setCel: 5, setLoop: 0 ... ]
3197 	0x35, 0x01,                         // ldi 1
3198 	0xa3, 0x00,                         // sal local[0] [ 1, a non-zero value indicates ego is sitting ]
3199 	SIG_END
3200 };
3201 
3202 static const uint16 gk1GranRoomInitPatch[] = {
3203 	0x39, PATCH_SELECTOR8(cel),         // pushi cel [ use cel instead of equivalent setCel to save a byte ]
3204 	0x78,                               // push1
3205 	0x39, 0x05,                         // pushi 05
3206 	0x38, PATCH_SELECTOR16(setLoop),    // pushi setLoop
3207 	0x7a,                               // push2
3208 	0x76,                               // push0 [ loop: 0 ]
3209 	0x78,                               // push1 [ 2nd param tells setLoop to set kSignalDoesntTurn ]
3210 	0x38, PATCH_SELECTOR16(init),       // pushi init
3211 	0x76,                               // push0
3212 	0x38, PATCH_SELECTOR16(posn),       // pushi posn
3213 	0x7a,                               // push2
3214 	0x38, PATCH_UINT16(0x00af),         // pushi 00af
3215 	0x39, 0x75,                         // pushi 75
3216 	0x81, 0x00,                         // lag global[0]
3217 	0xa3, 0x00,                         // sal local[0] [ setting a non-zero object instead of 1 saves 2 bytes ]
3218 	0x4a, PATCH_UINT16(0x0020),         // send 20 [ GKEgo: ... cel: 5, setLoop: 0 1 ... ]
3219 	0x33, 0x87,                         // jmp -79 [ add knitting basket obstacle to room ]
3220 	PATCH_END
3221 };
3222 
3223 // After phoning Wolfgang on day 7, Gabriel is placed beyond room 220's obstacle
3224 //  boundary and can walk through walls and behind the room. This also occurs in
3225 //  the original. The script inconsistently uses accessible and inaccessible
3226 //  positions for placing ego next to the phone. We patch all instances to use
3227 //  the accessible position.
3228 //
3229 // Applies to: All PC Floppy and CD versions. TODO: Test Mac, should apply
3230 // Responsible methods: rm220:init, useThePhone:changeState(0)
3231 // Fixes bug: #10853
3232 static const uint16 gk1EgoPhonePositionSignature[] = {
3233 	SIG_MAGICDWORD,
3234 	0x39, 0x68,                         // pushi 68 [ x: 104 ]
3235 	0x39, 0x7e,                         // pushi 7e [ y: 126 ]
3236 	SIG_END
3237 };
3238 
3239 static const uint16 gk1EgoPhonePositionPatch[] = {
3240 	0x39, 0x6b,                         // pushi 6b [ x: 107 ]
3241 	0x39, 0x7c,                         // pushi 7c [ y: 124 ]
3242 	PATCH_END
3243 };
3244 
3245 // Restarting the game doesn't reset the current inventory item in the icon bar.
3246 //  The previously selected item can then be used on day 1.
3247 //
3248 // Room 93 restarts the game and resets inventory by setting each item's owner
3249 //  to zero. It makes no attempt to reset the icon bar. We fix this by instead
3250 //  calling GKEgo:put on each item and passing zero for the new owner, as this
3251 //  handles updating the icon bar when dropping an item. The "state" property is
3252 //  no longer cleared for items but that's okay because it's never set or used.
3253 //
3254 // Applies to: All versions
3255 // Responsible method: doTheRestart:changeState(0)
3256 // Fixes bug: #11222
3257 static const uint16 gk1RestartInventorySignature[] = {
3258 	SIG_MAGICDWORD,
3259 	0x38, SIG_SELECTOR16(owner),            // pushi owner
3260 	0x78,                                   // push1
3261 	0x76,                                   // push0
3262 	0x39, SIG_SELECTOR8(state),             // pushi state
3263 	0x78,                                   // push1
3264 	0x76,                                   // push0
3265 	0x39, SIG_SELECTOR8(at),                // pushi at
3266 	0x78,                                   // push1
3267 	0x8b, 0x00,                             // lsl 00
3268 	0x81, 0x09,                             // lag 09
3269 	0x4a, SIG_UINT16(0x0006),               // send 06 [ GKInventory at: local0 ]
3270 	0x4a, SIG_UINT16(0x000c),               // send 0c [ item owner: 0 state: 0 ]
3271 	SIG_END
3272 };
3273 
3274 static const uint16 gk1RestartInventoryPatch[] = {
3275 	0x38, PATCH_SELECTOR16(put),            // pushi put
3276 	0x7a,                                   // push2
3277 	0x8b, 0x00,                             // lsl 00
3278 	0x76,                                   // push0
3279 	0x81, 0x00,                             // lag 00
3280 	0x4a, PATCH_UINT16(0x0008),             // send 08 [ GKEgo put: local0 0 ]
3281 	0x33, 0x08,                             // jmp 08
3282 	PATCH_END
3283 };
3284 
3285 //          script, description,                                      signature                         patch
3286 static const SciScriptPatcherEntry gk1Signatures[] = {
3287 	{  true,     0, "remove alt+n syslogger hotkey",               1, gk1SysLoggerHotKeySignature,      gk1SysLoggerHotKeyPatch },
3288 	{  true,    51, "fix interrogation bug",                       1, gk1InterrogationBugSignature,     gk1InterrogationBugPatch },
3289 	{  true,    93, "fix inventory on restart",                    1, gk1RestartInventorySignature,     gk1RestartInventoryPatch },
3290 	{  true,   211, "fix day 1 grace phone speech timing",         1, gk1Day1GracePhoneSignature,       gk1Day1GracePhonePatch },
3291 	{  true,   212, "fix day 5 drum book dialogue error",          1, gk1Day5DrumBookDialogueSignature, gk1Day5DrumBookDialoguePatch },
3292 	{  true,   212, "fix day 5 phone softlock",                    1, gk1Day5PhoneFreezeSignature,      gk1Day5PhoneFreezePatch },
3293 	{  true,   220, "fix ego phone position",                      2, gk1EgoPhonePositionSignature,     gk1EgoPhonePositionPatch },
3294 	{  true,   230, "fix day 6 police beignet timer issue (1/2)",  1, gk1Day6PoliceBeignetSignature1,   gk1Day6PoliceBeignetPatch1 },
3295 	{  true,   230, "fix day 6 police beignet timer issue (2/2)",  1, gk1Day6PoliceBeignetSignature2,   gk1Day6PoliceBeignetPatch2 },
3296 	{  true,   230, "fix day 6 police sleep timer issue",          1, gk1Day6PoliceSleepSignature,      gk1Day6PoliceSleepPatch },
3297 	{  true,   230, "fix police station ego speed",                1, gk1PoliceEgoSpeedFixSignature,    gk1PoliceEgoSpeedFixPatch },
3298 	{  true,   240, "fix day 5 mosely veve missing points",        1, gk1Day5MoselyVevePointsSignature, gk1Day5MoselyVevePointsPatch },
3299 	{  true,   250, "fix ego speed when exiting drug store",       1, gk1DrugStoreEgoSpeedFixSignature, gk1DrugStoreEgoSpeedFixPatch },
3300 	{  true,   260, "fix air conditioner speech timing",           1, gk1AirConditionerSpeechSignature, gk1AirConditionerSpeechPatch },
3301 	{  true,   260, "fix day 5 snake attack (1/2)",                1, gk1Day5SnakeAttackSignature1,     gk1Day5SnakeAttackPatch1 },
3302 	{  true,   260, "fix day 5 snake attack (2/2)",                1, gk1Day5SnakeAttackSignature2,     gk1Day5SnakeAttackPatch2 },
3303 	{  true,   280, "fix pathfinding in Madame Cazanoux's house",  1, gk1CazanouxPathfindingSignature,  gk1CazanouxPathfindingPatch },
3304 	{  true,   380, "fix Gran's room obstacles and ego flicker",   1, gk1GranRoomInitSignature,         gk1GranRoomInitPatch },
3305 	{  true,   410, "fix day 2 binoculars lockup",                 1, gk1Day2BinocularsLockupSignature, gk1Day2BinocularsLockupPatch },
3306 	{  true,   710, "fix day 9 vine swing speech playing",         1, gk1Day9VineSwingSignature,        gk1Day9VineSwingPatch },
3307 	{  true,   710, "fix day 9 mummy animation (floppy)",          1, gk1MummyAnimateFloppySignature,   gk1MummyAnimateFloppyPatch },
3308 	{  true,   710, "fix day 9 mummy animation (cd)",              1, gk1MummyAnimateCDSignature,       gk1MummyAnimateCDPatch },
3309 	{  true,   800, "fix day 10 honfour unlock door lockup",       1, gk1HonfourUnlockDoorSignature,    gk1HonfourUnlockDoorPatch },
3310 	{  true, 64908, "disable video benchmarking",                  1, sci2BenchmarkSignature,           sci2BenchmarkPatch },
3311 	{  true, 64990, "increase number of save games (1/2)",         1, sci2NumSavesSignature1,           sci2NumSavesPatch1 },
3312 	{  true, 64990, "increase number of save games (2/2)",         1, sci2NumSavesSignature2,           sci2NumSavesPatch2 },
3313 	{  true, 64990, "disable change directory button",             1, sci2ChangeDirSignature,           sci2ChangeDirPatch },
3314 	SCI_SIGNATUREENTRY_TERMINATOR
3315 };
3316 
3317 #pragma mark -
3318 #pragma mark Gabriel Knight 2
3319 
3320 // GK2's inventory scrolls smoothly when the mouse is held down in the original
3321 //  due to an inner loop in ScrollButton:track, but this causes slow scrolling
3322 //  in our interpreter since we throttle kFrameOut. The script's inner loop is
3323 //  itself throttled by ScrollButton:moveDelay, which is set to 25 and limits
3324 //  event processing to every 25th iteration. Removing this delay results in
3325 //  smooth scrolling as in the original.
3326 //
3327 // Applies to: All versions
3328 // Responsible method: ScrollButton:track
3329 static const uint16 gk2InventoryScrollSpeedSignature[] = {
3330 	SIG_MAGICDWORD,
3331 	0x63, 0x9c,                 // pToa moveDelay [ 25 ]
3332 	0xa5, 0x02,                 // sat 02
3333 	SIG_END
3334 };
3335 
3336 static const uint16 gk2InventoryScrollSpeedPatch[] = {
3337 	0x35, 0x01,                 // ldi 01
3338 	PATCH_END
3339 };
3340 
3341 // The down scroll button in GK2 jumps up a pixel on mousedown because there is
3342 //  a send to scrollSelections using an immediate value 1, which means to scroll
3343 //  up by 1 pixel. This patch fixes the send to scrollSelections by passing the
3344 //  button's delta instead of 1. The Italian version's vocab.997 is missing the
3345 //  scrollSelections selector so this patch avoids referencing it. Two versions
3346 //  are necessary to accomodate scripts compiled with and without line numbers.
3347 //
3348 // Applies to: All versions
3349 // Responsible method: ScrollButon:track
3350 // Fixes bug: #9648
3351 static const uint16 gk2InventoryScrollDirSignature1[] = {
3352 	SIG_MAGICDWORD,
3353 	0x78,                               // push1
3354 	0x63, 0x98,                         // pToa client
3355 	0x4a, SIG_UINT16(0x0006),           // send 06 [ client scrollSelections: 1 ]
3356 	0x7e,                               // line
3357 	SIG_END
3358 };
3359 
3360 static const uint16 gk2InventoryScrollDirPatch1[] = {
3361 	0x66, PATCH_UINT16(0x009a),         // pTos delta
3362 	0x62, PATCH_UINT16(0x0098),         // pToa client
3363 	0x4a, PATCH_UINT16(0x0006),         // send 06 [ client scrollSelections: delta ]
3364 	PATCH_END
3365 };
3366 
3367 static const uint16 gk2InventoryScrollDirSignature2[] = {
3368 	0x78,                               // push1
3369 	0x63, 0x98,                         // pToa client
3370 	0x4a, SIG_MAGICDWORD,               // send 06 [ client scrollSelections: 1 ]
3371 	      SIG_UINT16(0x0006),
3372 	0x35, 0x02,                         // ldi 02
3373 	0x65, 0x56,                         // aTop cel
3374 	SIG_END
3375 };
3376 
3377 static const uint16 gk2InventoryScrollDirPatch2[] = {
3378 	0x67, 0x9a,                         // pTos delta
3379 	0x63, 0x98,                         // pToa client
3380 	0x4a, PATCH_UINT16(0x0006),         // send 06 [ client scrollSelections: delta ]
3381 	0x7a,                               // push2
3382 	0x69, 0x56,                         // sTop cel
3383 	PATCH_END
3384 };
3385 
3386 // The init code 'GK2::init' that runs when GK2 starts up unconditionally resets
3387 // the music volume to 63, but the game should always use the volume stored in
3388 // ScummVM.
3389 // Applies to: All versions
3390 // Fixes bug: #9700
3391 static const uint16 gk2VolumeResetSignature[] = {
3392 	SIG_MAGICDWORD,
3393 	0x35, 0x3f, // ldi $3f
3394 	0xa1, 0x4c, // sag global[$4c] (music volume)
3395 	SIG_END
3396 };
3397 
3398 static const uint16 gk2VolumeResetPatch[] = {
3399 	0x33, 0x02,  // jmp 2 [past volume changes]
3400 	PATCH_END
3401 };
3402 
3403 // GK2 has custom video benchmarking code that needs to be disabled in a local
3404 //  procedure called from GK2:init. It sets the game's detailLevel and returns
3405 //  a value which is assigned to GK2:speedRating and never used. The maximum
3406 //  detailLevel the game recognizes is six so we just set it to that.
3407 //
3408 // Applies to: All versions
3409 // Responsible method: GK2:init
3410 static const uint16 gk2BenchmarkSignature[] = {
3411 	0x76,                       // push0
3412 	0x40, SIG_ADDTOOFFSET(+2),  // call speed test proc
3413 	      SIG_MAGICDWORD,
3414 		  SIG_UINT16(0x0000),
3415 	0x65, 0x28,                 // aTop speedRating
3416 	SIG_END
3417 };
3418 
3419 static const uint16 gk2BenchmarkPatch[] = {
3420 	0x35, 0x06,                 // ldi 06
3421 	0x65, 0x18,                 // aTop _detailLevel
3422 	0x33, 0x02,                 // jmp 02
3423 	PATCH_END
3424 };
3425 
3426 // GK2 has a complex sound bug which causes seemingly random lockups when
3427 //  changing rooms in many areas including the Herrenchiemse Museum, the Hunt
3428 //  Club, and St. Georg Church. This also occurs in the original.
3429 //
3430 // SoundManager continuously plays an array of sounds provided to its play
3431 //  method. Sounds play in a random order with a random delay of five to ten
3432 //  seconds in between. SoundManager is attached to soundRegion and survives
3433 //  room changes. Rooms that set a new playlist call play on initialization.
3434 //  The problem is that SoundManager:play doesn't clear its delay timer. If play
3435 //  is called during a delay then the timer continues and expires during the
3436 //  next sound. This is noticeable throughout the game when background music is
3437 //  randomly interrupted by different music. Many room scripts change rooms by
3438 //  calling SoundManager:fade in handsOff mode and proceeding once they've been
3439 //  cued. If a stray SoundManager timer expires while a script is waiting for
3440 //  fade to complete then SoundManager:cue will play the next sound, overwrite
3441 //  gk2Music:client with itself, and the waiting script will never cue.
3442 //
3443 // We fix this by clearing SoundManager's timer state in SoundManager:play.
3444 //  This prevents the delay timer from ever running while music is playing.
3445 //
3446 // Applies to: All versions
3447 // Responsible method: SoundManager:play
3448 static const uint16 gk2SoundManagerLockupSignature1[] = {
3449 	0x7e, SIG_ADDTOOFFSET(+2),          // line
3450 	0x7e, SIG_ADDTOOFFSET(+2),          // line
3451 	SIG_MAGICDWORD,
3452 	0x35, 0x00,                         // ldi 00
3453 	0x65, 0x34,                         // aTop cleanup
3454 	SIG_END
3455 };
3456 
3457 static const uint16 gk2SoundManagerLockupPatch1[] = {
3458 	0x35, 0x00,                         // ldi 00
3459 	0x64, PATCH_UINT16(0x001e),         // aTop seconds
3460 	0x64, PATCH_UINT16(0x0012),         // aTop scratch
3461 	PATCH_END
3462 };
3463 
3464 static const uint16 gk2SoundManagerLockupSignature2[] = {
3465 	0x87, SIG_MAGICDWORD, 0x00,         // lap 00
3466 	0x18,                               // not
3467 	0x31, 0x10,                         // bnt 10 [ skip debug message ]
3468 	0x78,                               // push1
3469 	0x72,                               // lofsa "WARNING: 0 args passed to SoundManager!"
3470 	SIG_END
3471 };
3472 
3473 static const uint16 gk2SoundManagerLockupPatch2[] = {
3474 	0x35, 0x00,                         // ldi 00
3475 	0x65, 0x1e,                         // aTop seconds
3476 	0x65, 0x12,                         // aTop scratch
3477 	0x32, PATCH_UINT16(0x0014),         // jmp 0014 [ skip debug message ]
3478 	PATCH_END
3479 };
3480 
3481 // Clicking on Frau Miller in room 810 after exhausting her topics and then
3482 //  clicking on anything else can lockup or crash the game. rm810:newRoom fades
3483 //  the music before transitioning to room 8110, which takes several seconds.
3484 //  The game doesn't disable input during this period and if the player begins
3485 //  another action then rm810:cue can unexpectedly interrupt it. If Grace is
3486 //  walking then the room will reload in a handsOff state. Other edge cases
3487 //  include setting the room number to zero and subsequently crashing.
3488 //
3489 // We fix this by calling handsOff so that the player can't interrupt the Frau
3490 //  Miller room transition while waiting for the music to fade, which is
3491 //  consistent with the exit to the map.
3492 //
3493 // Applies to: All versions
3494 // Responsible method: rm810:newRoom
3495 static const uint16 gk2FrauMillerLockupSignature[] = {
3496 	SIG_MAGICDWORD,
3497 	0x39, 0x03,                         // pushi 03
3498 	0x8f, 0x01,                         // lsp 01
3499 	0x38, SIG_UINT16(0x1fae),           // pushi 1fae
3500 	0x38, SIG_UINT16(0x0320),           // pushi 0320
3501 	0x46, SIG_UINT16(0xfde7),           // calle proc64999_5 [ OneOf newRoomNumber 8110 800 ]
3502 	      SIG_UINT16(0x0005),
3503 	      SIG_UINT16(0x0006),
3504 	0x31,                               // bnt [ don't fade music ]
3505 	SIG_END
3506 };
3507 
3508 static const uint16 gk2FrauMillerLockupPatch[] = {
3509 	0x8f, 0x01,                         // lsp 01
3510 	0x34, PATCH_UINT16(0x1fae),         // ldi 1fae
3511 	0x24,                               // le? [ newRoomNumber <= 8110 ]
3512 	0x31, PATCH_GETORIGINALBYTEADJUST(+18, +11), // bnt [ don't fade music ]
3513 	0x38, PATCH_SELECTOR16(handsOff),   // pushi handsOff
3514 	0x39, 0x00,                         // pushi 00
3515 	0x80, PATCH_UINT16(0x0001),         // lag 0001
3516 	0x4a, PATCH_UINT16(0x0004),         // send 04 [ GK2 handsOff: ]
3517 	PATCH_END
3518 };
3519 
3520 // GK2 1.0 contains a deadend bug in chapter 3. Exhausting Leber's topics before
3521 //  reading Grace's letter prevents returning to the police station to ask about
3522 //  the Black Wolf, which is necessary to complete the chapter.
3523 //
3524 // We fix this as Sierra did by adding a flag 218 test so that the police
3525 //  station doesn't close before Leber has been asked about the Black Wolf.
3526 //
3527 // Applies to: English PC 1.0
3528 // Responsible method: rm3210:dispose
3529 static const uint16 gk2PoliceStationDeadendSignature[] = {
3530 	0x78,                               // push1
3531 	0x38, SIG_UINT16(0x00dd),           // pushi 00dd
3532 	0x47, 0x0b, 0x00, SIG_MAGICDWORD,   // calle proc11_0 [ is flag 221 set? ]
3533 	      SIG_UINT16(0x002),
3534 	0x31, 0x51,                         // bnt 51 [ skip closing police station ]
3535 	SIG_END
3536 };
3537 
3538 static const uint16 gk2PoliceStationDeadendPatch[] = {
3539 	0x80, PATCH_UINT16(0x00a3),         // lag 00a3 [ flags 208-223 ]
3540 	0x39, 0x24,                         // pushi 24
3541 	0x12,                               // and
3542 	0x39, 0x24,                         // pushi 24
3543 	0x1a,                               // eq? [ are flags 218 and 221 set? ]
3544 	PATCH_END
3545 };
3546 
3547 // In chapter 3, Xaver can be asked about the Black Wolf before learning about
3548 //  the Black Wolf from Grace's letter. The tBlackWolf topic in room 4320 is
3549 //  missing the readyFlagNum value of 514 that the other tBlackWolf topics in
3550 //  chapter 3 have, so we set it.
3551 //
3552 // Applies to: All versions
3553 // Responsible method: heap in script 4320
3554 static const uint16 gk2XaverBlackWolfSignature[] = {
3555 	SIG_MAGICDWORD,             // tBlackWolf
3556 	SIG_UINT16(0x010e),         // sceneNum = 270
3557 	SIG_UINT16(0x00f0),         // flagNum = 240
3558 	SIG_UINT16(0x0000),         // readyFlagNum = 0
3559 	SIG_END
3560 };
3561 
3562 static const uint16 gk2XaverBlackWolfkPatch[] = {
3563 	PATCH_ADDTOOFFSET(+4),
3564 	PATCH_UINT16(0x0202),       // readyFlagNum = 514
3565 	PATCH_END
3566 };
3567 
3568 // Chapter 4 has a bug in many versions of GK2 that is effectively a deadend.
3569 //  Asking Georg about "Ludwig's letter to the Conductor" is required to finish
3570 //  the chapter. This topic becomes available after looking at the "Ludwig and
3571 //  Wagner" plaque in Herrenchiemsee and then clicking again to read it aloud.
3572 //  The problem is that the rest of the game only cares about looking at the
3573 //  plaque. If Herrenchiemsee is completed without reading the plaque then the
3574 //  Hint feature claims everything is done and the player appears to be stuck.
3575 //
3576 // We fix this as Sierra did by making Georg's letter topic available upon just
3577 //  looking at the plaque, which is consistent with the rest of the scripts.
3578 //  Although Sierra advertised this fix in the readme for GK2PAT 1.11, the patch
3579 //  file seems to be missing, but appears in later versions and the GOG release.
3580 //
3581 // Applies to: English PC 1.0, 1.1, 1.11 Patch, Mac
3582 // Responsible method: Heap in script 8520
3583 static const uint16 gk2GeorgLetterTopicSignature[] = {
3584 	SIG_MAGICDWORD,             // tLtr2Conductor
3585 	SIG_UINT16(0x0211),         // sceneNum = 529
3586 	SIG_UINT16(0x012a),         // flagNum = 298
3587 	SIG_UINT16(0x0283),         // readyFlagNum = 643
3588 	SIG_END
3589 };
3590 
3591 static const uint16 gk2GeorgLetterTopicPatch[] = {
3592 	PATCH_ADDTOOFFSET(+4),
3593 	PATCH_UINT16(0x026f),       // readyFlagNum = 623
3594 	PATCH_END
3595 };
3596 
3597 // In early versions of GK2, clicking on the holy water basket after using the
3598 //  holy water locks up the game. The script is supposed to test the flag that's
3599 //  set when getting the water but some code mistakenly tests inventory instead.
3600 //  We fix this as Sierra did by replacing the inventory tests with flag tests.
3601 //
3602 // Applies to: English PC 1.0, Mac
3603 // Responsible methods: waterBasket:handleEvent, waterBasket:doVerb
3604 static const uint16 gk2HolyWaterLockupSignature[] = {
3605 	SIG_MAGICDWORD,
3606 	0x38, SIG_SELECTOR16(has),  // pushi has
3607 	0x78,                       // push1
3608 	0x39, 0x3e,                 // pushi 3e
3609 	0x81, 0x00,                 // lag 00
3610 	0x4a, SIG_UINT16(0x0006),   // send 06 [ GraceEgo has: 62 (invBottleOfWater) ]
3611 	SIG_END
3612 };
3613 
3614 static const uint16 gk2HolyWaterLockupPatch[] = {
3615 	0x38, PATCH_UINT16(0x0001), // pushi 0001
3616 	0x38, PATCH_UINT16(0x0476), // pushi 0476
3617 	0x47, 0x0b, 0x00,           // calle proc11_0 [ is flag 1142 set? ]
3618 	      PATCH_UINT16(0x0002),
3619 	PATCH_END
3620 };
3621 
3622 // In early versions of GK2, Neuschwanstein castle can flash when clicking the
3623 //  Hint button even after completing everything. The Hint script tests too many
3624 //  flags including one whose value is random since it toggles back and forth
3625 //  between two tape messages. We remove these flag tests as Sierra did.
3626 //
3627 // Applies to: English PC 1.0, Mac
3628 // Responsible method: local procedure #0 in script 800
3629 static const uint16 gk2NeuschwansteinHintSignature1[] = {
3630 	SIG_MAGICDWORD,
3631 	0x78,                       // push1
3632 	0x38, SIG_UINT16(0x024d),   // pushi 024d
3633 	0x47, 0x0b, 0x00,           // calle proc11_0 [ is flag 589 set? ]
3634 	SIG_UINT16(0x0002),
3635 	SIG_END
3636 };
3637 
3638 static const uint16 gk2NeuschwansteinHintSignature2[] = {
3639 	SIG_MAGICDWORD,
3640 	0x78,                       // push1
3641 	0x38, SIG_UINT16(0x024e),   // pushi 024e
3642 	0x47, 0x0b, 0x00,           // calle proc11_0 [ is flag 590 set? ]
3643 	SIG_UINT16(0x0002),
3644 	SIG_END
3645 };
3646 
3647 static const uint16 gk2NeuschwansteinHintSignature3[] = {
3648 	SIG_MAGICDWORD,
3649 	0x78,                       // push1
3650 	0x38, SIG_UINT16(0x0250),   // pushi 0250
3651 	0x47, 0x0b, 0x00,           // calle proc11_0 [ is flag 592 set? ]
3652 	SIG_UINT16(0x0002),
3653 	SIG_END
3654 };
3655 
3656 static const uint16 gk2NeuschwansteinHintPatch[] = {
3657 	0x35, 0x01,                 // ldi 01
3658 	0x33, 0x05,                 // jmp 05
3659 	PATCH_END
3660 };
3661 
3662 // Clicking an inventory item on the Wagner paintings in rooms 8616 and 8617
3663 //  causes a missing message error. The paintings only have responses for the
3664 //  "Do" verb but painting:doVerb passes the incoming verb to gk2Messager:say
3665 //  without any filtering. We fix this by always playing the "Do" message.
3666 //
3667 // Applies to: All versions
3668 // Responsible methods: painting:doVerb in scripts 8616 and 8617
3669 static const uint16 gk2WagnerPaintingMessageSignature[] = {
3670 	SIG_MAGICDWORD,
3671 	0x38, SIG_SELECTOR16(say),  // pushi say
3672 	0x38, SIG_UINT16(0x0006),   // pushi 0006
3673 	0x67, SIG_ADDTOOFFSET(+1),  // pTos noun
3674 	0x8f, 0x01,                 // lsp 01 [ verb ]
3675 	SIG_END
3676 };
3677 
3678 static const uint16 gk2WagnerPaintingMessagePatch[] = {
3679 	PATCH_ADDTOOFFSET(+8),
3680 	0x39, 0x3e,                 // pushi 3e [ "Do" verb ]
3681 	PATCH_END
3682 };
3683 
3684 // The game-over rooms 665 and 666 draw a pic over everything by setting the
3685 //  default plane's priority to 202, but this is already inventoryBorderPlane's
3686 //  priority. In our interpreter this causes a border fragment to be drawn above
3687 //  the pics. This worked by luck in Sierra's interpreter because it sorts on
3688 //  memory ID when planes have the same priority. In ScummVM the renderer
3689 //  guarantees a sort order based on the creation order of the planes. The
3690 //  default plane is created first and drawn before inventoryBorderPlane.
3691 //
3692 // We fix this by increasing the plane priority in the game-over rooms.
3693 //
3694 // Applies to: All versions
3695 // Responsible methods: gabeNews:init, uDie:init
3696 // Fixes bug: #11298
3697 static const uint16 gk2GameOverPrioritySignature[] = {
3698 	0x39, SIG_SELECTOR8(priority),  // pushi priority
3699 	SIG_MAGICDWORD,
3700 	0x78,                           // push1
3701 	0x38, SIG_UINT16(0x00ca),       // pushi 00ca
3702 	0x81, 0x03,                     // lag 03
3703 	0x4a, SIG_UINT16(0x0012),       // send 12 [ Plane ... priority: 202 ]
3704 	SIG_END
3705 };
3706 
3707 static const uint16 gk2GameOverPriorityPatch[] = {
3708 	PATCH_ADDTOOFFSET(+3),
3709 	0x38, PATCH_UINT16(0x00cb),     // pushi 00cb [ priority: 203 ]
3710 	PATCH_END
3711 };
3712 
3713 // GK2 fans have created patches that add subtitles to the entire game. There
3714 //  are at least English and Spanish patch sets. Sierra added the subtitle
3715 //  feature solely for the Portuguese version. The fan patches include these
3716 //  subtitle scripts, replace the Portuguese resources and embedded script
3717 //  strings, and configure Sierra's interpreter to use the Portuguese language
3718 //  through RESOURCE.CFG. This sets GK2:printLang which the scripts test for
3719 //  Portuguese in order to activate subtitles.
3720 //
3721 // The subtitle patches are compatible with ScummVM except for the requirement
3722 //  that GK2:printLang equals Portuguese (351) since we don't use RESOURCE.CFG.
3723 //  We fix this by patching the GK2:printLang tests to always activate subtitles
3724 //  when a sync resource is present for synchronizing text to video playback.
3725 //
3726 // Applies to: PC versions with a subtitle fan-patch applied
3727 // Responsible methods: Any that test GK2:printLang for Portuguese
3728 // Fixes bugs: #9677, #11282
3729 static const uint16 gk2SubtitleCompatibilitySignature[] = {
3730 	SIG_MAGICDWORD,
3731 	0x39, SIG_SELECTOR8(printLang), // pushi printLang
3732 	0x76,                           // push0
3733 	0x81, 0x01,                     // lag 01
3734 	0x4a, SIG_UINT16(0x0004),       // send 04 [ GK2 printLang? ]
3735 	SIG_END
3736 };
3737 
3738 static const uint16 gk2SubtitleCompatibilityPatch[] = {
3739 	0x34, PATCH_UINT16(0x015f),     // ldi 015f [ K_LANG_PORTUGUESE ]
3740 	0x33, 0x03,                     // jmp 03
3741 	PATCH_END
3742 };
3743 
3744 //          script, description,                                              signature                         patch
3745 static const SciScriptPatcherEntry gk2Signatures[] = {
3746 	{  true,     0, "disable volume reset on startup",                     1, gk2VolumeResetSignature,           gk2VolumeResetPatch },
3747 	{  true,     0, "disable video benchmarking",                          1, gk2BenchmarkSignature,             gk2BenchmarkPatch },
3748 	{  true,    23, "fix inventory scroll speed",                          2, gk2InventoryScrollSpeedSignature,  gk2InventoryScrollSpeedPatch },
3749 	{  true,    23, "fix inventory scroll direction",                      1, gk2InventoryScrollDirSignature1,   gk2InventoryScrollDirPatch1 },
3750 	{  true,    23, "fix inventory scroll direction (no line numbers)",    1, gk2InventoryScrollDirSignature2,   gk2InventoryScrollDirPatch2 },
3751 	{  true,    37, "fix sound manager lockup",                            1, gk2SoundManagerLockupSignature1,   gk2SoundManagerLockupPatch1 },
3752 	{  true,    37, "fix sound manager lockup (no line numbers)",          1, gk2SoundManagerLockupSignature2,   gk2SoundManagerLockupPatch2 },
3753 	{  true,   665, "fix game-over priority",                              1, gk2GameOverPrioritySignature,      gk2GameOverPriorityPatch },
3754 	{  true,   666, "fix game-over priority",                              1, gk2GameOverPrioritySignature,      gk2GameOverPriorityPatch },
3755 	{  true,   800, "fix neuschwanstein hint (1/3)",                       1, gk2NeuschwansteinHintSignature1,   gk2NeuschwansteinHintPatch },
3756 	{  true,   800, "fix neuschwanstein hint (2/3)",                       1, gk2NeuschwansteinHintSignature2,   gk2NeuschwansteinHintPatch },
3757 	{  true,   800, "fix neuschwanstein hint (3/3)",                       1, gk2NeuschwansteinHintSignature3,   gk2NeuschwansteinHintPatch },
3758 	{  true,   810, "fix frau miller lockup",                              1, gk2FrauMillerLockupSignature,      gk2FrauMillerLockupPatch },
3759 	{  true,  1020, "fix holy water lockup",                               2, gk2HolyWaterLockupSignature,       gk2HolyWaterLockupPatch },
3760 	{  true,  3210, "fix police station deadend",                          1, gk2PoliceStationDeadendSignature,  gk2PoliceStationDeadendPatch },
3761 	{  true,  4320, "fix xaver black wolf topic",                          1, gk2XaverBlackWolfSignature,        gk2XaverBlackWolfkPatch },
3762 	{  true,  8520, "fix georg letter topic",                              1, gk2GeorgLetterTopicSignature,      gk2GeorgLetterTopicPatch },
3763 	{  true,  8616, "fix wagner painting message",                         2, gk2WagnerPaintingMessageSignature, gk2WagnerPaintingMessagePatch },
3764 	{  true,  8617, "fix wagner painting message",                         2, gk2WagnerPaintingMessageSignature, gk2WagnerPaintingMessagePatch },
3765 	{  true, 64990, "increase number of save games (1/2)",                 1, sci2NumSavesSignature1,            sci2NumSavesPatch1 },
3766 	{  true, 64990, "increase number of save games (2/2)",                 1, sci2NumSavesSignature2,            sci2NumSavesPatch2 },
3767 	{  true, 64990, "disable change directory button",                     1, sci2ChangeDirSignature,            sci2ChangeDirPatch },
3768 	{ false,     0, "subtitle patch compatibility",                        3, gk2SubtitleCompatibilitySignature, gk2SubtitleCompatibilityPatch },
3769 	{ false,    11, "subtitle patch compatibility",                        7, gk2SubtitleCompatibilitySignature, gk2SubtitleCompatibilityPatch },
3770 	{ false,    12, "subtitle patch compatibility",                        5, gk2SubtitleCompatibilitySignature, gk2SubtitleCompatibilityPatch },
3771 	{ false,    91, "subtitle patch compatibility",                        7, gk2SubtitleCompatibilitySignature, gk2SubtitleCompatibilityPatch },
3772 	{ false,   200, "subtitle patch compatibility",                        1, gk2SubtitleCompatibilitySignature, gk2SubtitleCompatibilityPatch },
3773 	{ false,  1300, "subtitle patch compatibility",                        1, gk2SubtitleCompatibilitySignature, gk2SubtitleCompatibilityPatch },
3774 	{ false, 64924, "subtitle patch compatibility",                        1, gk2SubtitleCompatibilitySignature, gk2SubtitleCompatibilityPatch },
3775 	SCI_SIGNATUREENTRY_TERMINATOR
3776 };
3777 
3778 #endif
3779 
3780 // When spotting the destroyer, timing problems prevent completing the bridge
3781 //  scene at fast game speeds.
3782 //
3783 // In the control room, room 25, ego and the captain go to the bridge, room 28,
3784 //  where they spot ships in an effectively automatic scene. When this completes
3785 //  they return to the control room, the captain falls, and the player regains
3786 //  control of ego and has to walk to the control panel. This entire sequence
3787 //  has to be completed within 400 game cycles or the destroyer kills the sub,
3788 //  but the bridge timing is in wall time and has at least 25 seconds of delays,
3789 //  which at faster speeds is longer than 400 cycles. The bridge also animates
3790 //  during messages, causing the timer to run while reading, so even at slower
3791 //  speeds the game can illogically end before the ships are revealed.
3792 //
3793 // There are several problems here but the real bug is that the timer starts
3794 //  before the player has control. We fix this by disabling the timer during the
3795 //  bridge and resetting it to 120 game cycles when the player regains control.
3796 //  This preserves the original timer duration in the control room, where the
3797 //  real timed action is, and is compatible with existing saved games. When the
3798 //  timer expires, subMarineScript:changeState(9) no longer ends the game if
3799 //  subMarine:roomFlags flag 2 isn't set, which captainfallsScript sets at the
3800 //  same time that it now calls subMarineScript:changeState(8).
3801 //
3802 // Applies to: All versions
3803 // Responsible methods: subMarineScript:changeState, captainfallsScript:changeState
3804 // Fixes bug #11017
3805 static const uint16 icemanDestroyerTimer1Signature[] = {
3806 	0x30, SIG_UINT16(0x0022),           // bnt 0022 [ state 8 ]
3807 	SIG_ADDTOOFFSET(+0x1f),
3808 	SIG_MAGICDWORD,
3809 	0x32, SIG_UINT16(0x0074),           // jmp 0074 [ end of method ]
3810 	0x3c,                               // dup
3811 	0x35, 0x08,                         // ldi 08
3812 	0x1a,                               // eq?
3813 	0x30, SIG_UINT16(0x0008),           // bnt 0008 [ state 9 ]
3814 	0x34, SIG_UINT16(0x0190),           // ldi 0190
3815 	0x65, 0x10,                         // aTop cycles [ cycles = 400 ]
3816 	0x32, SIG_UINT16(0x0065),           // jmp 0065 [ end of method ]
3817 	0x3c,                               // dup
3818 	0x35, 0x09,                         // ldi 09
3819 	0x1a,                               // eq?
3820 	0x30, SIG_UINT16(0x0023),           // bnt 0023 [ state 15 ]
3821 	0x8f, 0x00,                         // lsp 00
3822 	0x35, 0x02,                         // ldi 02
3823 	0x22,                               // lt?      [ didn't reach control panel? ]
3824 	0x30, SIG_UINT16(0x0014),           // bnt 0014 [ skip death if reached control panel ]
3825 	SIG_END
3826 };
3827 
3828 static const uint16 icemanDestroyerTimer1Patch[] = {
3829 	0x30, PATCH_UINT16(0x001f),         // bnt 001f [ state 8 ]
3830 	PATCH_ADDTOOFFSET(+0x1f),
3831 	0x3c,                               // dup
3832 	0x35, 0x08,                         // ldi 08
3833 	0x1a,                               // eq?
3834 	0x31, 0x04,                         // bnt 04 [ state 9 ]
3835 	0x35, 0x78,                         // ldi 78
3836 	0x65, 0x10,                         // aTop cycles [ cycles = 120 ]
3837 	0x3c,                               // dup
3838 	0x35, 0x09,                         // ldi 09
3839 	0x1a,                               // eq?
3840 	0x31, 0x2c,                         // bnt 2c [ state 15 ]
3841 	0x38, PATCH_SELECTOR16(roomFlags),  // pushi roomFlags
3842 	0x76,                               // push0
3843 	0x63, 0x08,                         // pToa client
3844 	0x4a, 0x04,                         // send 04 [ subMarine roomFlags? ]
3845 	0x7a,                               // push2  [ flag 2 set when captain falls ]
3846 	0x12,                               // and    [ has captain fallen? ]
3847 	0x31, 0x19,                         // bnt 19 [ skip death if captain hasn't fallen ]
3848 	0x8f, 0x00,                         // lsp 00
3849 	0x22,                               // lt?    [ didn't reach control panel? ]
3850 	0x31, 0x14,                         // bnt 14 [ skip death if reached control panel ]
3851 	PATCH_END
3852 };
3853 
3854 static const uint16 icemanDestroyerTimer2Signature[] = {
3855 	// print four messages
3856 	0x7a,                               // push2
3857 	0x38, SIG_UINT16(0x0187),           // pushi 0187
3858 	SIG_MAGICDWORD,
3859 	0x7a,                               // push2
3860 	0x47, 0xff, 0x00, 0x04,             // calle proc255_0 [ print 391 2 ]
3861 	SIG_ADDTOOFFSET(+20),               // [ print 391 3, print 391 4 ]
3862 	0x7a,                               // push2
3863 	0x38, SIG_UINT16(0x0187),           // pushi 0187
3864 	0x39, 0x05,                         // pushi 05
3865 	0x47, 0xff, 0x00, 0x04,             // calle proc255_0 [ print 391 5 ]
3866 	SIG_END
3867 };
3868 
3869 static const uint16 icemanDestroyerTimer2Patch[] = {
3870 	// print four messages using a loop
3871 	0x35, 0x02,                         // ldi 02
3872 	0xa7, 0x01,                         // sap 01
3873 	0x8f, 0x01,                         // lsp 01
3874 	0x35, 0x05,                         // ldi 05
3875 	0x24,                               // le?    [ loop while 2 <= param1 <= 5 ]
3876 	0x31, 0x0e,                         // bnt 0e [ exit loop ]
3877 	0x7a,                               // push2
3878 	0x38, PATCH_UINT16(0x0187),         // pushi 0187
3879 	0x8f, 0x01,                         // lsp 01
3880 	0x47, 0xff, 0x00, 0x04,             // calle proc255_0 [ print 391 param1 ]
3881 	0xcf, 0x01,                         // +sp 01 [ increment and push param1 ]
3882 	0x33, 0xed,                         // jmp ed [ continue loop ]
3883 	// reset subMarineScript timer
3884 	0x39, PATCH_SELECTOR8(script),      // pushi script
3885 	0x76,                               // push0
3886 	0x51, 0x5c,                         // class subMarine
3887 	0x4a, 0x04,                         // send 04 [ subMarine script? ]
3888 	0x39, PATCH_SELECTOR8(changeState), // pushi changeState
3889 	0x78,                               // push1
3890 	0x39, 0x08,                         // pushi 08
3891 	0x4a, 0x06,                         // send 06 [ subMarineScript changeState: 8 ]
3892 	PATCH_END
3893 };
3894 
3895 // At the pier in Honolulu, room 23, "climb down" causes ego to bypass boarding
3896 //  procedure, walk through the air, climb down the hatch, and get stuck in the
3897 //  submarine without triggering a room change. There is no "climb up" command.
3898 //
3899 // Boarding requires asking the officer permission. comeAboardScript gives him
3900 //  the orders, runs downTheHatchScript, and changes to room 31 when finished.
3901 //  downTheHatchScript only walks ego to the hatch and runs the climb animation.
3902 //  "climb down" simply runs downTheHatchScript and nothing else, leaving the
3903 //  room in a broken state by running this intermediate script out of context.
3904 //
3905 // We patch "climb down" to respond with the message for other hatch commands.
3906 //
3907 // Applies to: All versions
3908 // Responsible method: hatch:handleEvent
3909 // Fixes bug #11039
3910 static const uint16 icemanClimbDownHatchSignature[] = {
3911 	0x7a,                               // push2
3912 	SIG_MAGICDWORD,
3913 	0x39, 0x17,                         // pushi 17
3914 	0x39, 0x18,                         // pushi 18
3915 	0x47, 0xff, 0x00, 0x04,             // calle proc255_0 04 [ "You must follow proper boarding procedure." ]
3916 	0x32, SIG_UINT16(0x0021),           // jmp 0021 [ end of method ]
3917 	SIG_ADDTOOFFSET(+22),
3918 	0x39, SIG_SELECTOR8(setScript),     // pushi setScript
3919 	0x78,                               // push1
3920 	0x72, SIG_UINT16(0xfc24),           // lofsa downTheHatchScript
3921 	SIG_END
3922 };
3923 
3924 static const uint16 icemanClimbDownHatchPatch[] = {
3925 	PATCH_ADDTOOFFSET(+34),
3926 	0x33, 0xdc,                         // jmp dc [ "You must follow proper boarding procedure." ]
3927 	PATCH_END
3928 };
3929 
3930 //         script, description,                                       signature                                      patch
3931 static const SciScriptPatcherEntry icemanSignatures[] = {
3932 	{ true,    23, "climb down hatch",                             1, icemanClimbDownHatchSignature,                 icemanClimbDownHatchPatch },
3933 	{ true,   314, "destroyer timer (1/2)",                        1, icemanDestroyerTimer1Signature,                icemanDestroyerTimer1Patch },
3934 	{ true,   391, "destroyer timer (2/2)",                        1, icemanDestroyerTimer2Signature,                icemanDestroyerTimer2Patch },
3935 	SCI_SIGNATUREENTRY_TERMINATOR
3936 };
3937 
3938 // ===========================================================================
3939 // At least during the harpy scene, export 29 of script 0 is called and has an
3940 //  issue where temp[3] won't get inititialized, but is later used to set
3941 //  master volume. This makes SSCI set the volume to max. We fix the procedure,
3942 //  so volume won't get modified in those cases.
3943 //
3944 // Applies to at least: PC CD
3945 // Responsible method: export 29 in script 0
3946 // Fixes bug: #5209
3947 static const uint16 kq5SignatureCdHarpyVolume[] = {
3948 	SIG_MAGICDWORD,
3949 	0x80, SIG_UINT16(0x0191),        // lag global[191h]
3950 	0x18,                            // not
3951 	0x30, SIG_UINT16(0x002c),        // bnt [jump further] (jumping, if global[191h] is 1)
3952 	0x35, 0x01,                      // ldi 01
3953 	0xa0, SIG_UINT16(0x0191),        // sag global[191h] (setting to 1)
3954 	0x38, SIG_UINT16(0x017b),        // pushi 017b
3955 	0x76,                            // push0
3956 	0x81, 0x01,                      // lag global[1]
3957 	0x4a, 0x04,                      // send 04 - read KQ5::masterVolume
3958 	0xa5, 0x03,                      // sat temp[3] (store volume)
3959 	0x38, SIG_UINT16(0x017b),        // pushi 017b
3960 	0x76,                            // push0
3961 	0x81, 0x01,                      // lag global[1]
3962 	0x4a, 0x04,                      // send 04 - read KQ5::masterVolume
3963 	0x36,                            // push
3964 	0x35, 0x04,                      // ldi 04
3965 	0x20,                            // ge? (followed by bnt)
3966 	SIG_END
3967 };
3968 
3969 static const uint16 kq5PatchCdHarpyVolume[] = {
3970 	0x38, PATCH_UINT16(0x022f),      // pushi 022f (selector theVol) (3 new bytes)
3971 	0x76,                            // push0 (1 new byte)
3972 	0x51, 0x88,                      // class SpeakTimer (2 new bytes)
3973 	0x4a, 0x04,                      // send 04 (2 new bytes) -> read SpeakTimer::theVol
3974 	0xa5, 0x03,                      // sat temp[3] (2 new bytes)
3975 	0x80, PATCH_UINT16(0x0191),      // lag global[191h]
3976 	// saving 1 byte due optimization
3977 	0x2e, PATCH_UINT16(0x0023),      // bt [jump further] (jumping, if global[191h] is 1)
3978 	0x35, 0x01,                      // ldi 01
3979 	0xa0, PATCH_UINT16(0x0191),      // sag global[191h] (setting to 1)
3980 	0x38, PATCH_UINT16(0x017b),      // pushi 017b
3981 	0x76,                            // push0
3982 	0x81, 0x01,                      // lag global[1]
3983 	0x4a, 0x04,                      // send 04 - read KQ5::masterVolume
3984 	0xa5, 0x03,                      // sat temp[3] (store volume)
3985 	// saving 8 bytes due removing of duplicate code
3986 	0x39, 0x04,                      // pushi 04 (saving 1 byte due swapping)
3987 	0x22,                            // lt? (because we switched values)
3988 	PATCH_END
3989 };
3990 
3991 // The witchCage object in script 200 is broken and claims to have 12
3992 // properties instead of the 8 it should have because it is a Cage.
3993 // Additionally its top,left,bottom,right properties are set to 0 rather
3994 // than the right values. We fix the object by setting the right values.
3995 // If they are all zero, this causes an impossible position check in
3996 // witch::cantBeHere and an infinite loop when entering room 22.
3997 //
3998 // This bug is accidentally not triggered in SSCI because the invalid number
3999 // of variables effectively hides witchCage::doit, causing this position check
4000 // to be bypassed entirely.
4001 // See also the warning+comment in Object::initBaseObject
4002 //
4003 // Applies to at least: PC CD, English PC floppy
4004 // Responsible method: heap in script 200
4005 // Fixes bug: #4964
4006 static const uint16 kq5SignatureWitchCageInit[] = {
4007 	SIG_UINT16(0x0000),         // top
4008 	SIG_UINT16(0x0000),         // left
4009 	SIG_UINT16(0x0000),         // bottom
4010 	SIG_UINT16(0x0000),         // right
4011 	SIG_UINT16(0x0000),         // extra property #1
4012 	SIG_MAGICDWORD,
4013 	SIG_UINT16(0x007a),         // extra property #2
4014 	SIG_UINT16(0x00c8),         // extra property #3
4015 	SIG_UINT16(0x00a3),         // extra property #4
4016 	SIG_END
4017 };
4018 
4019 static const uint16 kq5PatchWitchCageInit[] = {
4020 	PATCH_UINT16(0x0000),       // top
4021 	PATCH_UINT16(0x007a),       // left
4022 	PATCH_UINT16(0x00c8),       // bottom
4023 	PATCH_UINT16(0x00a3),       // right
4024 	PATCH_END
4025 };
4026 
4027 // The multilingual releases of KQ5 hang right at the end during the magic
4028 // battle with Mordack. It seems additional code was added to wait for signals,
4029 // but the signals are never set and thus the game hangs. We disable that code,
4030 // so that the battle works again. This also happened in the original
4031 // interpreter. We must not change similar code, that happens before.
4032 //
4033 // Applies to at least: French PC floppy, German PC floppy, Spanish PC floppy
4034 // Responsible method: stingScript::changeState, dragonScript::changeState, snakeScript::changeState
4035 static const uint16 kq5SignatureMultilingualEndingGlitch[] = {
4036 	SIG_MAGICDWORD,
4037 	0x89, 0x57,                      // lsg global[57h]
4038 	0x35, 0x00,                      // ldi 0
4039 	0x1a,                            // eq?
4040 	0x18,                            // not
4041 	0x30, SIG_UINT16(0x0011),        // bnt [skip signal check]
4042 	SIG_ADDTOOFFSET(+8),             // skip globalSound::prevSignal get code
4043 	0x36,                            // push
4044 	0x35, 0x0a,                      // ldi 0Ah
4045 	SIG_END
4046 };
4047 
4048 static const uint16 kq5PatchMultilingualEndingGlitch[] = {
4049 	PATCH_ADDTOOFFSET(+6),
4050 	0x32,                            // jmp (replace bnt)
4051 	PATCH_END
4052 };
4053 
4054 // In the final battle, the DOS version uses signals in the music to handle
4055 // timing, while in the Windows version another method is used and the GM
4056 // tracks do not contain these signals. The original kq5 interpreter used
4057 // global[400] to distinguish between Windows (1) and DOS (0) versions.
4058 //
4059 // We replace the global[400] checks with a fixed true. This is toggled with
4060 // enablePatch() below when alternative Windows GM MIDI tracks are used.
4061 //
4062 // Instead, we could have set global[400], but this has the possibly unwanted
4063 // side effects of switching to black&white cursors (which also needs complex
4064 // changes to GameFeatures::detectsetCursorType()) and breaking savegame
4065 // compatibilty between the DOS and Windows CD versions of KQ5.
4066 //
4067 // TODO: Investigate those side effects more closely.
4068 // Applies to at least: Win CD
4069 // Responsible method: mordOneScript::changeState(1), dragonScript::changeState(1),
4070 //                     fireScript::changeState() in script 124
4071 // Fixes bug: #6251
4072 static const uint16 kq5SignatureWinGMSignals[] = {
4073 	SIG_MAGICDWORD,
4074 	0x80, SIG_UINT16(0x0190),        // lag global[400]
4075 	0x18,                            // not
4076 	0x30, SIG_UINT16(0x001b),        // bnt 0x001b
4077 	0x89, 0x57,                      // lsg global[87]
4078 	SIG_END
4079 };
4080 
4081 static const uint16 kq5PatchWinGMSignals[] = {
4082 	0x34, PATCH_UINT16(0x0001),      // ldi 0x0001
4083 	PATCH_END
4084 };
4085 
4086 //          script, description,                                      signature                  patch
4087 static const SciScriptPatcherEntry kq5Signatures[] = {
4088 	{  true,     0, "CD: harpy volume change",                     1, kq5SignatureCdHarpyVolume,            kq5PatchCdHarpyVolume },
4089 	{  true,   124, "Multilingual: Ending glitching out",          3, kq5SignatureMultilingualEndingGlitch, kq5PatchMultilingualEndingGlitch },
4090 	{ false,   124, "Win: GM Music signal checks",                 4, kq5SignatureWinGMSignals,             kq5PatchWinGMSignals },
4091 	{  true,   200, "CD: witch cage init",                         1, kq5SignatureWitchCageInit,            kq5PatchWitchCageInit },
4092 	SCI_SIGNATUREENTRY_TERMINATOR
4093 };
4094 
4095 // ===========================================================================
4096 // In the garden (room 480), when giving the milk bottle to one of the babies,
4097 // script 481 starts a looping a baby cry sound (cryMusic). However, that
4098 // particular sound has an overriden check() method that explicitly restarts
4099 // the sound, even if it's set to be looped. Thus the same sound is played
4100 // twice, squelching all other sounds.
4101 //
4102 // We just rip out the unnecessary check() method, thereby stopping the sound
4103 // from constantly restarting (since it's being looped anyway), thus the normal
4104 // game speech can work while the baby cry sound is heard.
4105 //
4106 // Applies to at least: PC-CD
4107 // Responsible method: cryMusic::check in script 481
4108 // Fixes bug: #4955
4109 static const uint16 kq6SignatureDuplicateBabyCry[] = {
4110 	SIG_MAGICDWORD,
4111 	0x83, 0x00,                      // lal local[0]
4112 	0x31, 0x1e,                      // bnt 1e [07f4]
4113 	0x78,                            // push1
4114 	0x39, 0x04,                      // pushi 04
4115 	0x43, 0x75, 0x02,                // callk DoAudio[75], 02
4116 	SIG_END
4117 };
4118 
4119 static const uint16 kq6PatchDuplicateBabyCry[] = {
4120 	0x48,                            // ret
4121 	PATCH_END
4122 };
4123 
4124 // The inventory of King's Quest 6 is buggy. When it grows too large,
4125 //  it will get split into 2 pages. Switching between those pages will
4126 //  grow the stack, because it's calling itself per switch.
4127 // Which means after a while ScummVM will bomb out because the stack frame
4128 //  will be too large. This patch fixes the buggy script.
4129 //
4130 // Applies to at least: PC-CD, English PC floppy, German PC floppy, English Mac
4131 // Responsible method: KqInv::showSelf in script 907
4132 // Fixes bug: #5681
4133 static const uint16 kq6SignatureInventoryStackFix[] = {
4134 	0x67, 0x30,                         // pTos state
4135 	0x34, SIG_UINT16(0x2000),           // ldi 2000
4136 	0x12,                               // and
4137 	0x18,                               // not
4138 	0x31, 0x04,                         // bnt [not first refresh]
4139 	0x35, 0x00,                         // ldi 00
4140 	SIG_MAGICDWORD,
4141 	0x65, 0x1e,                         // aTop curIcon
4142 	0x67, 0x30,                         // pTos state
4143 	0x34, SIG_UINT16(0xdfff),           // ldi dfff
4144 	0x12,                               // and
4145 	0x65, 0x30,                         // aTop state
4146 	0x38, SIG_SELECTOR16(show),         // pushi show (e1h for KQ6CD)
4147 	0x78,                               // push1
4148 	0x87, 0x00,                         // lap param[0]
4149 	0x31, 0x04,                         // bnt [use global for show]
4150 	0x87, 0x01,                         // lap param[1]
4151 	0x33, 0x02,                         // jmp [use param for show]
4152 	0x81, 0x00,                         // lag global[0]
4153 	0x36,                               // push
4154 	0x54, 0x06,                         // self 06 (KqInv::show)
4155 	0x31, SIG_ADDTOOFFSET(+1),          // bnt [exit menu code] (0x08 for PC, 0x07 for mac)
4156 	0x39, 0x39,                         // pushi 39
4157 	0x76,                               // push0
4158 	0x54, 0x04,                         // self 04 (KqInv::doit)
4159 	SIG_END                             // followed by jmp (0x32 for PC, 0x33 for mac)
4160 };
4161 
4162 static const uint16 kq6PatchInventoryStackFix[] = {
4163 	0x67, 0x30,                         // pTos state
4164 	0x3c,                               // dup (1 more byte, needed for patch)
4165 	0x3c,                               // dup (1 more byte, saves 1 byte later)
4166 	0x34, PATCH_UINT16(0x2000),         // ldi 2000
4167 	0x12,                               // and
4168 	0x2f, 0x02,                         // bt [not first refresh] - saves 3 bytes in total
4169 	0x65, 0x1e,                         // aTop curIcon
4170 	0x00,                               // bnot (neg, either 2000 or 0000 in acc, this will create dfff or ffff) - saves 2 bytes
4171 	0x12,                               // and
4172 	0x65, 0x30,                         // aTop state
4173 	0x38, PATCH_GETORIGINALUINT16(+22), // pushi show
4174 	0x78,                               // push1
4175 	0x87, 0x00,                         // lap param[0]
4176 	0x31, 0x04,                         // bnt [call show using global[0]]
4177 	0x8f, 0x01,                         // lsp param[1], save 1 byte total with lsg global[0] combined
4178 	0x33, 0x02,                         // jmp [call show using param[1]]
4179 	0x89, 0x00,                         // lsg global[0], save 1 byte total, see above
4180 	0x54, 0x06,                         // self 06 (call x::show)
4181 	0x31, PATCH_GETORIGINALBYTEADJUST(+39, +6), // bnt [menu exit code] (0x0e for pc, 0x0d for mac)
4182 	0x34, PATCH_UINT16(0x2000),         // ldi 2000
4183 	0x12,                               // and
4184 	0x2f, 0x05,                         // bt [to ret]
4185 	0x39, 0x39,                         // pushi 39
4186 	0x76,                               // push0
4187 	0x54, 0x04,                         // self 04 (self::doit)
4188 	0x48,                               // ret (saves 2 bytes for PC, 1 byte for mac)
4189 	PATCH_END
4190 };
4191 
4192 // The "Drink Me" bottle code doesn't repaint the AddToPics elements to the
4193 //  screen, when Alexander returns back from the effect of the bottle.
4194 //  It's pretty strange that Sierra didn't find this bug, because it occurs
4195 //  when drinking the bottle right on the screen, where the bottle is found.
4196 // This bug also occurs in Sierra SCI.
4197 //
4198 // Applies to at least: PC-CD, English PC floppy, German PC floppy, English Mac
4199 // Responsible method: drinkMeScript::changeState in script 87
4200 // Fixes bug: #5252
4201 static const uint16 kq6SignatureDrinkMeFix[] = {
4202 	SIG_MAGICDWORD,
4203 	0x3c,                               // dup
4204 	0x35, 0x0f,                         // ldi 0f
4205 	0x1a,                               // eq?
4206 	0x30, SIG_UINT16(0x00a4),           // bnt [skip to next check]
4207 	SIG_ADDTOOFFSET(+161),
4208 	0x32, SIG_UINT16(0x007f),           // jmp [return]
4209 	0x3c,                               // dup
4210 	0x35, 0x10,                         // ldi 10
4211 	0x1a,                               // eq?
4212 	0x31, 0x07,                         // bnt [skip to next check]
4213 	0x35, 0x03,                         // ldi 03
4214 	0x65, 0x1a,                         // aTop (cycles)
4215 	0x32, SIG_UINT16(0x0072),           // jmp [return]
4216 	0x3c,                               // dup
4217 	0x35, 0x11,                         // ldi 11
4218 	0x1a,                               // eq?
4219 	0x31, 0x13,                         // bnt [skip to next check]
4220 	SIG_ADDTOOFFSET(+20),
4221 	0x35, 0x12,                         // ldi 12
4222 	SIG_ADDTOOFFSET(+23),
4223 	0x35, 0x13,                         // ldi 13
4224 	SIG_END
4225 };
4226 
4227 static const uint16 kq6PatchDrinkMeFix[] = {
4228 	PATCH_ADDTOOFFSET(+4),
4229 	0x30, PATCH_UINT16(0x00b1),         // bnt 00b1 [ check for 11h code ]
4230 	PATCH_ADDTOOFFSET(+161),
4231 	0x39, PATCH_SELECTOR8(doit),        // pushi doit
4232 	0x76,                               // push0
4233 	0x81, 0x0a,                         // lag global[0a]
4234 	0x4a, 0x04,                         // send 04 (call addToPics::doit)
4235 	0x3a,                               // toss
4236 	0x48,                               // ret
4237 	PATCH_ADDTOOFFSET(+8),              // skip to check 11h code
4238 	0x35, 0x10,                         // ldi 10 (instead of 11)
4239 	PATCH_ADDTOOFFSET(+23),             // skip to check 12h code
4240 	0x35, 0x11,                         // ldi 11 (instead of 12)
4241 	PATCH_ADDTOOFFSET(+23),             // skip to check 13h code
4242 	0x35, 0x12,                         // ldi 12 (instead of 13)
4243 	PATCH_END
4244 };
4245 
4246 // KQ6 Mac is missing pic 981 and crashes when drinking the "Drink Me" bottle.
4247 //  This also crashed the original. Pic 981 is a black background and it's only
4248 //  used in this scene. Pic 98 is also a black background and used when the game
4249 //  starts and everywhere else. We restore this scene by switching to pic 98.
4250 //
4251 // This patch is only enabled on Mac as the script is the same in all versions.
4252 //  The pics have different palettes and applying it to PC would disable the
4253 //  palette cycling between red and black during this scene. KQ6 Mac didn't do
4254 //  these palette cycling effects as it didn't include any palette resources.
4255 //
4256 // Applies to: English Mac
4257 // Responsible method: drinkMeScript:changeState(0)
4258 static const uint16 kq6SignatureMacDrinkMePic[] = {
4259 	SIG_MAGICDWORD,
4260 	0x38, SIG_UINT16(0x03d5),           // pushi 981d
4261 	0x39, 0x09,                         // pushi 09
4262 	0x43, 0x08, 0x04,                   // callk DrawPic 04
4263 	SIG_END
4264 };
4265 
4266 static const uint16 kq6PatchMacDrinkMePic[] = {
4267 	0x38, PATCH_UINT16(0x0062),         // pushi 98d
4268 	PATCH_END
4269 };
4270 
4271 // During the common Game Over cutscene, one of the guys says "Tickets, only",
4272 //  but the subtitle says "Tickets, please". Normally people wouldn't have
4273 //  noticed, but ScummVM supports audio + subtitles in this game at the same
4274 //  time. This is caused by a buggy message, which really has this text + audio
4275 //  attached.
4276 // We assume that "Tickets, only" (the audio) is the correct one. There is
4277 //  another message with "Tickets, only" in both text and audio. We change
4278 //  message 1, 0, 1, 1 to message 5, 0, 0, 2 to fix this issue.
4279 // This mismatch also occurs in Sierra SCI.
4280 //
4281 // Applies to at least: PC-CD
4282 // Responsible method: modeLessScript::changeState(0) in script 640
4283 static const uint16 kq6SignatureTicketsOnly[] = {
4284 	0x3c,                               // dup
4285 	0x35, 0x00,                         // ldi 0
4286 	0x1a,                               // eq?
4287 	SIG_MAGICDWORD,
4288 	0x31, 0x2b,                         // bnt [skip over state 0]
4289 	0x39, 0x1e,                         // pushi font (we keep the hardcoded selectors in here simply because this is only for KQ6-CD)
4290 	0x78,                               // push1
4291 	0x89, 0x16,                         // lsg global[16h]
4292 	0x38, SIG_UINT16(0x009a),           // pushi posn
4293 	0x7a,                               // push2
4294 	0x38, SIG_UINT16(0x00c8),           // pushi 00c8h (200d)
4295 	0x39, 0x64,                         // pushi 64h (100d)
4296 	0x38, SIG_UINT16(0x00ab),           // pushi say
4297 	0x39, 0x05,                         // pushi 05 (parameter count for say)
4298 	0x76,                               // push0
4299 	0x78,                               // push1
4300 	0x76,                               // push0
4301 	0x78,                               // push1
4302 	0x78,                               // push1
4303 	SIG_END
4304 };
4305 
4306 static const uint16 kq6PatchTicketsOnly[] = {
4307 	0x32, PATCH_UINT16(0x0000),         // jmp (waste 3 bytes)
4308 	0x2f, 0x2c,                         // bt [skip over state 0] (saves 1 byte)
4309 	0x39, 0x1e,                         // pushi font (we keep the hardcoded selectors in here simply because this is only for KQ6-CD)
4310 	0x78,                               // push1
4311 	0x89, 0x16,                         // lsg global[16h]
4312 	0x38, PATCH_UINT16(0x009a),         // pushi posn
4313 	0x7a,                               // push2
4314 	0x38, PATCH_UINT16(0x00c8),         // pushi 00c8h (200d)
4315 	0x39, 0x64,                         // pushi 64h (100d)
4316 	0x38, PATCH_UINT16(0x00ab),         // pushi say
4317 	0x39, 0x05,                         // pushi 05 (parameter count for say)
4318 	0x76,                               // push0
4319 	0x39, 0x05,                         // pushi 05
4320 	0x76,                               // push0
4321 	0x76,                               // push0
4322 	0x7a,                               // push2
4323 	PATCH_END
4324 };
4325 
4326 // Looking at the ribbon in inventory says that there's a hair even after it's
4327 //  been removed. This occurs after the hair has been put in the skull or is on
4328 //  a different inventory page than the ribbon.
4329 //
4330 // The ribbon's Look handler has incorrect logic for determining if it contains
4331 //  a hair. It fails to test flag 143 which is set when getting the hair and so
4332 //  it displays the wrong message. The Do handler tests all the necessary flags.
4333 //  This bug probably would have been noticed except that both verb handlers
4334 //  also test inventory for hair, which is redundant as testing flags is enough,
4335 //  but it causes the right message some of the time. Testing inventory is wrong
4336 //  because possessing the hair is temporary, which is why the bug emerges after
4337 //  it's used, and it's broken because testing inventory across pages doesn't
4338 //  work in KQ6. ego:has returns false for any item on another page when the
4339 //  inventory window is open. As inventory increases the ribbon and hair end up
4340 //  on different pages and ribbon:doVerb can no longer see it.
4341 //
4342 // We fix the message by changing ribbon:doVerb(1) to test flag 143 like doVerb(5).
4343 //  This requires overwriting one of the redundant inventory tests.
4344 //
4345 // Beauty's clothes also have a hair and clothes:doVerb(1) has similar issues
4346 //  but it happens to work. Those items are always on the same page due to their
4347 //  low item numbers and the clothes are removed from inventory before the hair.
4348 //
4349 // Applies to: PC CD, PC Floppy, Mac Floppy
4350 // Responsible method: ribbon:doVerb(1) in script 907
4351 // Fixes bug: #10801
4352 static const uint16 kq6SignatureLookRibbonFix[] = {
4353 	0x30, SIG_ADDTOOFFSET(+2),          // bnt [ verb != Look ]
4354 	0x38, SIG_SELECTOR16(has),          // pushi has
4355 	0x78,                               // push1
4356 	0x39, 0x04,                         // pushi 04
4357 	0x81, SIG_MAGICDWORD, 0x00,         // lag global[0]
4358 	0x4a, 0x06,                         // send 6 [ ego:has 4 (beauty's hair) ]
4359 	0x2e,                               // bt [ continue hair tests ]
4360 	SIG_END
4361 };
4362 
4363 static const uint16 kq6PatchLookRibbonFix[] = {
4364 	PATCH_ADDTOOFFSET(+3),
4365 	0x78,                               // push1
4366 	0x38, PATCH_UINT16(0x008f),         // pushi 008f
4367 	0x46, PATCH_UINT16(0x0391),         // calle [export 0 of script 13], 02 [ is flag 8f set? ]
4368 	      PATCH_UINT16(0x0000), 0x02,
4369 	PATCH_END
4370 };
4371 
4372 // KQ6 CD introduced a bug in the wallflower dance in room 480. The dance is
4373 //  supposed to last until the music ends but in Text mode it stops after only
4374 //  three seconds once the user gains control. This isn't usually enough time
4375 //  to get the hole in the wall. This bug also occurs in Sierra's interpreter.
4376 //
4377 // wallFlowerDance was changed in the CD version for Speech mode but broke Text.
4378 //  In Text mode, changeState(9) creates a dialog with Print, which blocks, and
4379 //  then sets ticks to 12. Meanwhile, wallFlowerDance:handleEvent cues if an
4380 //  event is received in state 9. A mouse click starts a 12 tick race which
4381 //  handleEvent wins, cueing before the countdown expires, and so the countdown
4382 //  expires on state 10, skipping ahead to the three second fadeout. Closing the
4383 //  dialog with the keyboard works because Dialog claims keyboard events when
4384 //  blocking, preventing wallFlowerDance:handleEvent from receiving and cueing.
4385 //
4386 // We fix this by setting the Print dialog to modeless as it was in the floppy
4387 //  version and removing the countdown. wallFlowerDance:handleEvent now receives
4388 //  all events and is the only one responsible for advancing state 9 to 10 in
4389 //  Text mode. This patch does not affect audio modes Speech and Both.
4390 //
4391 // Applies to: PC CD
4392 // Responsible method: wallFlowerDance:changeState(9) in script 480
4393 // Fixes bug: #10811
4394 static const uint16 kq6CDSignatureWallFlowerDanceFix[] = {
4395 	SIG_MAGICDWORD,
4396 	0x39, SIG_SELECTOR8(init),          // pushi init
4397 	0x76,                               // push0
4398 	0x51, 0x15,                         // class Print [ Print: ... init ]
4399 	0x4a, 0x24,                         // send 24
4400 	0x35, 0x0c,                         // ldi 0c
4401 	0x65, 0x20,                         // aTop ticks
4402 	0x32, SIG_UINT16(0x00d0),           // jmp 00d0 [ end of method ]
4403 	SIG_END
4404 };
4405 
4406 static const uint16 kq6CDPatchWallFlowerDanceFix[] = {
4407 	0x38, PATCH_SELECTOR16(modeless),   // pushi modeless
4408 	0x78,                               // push1
4409 	0x78,                               // push1
4410 	0x39, PATCH_SELECTOR8(init),        // pushi init
4411 	0x76,                               // push0
4412 	0x51, 0x15,                         // class Print [ Print: ... modeless: 1, init ]
4413 	0x4a, 0x2a,                         // send 2a
4414 	0x3a,                               // toss
4415 	0x48,                               // ret
4416 	PATCH_END
4417 };
4418 
4419 // Audio + subtitles support - SHARED! - used for King's Quest 6 and Laura Bow 2.
4420 //  This patch gets enabled when the user selects "both" in the ScummVM
4421 //  "Speech + Subtitles" menu. We currently use global[98d] to hold a kMemory
4422 //  pointer.
4423 // Applies to at least: KQ6 PC-CD, LB2 PC-CD
4424 // Patched method: Messager::sayNext / lb2Messager::sayNext (always use text branch)
4425 static const uint16 kq6laurabow2CDSignatureAudioTextSupport1[] = {
4426 	0x89, 0x5a,                         // lsg global[5a]
4427 	0x35, 0x02,                         // ldi 02
4428 	0x12,                               // and
4429 	SIG_MAGICDWORD,
4430 	0x31, 0x13,                         // bnt [audio call]
4431 	0x38, SIG_SELECTOR16(modNum),       // pushi modNum
4432 	SIG_END
4433 };
4434 
4435 static const uint16 kq6laurabow2CDPatchAudioTextSupport1[] = {
4436 	PATCH_ADDTOOFFSET(+5),
4437 	0x33, 0x13,                         // jmp [audio call]
4438 	PATCH_END
4439 };
4440 
4441 // Applies to at least: KQ6 PC-CD, LB2 PC-CD
4442 // Patched method: Messager::sayNext / lb2Messager::sayNext (allocate audio memory)
4443 static const uint16 kq6laurabow2CDSignatureAudioTextSupport2[] = {
4444 	0x7a,                               // push2
4445 	0x78,                               // push1
4446 	0x39, 0x0c,                         // pushi 0c
4447 	0x43, SIG_MAGICDWORD, 0x72, 0x04,   // callk Memory
4448 	0xa5, 0xc9,                         // sat temp[c9]
4449 	SIG_END
4450 };
4451 
4452 static const uint16 kq6laurabow2CDPatchAudioTextSupport2[] = {
4453 	PATCH_ADDTOOFFSET(+7),
4454 	0xa1, 0x62,                         // sag global[62] (global[98d])
4455 	PATCH_END
4456 };
4457 
4458 // Applies to at least: KQ6 PC-CD, LB2 PC-CD
4459 // Patched method: Messager::sayNext / lb2Messager::sayNext (release audio memory)
4460 static const uint16 kq6laurabow2CDSignatureAudioTextSupport3[] = {
4461 	0x7a,                               // push2
4462 	0x39, 0x03,                         // pushi 03
4463 	SIG_MAGICDWORD,
4464 	0x8d, 0xc9,                         // lst temp[c9]
4465 	0x43, 0x72, 0x04,                   // callk Memory
4466 	SIG_END
4467 };
4468 
4469 static const uint16 kq6laurabow2CDPatchAudioTextSupport3[] = {
4470 	PATCH_ADDTOOFFSET(+3),
4471 	0x89, 0x62,                         // lsg global[62] (global[98d])
4472 	PATCH_END
4473 };
4474 
4475 // startText call gets acc = 0 for text-only and acc = 2 for audio+text
4476 // Applies to at least: KQ6 PC-CD, LB2 PC-CD
4477 // Patched method: Narrator::say (use audio memory)
4478 static const uint16 kq6laurabow2CDSignatureAudioTextSupport4[] = {
4479 	// set caller property code
4480 	0x31, 0x08,                         // bnt [set acc to 0 for caller]
4481 	0x87, 0x02,                         // lap param[2]
4482 	0x31, 0x04,                         // bnt [set acc to 0 for caller]
4483 	0x87, 0x02,                         // lap param[2]
4484 	0x33, 0x02,                         // jmp [set caller]
4485 	0x35, 0x00,                         // ldi 00
4486 	0x65, 0x68,                         // aTop caller
4487 	// call startText + startAudio code
4488 	0x89, 0x5a,                         // lsg global[5a]
4489 	0x35, 0x01,                         // ldi 01
4490 	0x12,                               // and
4491 	0x31, 0x08,                         // bnt [skip code]
4492 	0x38, SIG_SELECTOR16(startText),    // pushi startText
4493 	0x78,                               // push1
4494 	0x8f, 0x01,                         // lsp param[1]
4495 	0x54, 0x06,                         // self 06
4496 	0x89, 0x5a,                         // lsg global[5a]
4497 	0x35, 0x02,                         // ldi 02
4498 	0x12,                               // and
4499 	0x31, 0x08,                         // bnt [skip code]
4500 	SIG_MAGICDWORD,
4501 	0x38, SIG_SELECTOR16(startAudio),   // pushi startAudio
4502 	0x78,                               // push1
4503 	0x8f, 0x01,                         // lsp param[1]
4504 	0x54, 0x06,                         // self 06
4505 	SIG_END
4506 };
4507 
4508 static const uint16 kq6laurabow2CDPatchAudioTextSupport4[] = {
4509 	0x31, 0x02,                         // bnt [set caller]
4510 	0x87, 0x02,                         // lap param[2]
4511 	0x65, 0x68,                         // aTop caller
4512 	0x81, 0x5a,                         // lag global[5a]
4513 	0x78,                               // push1
4514 	0x12,                               // and
4515 	0x31, 0x11,                         // bnt [skip startText code]
4516 	0x81, 0x5a,                         // lag global[5a]
4517 	0x7a,                               // push2
4518 	0x12,                               // and
4519 	0x33, 0x03,                         // jmp 3 [skip unused bytes]
4520 	PATCH_ADDTOOFFSET(+22),
4521 	0x89, 0x62,                         // lsg global[62] (global[98d])
4522 	PATCH_END
4523 };
4524 
4525 // Applies to at least: KQ6 PC-CD, LB2 PC-CD
4526 // Patched method: Talker::display/Narrator::say (remove reset saved mouse cursor code)
4527 //  code would screw over mouse cursor
4528 static const uint16 kq6laurabow2CDSignatureAudioTextSupport5[] = {
4529 	SIG_MAGICDWORD,
4530 	0x35, 0x00,                         // ldi 00
4531 	0x65, 0x82,                         // aTop saveCursor
4532 	SIG_END
4533 };
4534 
4535 static const uint16 kq6laurabow2CDPatchAudioTextSupport5[] = {
4536 	0x18, 0x18, 0x18, 0x18,             // (waste bytes)
4537 	PATCH_END
4538 };
4539 
4540 // Additional patch specifically for King's Quest 6
4541 //  Fixes text window placement, when in "dual" mode
4542 // Applies to at least: PC-CD
4543 // Patched method: Kq6Talker::init
4544 static const uint16 kq6CDSignatureAudioTextSupport1[] = {
4545 	SIG_MAGICDWORD,
4546 	0x89, 0x5a,                         // lsg global[5a]
4547 	0x35, 0x02,                         // ldi 02
4548 	0x1a,                               // eq?
4549 	0x31, SIG_ADDTOOFFSET(+1),          // bnt [jump-to-text-code]
4550 	0x78,                               // push1
4551 	SIG_END
4552 };
4553 
4554 static const uint16 kq6CDPatchAudioTextSupport1[] = {
4555 	PATCH_ADDTOOFFSET(+4),
4556 	0x12,                               // and
4557 	PATCH_END
4558 };
4559 
4560 // Additional patch specifically for King's Quest 6
4561 //  Fixes low-res portrait staying on screen for hi-res mode
4562 // Applies to at least: PC-CD
4563 // Patched method: Talker::startText
4564 //  this method is called by Narrator::say and acc is 0 for text-only and 2 for dual mode (audio+text)
4565 static const uint16 kq6CDSignatureAudioTextSupport2[] = {
4566 	SIG_MAGICDWORD,
4567 	0x3f, 0x01,                         // link 01
4568 	0x63, 0x8a,                         // pToa viewInPrint
4569 	0x18,                               // not
4570 	0x31, 0x06,                         // bnt [skip following code]
4571 	0x38, SIG_UINT16(0x00e1),           // pushi 00e1
4572 	0x76,                               // push0
4573 	0x54, 0x04,                         // self 04
4574 	SIG_END
4575 };
4576 
4577 static const uint16 kq6CDPatchAudioTextSupport2[] = {
4578 	PATCH_ADDTOOFFSET(+2),
4579 	0x67, 0x8a,                         // pTos viewInPrint
4580 	0x14,                               // or
4581 	0x2f,                               // bt [skip following code]
4582 	PATCH_END
4583 };
4584 
4585 // Additional patch specifically for King's Quest 6
4586 //  Fixes special windows, used for example in...
4587 //   The Pawn shop (room 280), when the man in a robe complains about no more
4588 //    mints.
4589 //   Room 300 at the cliffs (aka copy protection), when Alexander falls down
4590 //    the cliffs (closes automatically, but too late).
4591 //   Room 210, when Alexander gives the ring to the nightingale (these need a
4592 //    mouse click).
4593 //
4594 //  We have to change even more code because the game uses PODialog class for
4595 //   text windows and myDialog class for audio. Both are saved to
4596 //   KQ6Print::dialog.
4597 //
4598 //  Changing KQ6Print::dialog is disabled for now, because it has side-effects
4599 //   (breaking game over screens).
4600 //
4601 //  Original comment:
4602 //  Sadly PODialog is created during KQ6Print::addText, myDialog is set during
4603 //   KQ6Print::showSelf, which is called much later and KQ6Print::addText
4604 //   requires KQ6Print::dialog to be set, which means we have to set it before
4605 //   calling addText for audio mode, otherwise the user would have to click to
4606 //   get those windows disposed.
4607 //
4608 // Applies to at least: PC-CD
4609 // Patched method: KQ6Print::say
4610 static const uint16 kq6CDSignatureAudioTextSupport3[] = {
4611 	0x31, 0x6e,                         // bnt [to text code]
4612 	SIG_ADDTOOFFSET(+85),
4613 	SIG_MAGICDWORD,
4614 	0x8f, 0x01,                         // lsp param[1]
4615 	0x35, 0x01,                         // ldi 01
4616 	0x1a,                               // eq?
4617 	0x31, 0x0c,                         // bnt [code to set property repressText to 1]
4618 	0x38,                               // pushi (selector addText)
4619 	SIG_ADDTOOFFSET(+9),                // skip addText-calling code
4620 	0x33, 0x10,                         // jmp [to ret]
4621 	0x35, 0x01,                         // ldi 01
4622 	0x65, 0x2e,                         // aTop repressText
4623 	0x33, 0x0a,                         // jmp [to ret]
4624 	SIG_END
4625 };
4626 
4627 static const uint16 kq6CDPatchAudioTextSupport3[] = {
4628 	0x31, 0x68,                         // bnt (adjust branch to reuse audio mode addText-calling code)
4629 	PATCH_ADDTOOFFSET(+85),             // (right at the MAGIC_DWORD)
4630 	// check, if text is supposed to be shown. If yes, skip the follow-up check (param[1])
4631 	0x89, 0x5a,                         // lsg global[5a]
4632 	0x35, 0x01,                         // ldi 01
4633 	0x12,                               // and
4634 	0x2f, 0x07,                         // bt [skip over param check]
4635 	// original code, checks param[1]
4636 	0x8f, 0x01,                         // lsp param[1]
4637 	0x35, 0x01,                         // ldi 01
4638 	0x1a,                               // eq?
4639 	0x31, 0x10,                         // bnt [code to set property repressText to 1], adjusted
4640 	// waste 5 bytes instead of using myDialog class for now
4641 	// setting myDialog class all the time causes game over screens to misbehave (bug #9771)
4642 	0x34, PATCH_UINT16(0x0000),         // ldi 0 (waste 3 bytes)
4643 	0x35, 0x00,                         // ldi 0 (waste 2 bytes)
4644 	// use myDialog class, so that text box automatically disappears (this is not done for text only mode, like in the original)
4645 	//0x72, 0x0e, 0x00,                   // lofsa myDialog
4646 	//0x65, 0x12,                         // aTop dialog
4647 	// followed by original addText-calling code
4648 	0x38,
4649 	PATCH_GETORIGINALUINT16(+95),       // pushi (addText)
4650 	0x78,                               // push1
4651 	0x8f, 0x02,                         // lsp param[2]
4652 	0x59, 0x03,                         // &rest 03
4653 	0x54, 0x06,                         // self 06
4654 	0x48,                               // ret
4655 
4656 	0x35, 0x01,                         // ldi 01
4657 	0x65, 0x2e,                         // aTop repressText
4658 	0x48,                               // ret
4659 	PATCH_END
4660 };
4661 
4662 // Additional patch specifically for King's Quest 6
4663 //  Fixes text-window size for hires portraits mode
4664 //   Otherwise at least at the end some text-windows will be way too small
4665 // Applies to at least: PC-CD
4666 // Patched method: Talker::init
4667 static const uint16 kq6CDSignatureAudioTextSupport4[] = {
4668 	SIG_MAGICDWORD,
4669 	0x63, 0x94,                         // pToa raving
4670 	0x31, 0x0a,                         // bnt [no rave code]
4671 	0x35, 0x00,                         // ldi 00
4672 	SIG_ADDTOOFFSET(+6),                // skip reset of bust, eyes and mouth
4673 	0x33, 0x24,                         // jmp [to super class code]
4674 	SIG_END
4675 };
4676 
4677 static const uint16 kq6CDPatchAudioTextSupport4[] = {
4678 	PATCH_ADDTOOFFSET(+12),
4679 	0x33, PATCH_GETORIGINALBYTEADJUST(+13, -6), // (adjust jump to also include setSize call)
4680 	PATCH_END
4681 };
4682 
4683 //  Fixes text window placement, when dual mode is active (Guards in room 220)
4684 // Applies to at least: PC-CD
4685 // Patched method: tlkGateGuard1::init & tlkGateGuard2::init
4686 static const uint16 kq6CDSignatureAudioTextSupportGuards[] = {
4687 	SIG_MAGICDWORD,
4688 	0x89, 0x5a,                         // lsg global[5a]
4689 	0x35, 0x01,                         // ldi 01
4690 	0x1a,                               // eq?
4691 	SIG_END                             // followed by bnt for Guard1 and bt for Guard2
4692 };
4693 
4694 static const uint16 kq6CDPatchAudioTextSupportGuards[] = {
4695 	PATCH_ADDTOOFFSET(+2),
4696 	0x35, 0x02,                         // ldi 02
4697 	0x1c,                               // ne?
4698 	PATCH_END
4699 };
4700 
4701 //  Fixes text window placement, when portrait+text is shown (Stepmother in room 250)
4702 // Applies to at least: PC-CD
4703 // Patched method: tlkStepmother::init
4704 static const uint16 kq6CDSignatureAudioTextSupportStepmother[] = {
4705 	SIG_MAGICDWORD,
4706 	0x89, 0x5a,                         // lsg global[5a]
4707 	0x35, 0x02,                         // ldi 02
4708 	0x12,                               // and
4709 	0x31,                               // bnt [jump-for-text-code]
4710 	SIG_END
4711 };
4712 
4713 static const uint16 kq6CDPatchAudioTextSupportJumpAlways[] = {
4714 	PATCH_ADDTOOFFSET(+4),
4715 	0x1a,                               // eq?
4716 	PATCH_END
4717 };
4718 
4719 //  Fixes "Girl In The Tower" to get played in dual mode as well
4720 //  Also changes credits to use CD audio for dual mode.
4721 //
4722 // Applies to at least: PC-CD
4723 // Patched method: rm740::cue (script 740), sCredits::init (script 52)
4724 static const uint16 kq6CDSignatureAudioTextSupportGirlInTheTower[] = {
4725 	SIG_MAGICDWORD,
4726 	0x89, 0x5a,                         // lsg global[5a]
4727 	0x35, 0x02,                         // ldi 02
4728 	0x1a,                               // eq?
4729 	0x31,                               // bnt [jump-for-text-code]
4730 	SIG_END
4731 };
4732 
4733 static const uint16 kq6CDPatchAudioTextSupportGirlInTheTower[] = {
4734 	PATCH_ADDTOOFFSET(+4),
4735 	0x12,                               // and
4736 	PATCH_END
4737 };
4738 
4739 //  Fixes dual mode for scenes with Azure and Ariel (room 370)
4740 //   Effectively same patch as the one for fixing "Girl In The Tower"
4741 // Applies to at least: PC-CD
4742 // Patched methods: rm370::init, caughtAtGateCD::changeState, caughtAtGateTXT::changeState, toLabyrinth::changeState
4743 // Fixes bug: #6750
4744 static const uint16 kq6CDSignatureAudioTextSupportAzureAriel[] = {
4745 	SIG_MAGICDWORD,
4746 	0x89, 0x5a,                         // lsg global[5a]
4747 	0x35, 0x02,                         // ldi 02
4748 	0x1a,                               // eq?
4749 	0x31,                               // bnt [jump-for-text-code]
4750 	SIG_END
4751 };
4752 
4753 static const uint16 kq6CDPatchAudioTextSupportAzureAriel[] = {
4754 	PATCH_ADDTOOFFSET(+4),
4755 	0x12,                               // and
4756 	PATCH_END
4757 };
4758 
4759 // Additional patch specifically for King's Quest 6
4760 //  Adds another button state for the text/audio button. We currently use the "speech" view for "dual" mode.
4761 // View 947, loop 9, cel 0+1 -> "text"
4762 // View 947, loop 8, cel 0+1 -> "speech"
4763 // View 947, loop 12, cel 0+1 -> "dual" (this view is injected by us into the game)
4764 // Applies to at least: PC-CD
4765 // Patched method: iconTextSwitch::show, iconTextSwitch::doit
4766 static const uint16 kq6CDSignatureAudioTextMenuSupport[] = {
4767 	SIG_MAGICDWORD,
4768 	0x89, 0x5a,                         // lsg global[5a]
4769 	0x35, 0x02,                         // ldi 02
4770 	0x1a,                               // eq?
4771 	0x31, 0x06,                         // bnt [set text view]
4772 	0x35, 0x08,                         // ldi 08
4773 	0x65, 0x14,                         // aTop loop
4774 	0x33, 0x04,                         // jmp [skip over text view]
4775 	0x35, 0x09,                         // ldi 09
4776 	0x65, 0x14,                         // aTop loop
4777 	SIG_ADDTOOFFSET(+102),              // skip to iconTextSwitch::doit code
4778 	0x89, 0x5a,                         // lsg global[5a]
4779 	0x3c,                               // dup
4780 	0x35, 0x01,                         // ldi 01
4781 	0x1a,                               // eq?
4782 	0x31, 0x06,                         // bnt [set text mode]
4783 	0x35, 0x02,                         // ldi 02
4784 	0xa1, 0x5a,                         // sag global[5a]
4785 	0x33, 0x0a,                         // jmp [skip over text mode code]
4786 	0x3c,                               // dup
4787 	0x35, 0x02,                         // ldi 02
4788 	0x1a,                               // eq?
4789 	0x31, 0x04,                         // bnt [skip over text ode code]
4790 	0x35, 0x01,                         // ldi 01
4791 	0xa1, 0x5a,                         // sag global[5a]
4792 	0x3a,                               // toss
4793 	0x67, 0x14,                         // pTos loop
4794 	0x35, 0x09,                         // ldi 09
4795 	0x1a,                               // eq?
4796 	0x31, 0x04,                         // bnt [set text view]
4797 	0x35, 0x08,                         // ldi 08
4798 	0x33, 0x02,                         // jmp [skip text view]
4799 	0x35, 0x09,                         // ldi 09
4800 	0x65, 0x14,                         // aTop loop
4801 	SIG_END
4802 };
4803 
4804 static const uint16 kq6CDPatchAudioTextMenuSupport[] = {
4805 	PATCH_ADDTOOFFSET(+13),
4806 	0x33, 0x79,                         // jmp [to new text+dual code]
4807 	PATCH_ADDTOOFFSET(+104),            // seek to iconTextSwitch::doit
4808 	0x81, 0x5a,                         // lag global[5a]
4809 	0x78,                               // push1
4810 	0x02,                               // add
4811 	0xa1, 0x5a,                         // sag global[5a]
4812 	0x36,                               // push
4813 	0x35, 0x03,                         // ldi 03
4814 	0x1e,                               // gt?
4815 	0x31, 0x03,                         // bnt [skip over]
4816 	0x78,                               // push1
4817 	0xa9, 0x5a,                         // ssg global[5a]
4818 	0x33, 0x17,                         // jmp [iconTextSwitch::show call]
4819 	// additional code for iconTextSwitch::show
4820 	0x89, 0x5a,                         // lsg global[5a]
4821 	0x35, 0x01,                         // ldi 01
4822 	0x1a,                               // eq?
4823 	0x31, 0x04,                         // bnt [dual mode]
4824 	0x35, 0x09,                         // ldi 09
4825 	0x33, 0x02,                         // jmp [skip over dual mode]
4826 	0x35, 0x0c,                         // ldi 0c (view 947, loop 12, cel 0+1 is our "dual" view, injected by view.cpp)
4827 	0x65, 0x14,                         // aTop loop
4828 	0x32, PATCH_UINT16(0xff75),         // jmp [back to iconTextSwitch::show]
4829 	PATCH_END
4830 };
4831 
4832 //          script, description,                                      signature                                 patch
4833 static const SciScriptPatcherEntry kq6Signatures[] = {
4834 	{  true,    87, "fix Drink Me bottle",                            1, kq6SignatureDrinkMeFix,                   kq6PatchDrinkMeFix },
4835 	{ false,    87, "Mac: Drink Me pic",                              1, kq6SignatureMacDrinkMePic,                kq6PatchMacDrinkMePic },
4836 	{  true,   480, "CD: fix wallflower dance",                       1, kq6CDSignatureWallFlowerDanceFix,         kq6CDPatchWallFlowerDanceFix },
4837 	{  true,   481, "fix duplicate baby cry",                         1, kq6SignatureDuplicateBabyCry,             kq6PatchDuplicateBabyCry },
4838 	{  true,   640, "fix 'Tickets, only' message",                    1, kq6SignatureTicketsOnly,                  kq6PatchTicketsOnly },
4839 	{  true,   907, "fix inventory stack leak",                       1, kq6SignatureInventoryStackFix,            kq6PatchInventoryStackFix },
4840 	{  true,   907, "fix hair detection for ribbon's look msg",       1, kq6SignatureLookRibbonFix,                kq6PatchLookRibbonFix },
4841 	// King's Quest 6 and Laura Bow 2 share basic patches for audio + text support
4842 	// *** King's Quest 6 audio + text support ***
4843 	{ false,   924, "CD: audio + text support KQ6&LB2 1",             1, kq6laurabow2CDSignatureAudioTextSupport1,     kq6laurabow2CDPatchAudioTextSupport1 },
4844 	{ false,   924, "CD: audio + text support KQ6&LB2 2",             1, kq6laurabow2CDSignatureAudioTextSupport2,     kq6laurabow2CDPatchAudioTextSupport2 },
4845 	{ false,   924, "CD: audio + text support KQ6&LB2 3",             1, kq6laurabow2CDSignatureAudioTextSupport3,     kq6laurabow2CDPatchAudioTextSupport3 },
4846 	{ false,   928, "CD: audio + text support KQ6&LB2 4",             1, kq6laurabow2CDSignatureAudioTextSupport4,     kq6laurabow2CDPatchAudioTextSupport4 },
4847 	{ false,   928, "CD: audio + text support KQ6&LB2 5",             2, kq6laurabow2CDSignatureAudioTextSupport5,     kq6laurabow2CDPatchAudioTextSupport5 },
4848 	{ false,   909, "CD: audio + text support KQ6 1",                 2, kq6CDSignatureAudioTextSupport1,              kq6CDPatchAudioTextSupport1 },
4849 	{ false,   928, "CD: audio + text support KQ6 2",                 1, kq6CDSignatureAudioTextSupport2,              kq6CDPatchAudioTextSupport2 },
4850 	{ false,   104, "CD: audio + text support KQ6 3",                 1, kq6CDSignatureAudioTextSupport3,              kq6CDPatchAudioTextSupport3 },
4851 	{ false,   928, "CD: audio + text support KQ6 4",                 1, kq6CDSignatureAudioTextSupport4,              kq6CDPatchAudioTextSupport4 },
4852 	{ false,  1009, "CD: audio + text support KQ6 Guards",            2, kq6CDSignatureAudioTextSupportGuards,         kq6CDPatchAudioTextSupportGuards },
4853 	{ false,  1027, "CD: audio + text support KQ6 Stepmother",        1, kq6CDSignatureAudioTextSupportStepmother,     kq6CDPatchAudioTextSupportJumpAlways },
4854 	{ false,    52, "CD: audio + text support KQ6 Girl In The Tower", 1, kq6CDSignatureAudioTextSupportGirlInTheTower, kq6CDPatchAudioTextSupportGirlInTheTower },
4855 	{ false,   740, "CD: audio + text support KQ6 Girl In The Tower", 1, kq6CDSignatureAudioTextSupportGirlInTheTower, kq6CDPatchAudioTextSupportGirlInTheTower },
4856 	{ false,   370, "CD: audio + text support KQ6 Azure & Ariel",     6, kq6CDSignatureAudioTextSupportAzureAriel,     kq6CDPatchAudioTextSupportAzureAriel },
4857 	{ false,   903, "CD: audio + text support KQ6 menu",              1, kq6CDSignatureAudioTextMenuSupport,           kq6CDPatchAudioTextMenuSupport },
4858 	SCI_SIGNATUREENTRY_TERMINATOR
4859 };
4860 
4861 #ifdef ENABLE_SCI32
4862 #pragma mark -
4863 #pragma mark Kings Quest 7
4864 
4865 // KQ7's subtitles were left unfinished in the shipped game, so there are
4866 // several problems when enabling them from the ScummVM launcher:
4867 //
4868 // 1. `kqMessager::findTalker` tries to determine which class to use for
4869 //    displaying subtitles using the talker number of each message, but the
4870 //    talker data is often bogus (e.g. princess messages normally use talker 7,
4871 //    but talker 99 (which is for the narrator) is used for her messages at the
4872 //    start of chapter 2), so it can't be used.
4873 // 2. Some display classes render subtitles at the top or middle of the game
4874 //    area, blocking the view of what is on the screen.
4875 // 3. In some areas, the colors of the subtitles are changed arbitrarily (e.g.
4876 //    pink/purple at the start of chapter 2).
4877 //
4878 // To work around these problems, we always use KQTalker, force the text area to
4879 // the bottom of the game area, and force it to always use black & white, which
4880 // are guaranteed to not be changed by game scripts.
4881 //
4882 // We make 2 changes to KQNarrator::init and one to Narrator::say.
4883 //
4884 // Applies to at least: PC CD 1.4 English, 1.51 English, 1.51 German, 2.00 English
4885 static const uint16 kq7SubtitleFixSignature1[] = {
4886 	SIG_MAGICDWORD,
4887 	0x39, SIG_SELECTOR8(fore), // pushi fore ($25)
4888 	0x78,                      // push1
4889 	0x39, 0x06,                // pushi 6 - sets fore to 6
4890 	0x39, SIG_SELECTOR8(back), // pushi back ($26)
4891 	0x78,                      // push1
4892 	0x78,                      // push1 - sets back to 1
4893 	0x39, SIG_SELECTOR8(font), // pushi font ($2a)
4894 	0x78,                      // push1
4895 	0x89, 0x16,                // lsg global[$16] - sets font to global[$16]
4896 	0x7a,                      // push2 (y)
4897 	0x78,                      // push1
4898 	0x76,                      // push0 - sets y to 0
4899 	0x54, SIG_UINT16(0x0018),  // self $18
4900 	SIG_END
4901 };
4902 
4903 static const uint16 kq7SubtitleFixPatch1[] = {
4904 	0x33, 0x12, // jmp [skip special init code]
4905 	PATCH_END
4906 };
4907 
4908 // Applies to at least: PC CD 1.51 English, 1.51 German, 2.00 English
4909 static const uint16 kq7SubtitleFixSignature2[] = {
4910 	SIG_MAGICDWORD,
4911 	0x89, 0x5a,                         // lsg global[$5a]
4912 	0x35, 0x02,                         // ldi 2
4913 	0x12,                               // and
4914 	0x31, 0x1e,                         // bnt [skip audio volume code]
4915 	0x38, SIG_SELECTOR16(masterVolume), // pushi masterVolume (0212h for 2.00, 0219h for 1.51)
4916 	0x76,                               // push0
4917 	0x81, 0x01,                         // lag global[1]
4918 	0x4a, SIG_UINT16(0x0004),           // send 4
4919 	0x65, 0x32,                         // aTop curVolume
4920 	0x38, SIG_SELECTOR16(masterVolume), // pushi masterVolume (0212h for 2.00, 0219h for 1.51)
4921 	0x78,                               // push1
4922 	0x67, 0x32,                         // pTos curVolume
4923 	0x35, 0x02,                         // ldi 2
4924 	0x06,                               // mul
4925 	0x36,                               // push
4926 	0x35, 0x03,                         // ldi 3
4927 	0x08,                               // div
4928 	0x36,                               // push
4929 	0x81, 0x01,                         // lag global[1]
4930 	0x4a, SIG_UINT16(0x0006),           // send 6
4931 	// end of volume code
4932 	0x35, 0x01,                         // ldi 1
4933 	0x65, 0x28,                         // aTop initialized
4934 	SIG_END
4935 };
4936 
4937 static const uint16 kq7SubtitleFixPatch2[] = {
4938 	PATCH_ADDTOOFFSET(+5),    // skip to bnt
4939 	0x31, 0x1b,               // bnt [skip audio volume code]
4940 	PATCH_ADDTOOFFSET(+15),   // right after "aTop curVolume / pushi masterVolume / push1"
4941 	0x7a,                     // push2
4942 	0x06,                     // mul (saves 3 bytes in total)
4943 	0x36,                     // push
4944 	0x35, 0x03,               // ldi 3
4945 	0x08,                     // div
4946 	0x36,                     // push
4947 	0x81, 0x01,               // lag global[1]
4948 	0x4a, PATCH_UINT16(0x06), // send 6
4949 	// end of volume code
4950 	0x35, 0x76,               // ldi 118
4951 	0x65, 0x16,               // aTop y
4952 	0x78,                     // push1 (saves 1 byte)
4953 	0x69, 0x28,               // sTop initialized
4954 	PATCH_END
4955 };
4956 
4957 // Applies to at least: PC CD 1.51 English, 1.51 German, 2.00 English
4958 static const uint16 kq7SubtitleFixSignature3[] = {
4959 	SIG_MAGICDWORD,
4960 	0x63, 0x28,                 // pToa initialized
4961 	0x18,                       // not
4962 	0x31, 0x07,                 // bnt [skip init code]
4963 	0x38, SIG_SELECTOR16(init), // pushi init ($8e for 2.00, $93 for 1.51)
4964 	0x76,                       // push0
4965 	0x54, SIG_UINT16(0x0004),   // self 4
4966 	// end of init code
4967 	0x8f, 0x00,                 // lsp param[0]
4968 	0x35, 0x01,                 // ldi 1
4969 	0x1e,                       // gt?
4970 	0x31, 0x08,                 // bnt [set acc to 0]
4971 	0x87, 0x02,                 // lap param[2]
4972 	0x31, 0x04,                 // bnt [set acc to 0]
4973 	0x87, 0x02,                 // lap param[2]
4974 	0x33, 0x02,                 // jmp [over set acc to 0 code]
4975 	0x35, 0x00,                 // ldi 00
4976 	0x65, 0x18,                 // aTop caller
4977 	SIG_END
4978 };
4979 
4980 static const uint16 kq7SubtitleFixPatch3[] = {
4981 	PATCH_ADDTOOFFSET(+2),              // skip over "pToa initialized code"
4982 	0x2f, 0x0c,                         // bt [skip init code] - saved 1 byte
4983 	0x38, PATCH_GETORIGINALUINT16(+6),  // pushi init
4984 	0x76,                               // push0
4985 	0x54, PATCH_UINT16(0x0004),         // self 4
4986 	// additionally set background color here (5 bytes)
4987 	0x34, PATCH_UINT16(0x00ff),         // ldi 255
4988 	0x65, 0x2e,                         // aTop back
4989 	// end of init code
4990 	0x8f, 0x00,                         // lsp param[0]
4991 	0x35, 0x01,                         // ldi 1 (this may get optimized to get another byte)
4992 	0x1e,                               // gt?
4993 	0x31, 0x04,                         // bnt [set acc to 0]
4994 	0x87, 0x02,                         // lap param[2]
4995 	0x2f, 0x02,                         // bt [over set acc to 0 code]
4996 	PATCH_END
4997 };
4998 
4999 // KQ7 has custom video benchmarking code that needs to be disabled in a subroutine
5000 // that is called by KQ7CD::init; see sci2BenchmarkSignature
5001 static const uint16 kq7BenchmarkSignature[] = {
5002 	0x38, SIG_SELECTOR16(new), // pushi new
5003 	0x76,                      // push0
5004 	0x51, SIG_ADDTOOFFSET(+1), // class Actor
5005 	0x4a, SIG_UINT16(0x0004),  // send 4
5006 	0xa5, 0x00,                // sat temp[0]
5007 	0x39, SIG_SELECTOR8(view), // pushi view ($e)
5008 	SIG_MAGICDWORD,
5009 	0x78,                      // push1
5010 	0x38, SIG_UINT16(0xfdd4),  // pushi 64980
5011 	SIG_END
5012 };
5013 
5014 static const uint16 kq7BenchmarkPatch[] = {
5015 	0x34, PATCH_UINT16(10000), // ldi 10000
5016 	0x48,                      // ret
5017 	PATCH_END
5018 };
5019 
5020 // When attempting to use an inventory item on an object that does not interact
5021 // with that item, the game briefly displays an X cursor. It does this by
5022 // spinning for 90000 cycles, which makes the duration dependent on CPU speed,
5023 // maxes out the CPU for no reason, and keeps the engine from polling for
5024 // events (which may make the window appear nonresponsive to the OS).
5025 //
5026 // We replace the loop with a call to kWait().
5027 //
5028 // Applies to at least: KQ7 English 2.00b
5029 // Responsible method: KQ7CD::pragmaFail in script 0
5030 static const uint16 kq7PragmaFailSpinSignature[] = {
5031 	0x35, 0x00,               // ldi 0
5032 	0xa5, 0x02,               // sat temp[2]
5033 	SIG_MAGICDWORD,
5034 	0x8d, 0x02,               // lst temp[2]
5035 	0x35, 0x03,               // ldi 3
5036 	0x22,                     // lt?
5037 	SIG_END
5038 };
5039 
5040 static const uint16 kq7PragmaFailSpinPatch[] = {
5041 	0x78,                                     // push1
5042 	0x39, 0x12,                               // pushi 18 (~300ms)
5043 	0x43, kScummVMWaitId, PATCH_UINT16(0x02), // callk Wait, 2
5044 	0x33, 0x16,                               // jmp [to setCursor]
5045 	PATCH_END
5046 };
5047 
5048 // Room 6100 creates an extra ambrosia, usually floating in upper left of the
5049 //  screen, due to an off by one error. The script's local arrays contain four
5050 //  ambrosia coordinates but the loop that accesses them iterates five times.
5051 //
5052 // Applies to: All versions
5053 // Responsible method: rm6100:init
5054 // Fixes bug #9790
5055 static const uint16 kq7ExtraAmbrosiaSignature[] = {
5056 	SIG_MAGICDWORD,
5057 	0x8d, 0x00,                 // lst 00
5058 	0x35, 0x04,                 // ldi 04
5059 	0x24,                       // le?
5060 	SIG_END
5061 };
5062 
5063 static const uint16 kq7ExtraAmbrosiaPatch[] = {
5064 	PATCH_ADDTOOFFSET(+4),
5065 	0x22,                       // lt?
5066 	PATCH_END
5067 };
5068 
5069 // In KQ7 1.4, after giving the statue to the snake oil salesman, the curtain is
5070 //  drawn on top of ego when walking in front of the wagon. The script doesn't
5071 //  dispose of the salesman and this leaves his final cel stuck on the screen.
5072 //  We add the missing call to snakeSalesman:dispose.
5073 //
5074 // Applies to: English PC 1.4
5075 // Responsible method: giveStatue:changeState
5076 // Fixes bug: #10221
5077 static const uint16 kq7SnakeOilSalesmanSignature[] = {
5078 	0x38, SIG_SELECTOR16(setHeading),   // pushi setHeading
5079 	SIG_ADDTOOFFSET(+0x281),
5080 	0x72, SIG_UINT16(0x15b4),           // lofsa snakeSalesman
5081 	SIG_ADDTOOFFSET(+0x3f),
5082 	0x3c,                               // dup
5083 	0x35, SIG_MAGICDWORD, 0x0c,         // ldi 0c
5084 	0x1a,                               // eq?
5085 	0x30, SIG_UINT16(0x0010),           // bnt 0010 [ state 13 ]
5086 	0x38, SIG_SELECTOR16(setHeading),   // pushi setHeading
5087 	0x7a,                               // pushi2
5088 	0x38, SIG_UINT16(0x00b4),           // pushi 00b4
5089 	0x7c,                               // pushSelf
5090 	0x81, 0x00,                         // lag 00
5091 	0x4a, SIG_UINT16(0x0008),           // send 08  [ KQEgo setHeading: 180 self ]
5092 	0x32, SIG_UINT16(0x0017),           // jmp 0017 [ end of method ]
5093 	SIG_END
5094 };
5095 
5096 static const uint16 kq7SnakeOilSalesmanPatch[] = {
5097 	PATCH_ADDTOOFFSET(+0x02cd),
5098 	0x38, PATCH_SELECTOR16(dispose),    // pushi dispose
5099 	0x76,                               // push0
5100 	0x72, PATCH_UINT16(0x15b4),         // lofsa snakeSalesman
5101 	0x4a, PATCH_UINT16(0x0004),         // send 04  [ snakeSalesman: dispose ]
5102 	0x32, PATCH_UINT16(0xfd26),         // jmp fd26 [ KQEgo setHeading and end of method ]
5103 	PATCH_END
5104 };
5105 
5106 //          script, description,                                      signature                                 patch
5107 static const SciScriptPatcherEntry kq7Signatures[] = {
5108 	{  true,     0, "disable video benchmarking",                  1, kq7BenchmarkSignature,                    kq7BenchmarkPatch },
5109 	{  true,     0, "remove hardcoded spin loop",                  1, kq7PragmaFailSpinSignature,               kq7PragmaFailSpinPatch },
5110 	{  true,  5300, "fix snake oil salesman disposal",             1, kq7SnakeOilSalesmanSignature,             kq7SnakeOilSalesmanPatch },
5111 	{  true,  6100, "fix extra ambrosia",                          1, kq7ExtraAmbrosiaSignature,                kq7ExtraAmbrosiaPatch },
5112 	{  true,    31, "enable subtitles (1/3)",                      1, kq7SubtitleFixSignature1,                 kq7SubtitleFixPatch1 },
5113 	{  true, 64928, "enable subtitles (2/3)",                      1, kq7SubtitleFixSignature2,                 kq7SubtitleFixPatch2 },
5114 	{  true, 64928, "enable subtitles (3/3)",                      1, kq7SubtitleFixSignature3,                 kq7SubtitleFixPatch3 },
5115 	SCI_SIGNATUREENTRY_TERMINATOR
5116 };
5117 
5118 #pragma mark -
5119 #pragma mark Lighthouse
5120 
5121 // When going to room 5 (sierra logo & menu room) from room 380 (the credits
5122 // room), the game tries to clear flags from 0 (global[116] bit 0) to 1423
5123 // (global[204] bit 15), but global[201] is not a flag global (it holds a
5124 // reference to theInvisCursor). This patch stops clearing after flag 1359
5125 // (global[200] bit 15). Hopefully that is good enough to not break the game.
5126 // Applies to at least: English 1.0c & 2.0a
5127 static const uint16 lighthouseFlagResetSignature[] = {
5128 	SIG_MAGICDWORD,
5129 	0x34, SIG_UINT16(0x58f), // ldi 1423
5130 	0x24,                    // le?
5131 	SIG_END
5132 };
5133 
5134 static const uint16 lighthouseFlagResetPatch[] = {
5135 	0x34, PATCH_UINT16(0x54f), // ldi 1359
5136 	PATCH_END
5137 };
5138 
5139 // When doing a system check on the portal computer in the lighthouse, the game
5140 // counts up to 1024MB, one megabyte at a time. In SSCI, this count speed would
5141 // be video speed dependent, but with our frame rate throttler, it takes 17
5142 // seconds. So, replace this slowness with a much faster POST that is more
5143 // accurate to the original game.
5144 // Applies to at least: US English 1.0c
5145 static const uint16 lighthouseMemoryCountSignature[] = {
5146 	SIG_MAGICDWORD,
5147 	0x8d, 0x02,             // lst temp[2]
5148 	0x35, 0x0a,             // ldi 10
5149 	0x24,                   // le?
5150 	0x31, 0x3b,             // bnt [to second digit overflow]
5151 	SIG_ADDTOOFFSET(+4),    // ldi, sat
5152 	0x8d, 0x03,             // lst temp[3]
5153 	0x35, 0x0a,             // ldi 10
5154 	SIG_END
5155 };
5156 
5157 static const uint16 lighthouseMemoryCountPatch[] = {
5158 	PATCH_ADDTOOFFSET(+2), // lst temp[2]
5159 	0x35, 0x02,            // ldi 2
5160 	PATCH_ADDTOOFFSET(+9), // le?, bnt, ldi, sat, lst
5161 	0x35, 0x02,            // ldi 2
5162 	PATCH_END
5163 };
5164 
5165 //          script, description,                                      signature                         patch
5166 static const SciScriptPatcherEntry lighthouseSignatures[] = {
5167 	{  true,     5, "fix bad globals clear after credits",         1, lighthouseFlagResetSignature,     lighthouseFlagResetPatch },
5168 	{  true,   360, "fix slow computer memory counter",            1, lighthouseMemoryCountSignature,   lighthouseMemoryCountPatch },
5169 	{  true, 64990, "increase number of save games (1/2)",         1, sci2NumSavesSignature1,           sci2NumSavesPatch1 },
5170 	{  true, 64990, "increase number of save games (2/2)",         1, sci2NumSavesSignature2,           sci2NumSavesPatch2 },
5171 	{  true, 64990, "disable change directory button",             1, sci2ChangeDirSignature,           sci2ChangeDirPatch },
5172 	SCI_SIGNATUREENTRY_TERMINATOR
5173 };
5174 
5175 #endif
5176 
5177 // ===========================================================================
5178 // When Robin hands out the scroll to Marion and then types his name using the
5179 // hand code, the German version's script contains a typo (likely a copy/paste
5180 // error), and the local procedure that shows each letter is called twice. The
5181 // The procedure expects a letter arg and returns no value, so the first call
5182 // takes its letter and feeds an undefined value to the second call. Thus the
5183 // kStrCat() within the procedure reads a random pointer and crashes.
5184 //
5185 // We patch all of the 5 doubled local calls (one for each letter typed from
5186 // "R", "O", "B", "I", "N") to be the same as the English version.
5187 // Applies to at least: German floppy
5188 // Responsible method: giveScroll::changeState(19,21,23,25,27) in script 210
5189 // Fixes bug: #5264
5190 static const uint16 longbowSignatureShowHandCode[] = {
5191 	0x78,                            // push1 (1 call arg)
5192                                      //
5193 	0x78,                            // push1 (1 call arg)
5194 	0x72, SIG_ADDTOOFFSET(+2),       // lofsa (letter that was typed)
5195 	0x36,                            // push
5196 	0x40, SIG_ADDTOOFFSET(+2), 0x02, // call [localproc], 02
5197                                      //
5198 	0x36,                            // push (the result is an arg for the next call)
5199 	0x40, SIG_ADDTOOFFSET(+2), SIG_MAGICDWORD, 0x02, // call [localproc], 02
5200                                      //
5201 	0x38, SIG_SELECTOR16(setMotion), // pushi setMotion (0x11c in Longbow German)
5202 	0x39, SIG_SELECTOR8(x),          // pushi x (0x04 in Longbow German)
5203 	0x51, 0x1e,                      // class MoveTo
5204 	SIG_END
5205 };
5206 
5207 static const uint16 longbowPatchShowHandCode[] = {
5208 	0x39, 0x01,                      // pushi 1 (combine the two push1's in one, like in the English version)
5209 	PATCH_ADDTOOFFSET(+3),           // leave the lofsa untouched
5210 	// The following will remove the first push & call
5211 	0x32, PATCH_UINT16(0x0002),      // jmp 02 [to the second push & call]
5212 	0x35, 0x00,                      // ldi 0 (waste 2 bytes)
5213 	PATCH_END
5214 };
5215 
5216 // When walking through the forest, arithmetic errors may occur at "random".
5217 // The scripts try to add a value and a pointer to the object "berryBush".
5218 //
5219 // This is caused by a local variable overflow.
5220 //
5221 // The scripts create berry bush objects dynamically. The array storage for
5222 // those bushes may hold a total of 8 bushes. But sometimes 10 bushes
5223 // are created. This overwrites 2 additional locals in script 225 and
5224 // those locals are used normally for value lookups.
5225 //
5226 // Changing the total of bushes could cause all sorts of other issues,
5227 // that's why I rather patched the code, that uses the locals for a lookup.
5228 // Which means it doesn't matter anymore when those locals are overwritten.
5229 //
5230 // Applies to at least: English PC floppy, German PC floppy, English Amiga floppy
5231 // Responsible method: export 2 of script 225
5232 // Fixes bug: #6751
5233 static const uint16 longbowSignatureBerryBushFix[] = {
5234 	0x89, 0x70,                      // lsg global[70h]
5235 	0x35, 0x03,                      // ldi 03h
5236 	0x1a,                            // eq?
5237 	0x2e, SIG_UINT16(0x002d),        // bt [process code]
5238 	0x89, 0x70,                      // lsg global[70h]
5239 	0x35, 0x04,                      // ldi 04h
5240 	0x1a,                            // eq?
5241 	0x2e, SIG_UINT16(0x0025),        // bt [process code]
5242 	0x89, 0x70,                      // lsg global[70h]
5243 	0x35, 0x05,                      // ldi 05h
5244 	0x1a,                            // eq?
5245 	0x2e, SIG_UINT16(0x001d),        // bt [process code]
5246 	0x89, 0x70,                      // lsg global[70h]
5247 	0x35, 0x06,                      // ldi 06h
5248 	0x1a,                            // eq?
5249 	0x2e, SIG_UINT16(0x0015),        // bt [process code]
5250 	0x89, 0x70,                      // lsg global[70h]
5251 	0x35, 0x18,                      // ldi 18h
5252 	0x1a,                            // eq?
5253 	0x2e, SIG_UINT16(0x000d),        // bt [process code]
5254 	0x89, 0x70,                      // lsg global[70h]
5255 	0x35, 0x19,                      // ldi 19h
5256 	0x1a,                            // eq?
5257 	0x2e, SIG_UINT16(0x0005),        // bt [process code]
5258 	0x89, 0x70,                      // lsg global[70h]
5259 	0x35, 0x1a,                      // ldi 1Ah
5260 	0x1a,                            // eq?
5261 	// jump location for the "bt" instructions
5262 	0x30, SIG_UINT16(0x0011),        // bnt [skip over follow up code, to offset 0c35]
5263 	// 55 bytes until here
5264 	0x85, 0x00,                      // lat temp[0]
5265 	SIG_MAGICDWORD,
5266 	0x9a, SIG_UINT16(0x0110),        // lsli local[110h] -> 110h points normally to 110h / 2Bh
5267 	// 5 bytes
5268 	0x7a,                            // push2
5269 	SIG_END
5270 };
5271 
5272 static const uint16 longbowPatchBerryBushFix[] = {
5273 	PATCH_ADDTOOFFSET(+4),           // keep: lsg global[70h], ldi 03h
5274 	0x22,                            // lt? (global < 03h)
5275 	0x2f, 0x42,                      // bt [skip over all the code directly]
5276 	0x89, 0x70,                      // lsg global[70h]
5277 	0x35, 0x06,                      // ldi 06h
5278 	0x24,                            // le? (global <= 06h)
5279 	0x2f, 0x0e,                      // bt [to kRandom code]
5280 	0x89, 0x70,                      // lsg global[70h]
5281 	0x35, 0x18,                      // ldi 18h
5282 	0x22,                            // lt? (global < 18h)
5283 	0x2f, 0x34,                      // bt [skip over all the code directly]
5284 	0x89, 0x70,                      // lsg global[70h]
5285 	0x35, 0x1a,                      // ldi 1Ah
5286 	0x24,                            // le? (global <= 1Ah)
5287 	0x31, 0x2d,                      // bnt [skip over all the code directly]
5288 	// 28 bytes, 27 bytes saved
5289 	// kRandom code
5290 	0x85, 0x00,                      // lat temp[0]
5291 	0x2f, 0x05,                      // bt [skip over case 0]
5292 	// temp[0] == 0
5293 	0x38, PATCH_UINT16(0x0110),      // pushi 0110h - that's what's normally at local[110h]
5294 	0x33, 0x18,                      // jmp [kRandom call]
5295 	// check temp[0] further
5296 	0x78,                            // push1
5297 	0x1a,                            // eq?
5298 	0x31, 0x05,                      // bnt [skip over case 1]
5299 	// temp[0] == 1
5300 	0x38, PATCH_UINT16(0x002b),      // pushi 002Bh - that's what's normally at local[111h]
5301 	0x33, 0x0f,                      // jmp [kRandom call]
5302 	// temp[0] >= 2
5303 	0x8d, 0x00,                      // lst temp[0]
5304 	0x35, 0x02,                      // ldi 02
5305 	0x04,                            // sub
5306 	0x9a, PATCH_UINT16(0x0112),      // lsli local[112h] -> look up value in 2nd table
5307 	                                 // this may not be needed at all and was just added for safety reasons
5308 	// waste 9 spare bytes
5309 	0x35, 0x00,                      // ldi 00
5310 	0x35, 0x00,                      // ldi 00
5311 	0x34, PATCH_UINT16(0x0000),      // ldi 0000
5312 	PATCH_END
5313 };
5314 
5315 // The camp (room 150) has a bug that can prevent the outlaws from ever rescuing
5316 //  the boys at sunset on day 5 or 6. The rescue occurs when entering camp as an
5317 //  abbey monk after leaving town exactly 3 times but this assumes that the
5318 //  counter can't exceed this. Wearing a different disguise can increment the
5319 //  counter beyond 3 at which point sunset can never occur.
5320 //
5321 // We fix this by patching the counter tests to greater than or equals. This
5322 //  makes them consistent with the other scripts that test this global variable.
5323 //
5324 // Applies to: English PC Floppy, German PC Floppy, English Amiga Floppy
5325 // Responsible method: local procedure #3 in script 150
5326 // Fixes bug: #10839
5327 static const uint16 longbowSignatureCampSunsetFix[] = {
5328 	SIG_MAGICDWORD,
5329 	0x89, 0x8e,                     // lsg global[8e] [ times left town ]
5330 	0x35, 0x03,                     // ldi 03
5331 	0x1a,                           // eq?
5332 	SIG_END
5333 };
5334 
5335 static const uint16 longbowPatchCampSunsetFix[] = {
5336 	PATCH_ADDTOOFFSET(+4),
5337 	0x20,                        	// ge?
5338 	PATCH_END
5339 };
5340 
5341 // The town map (room 260) has a bug that can send Robin to the wrong room.
5342 //  Loading the map from town on day 5 or 6 automatically sends Robin to camp
5343 //  (room 150) after leaving town more than twice. The intent is to start the
5344 //  sunset scene where the outlaws rescue the boys, but the map doesn't test the
5345 //  correct sunset conditions and can load an empty camp, even on the wrong day.
5346 //
5347 // We fix this by changing the map's logic to match the camp's by requiring the
5348 //  abbey monk disguise to be worn and the rescue flag to not be set.
5349 //
5350 // Applies to: English PC Floppy, German PC Floppy, English Amiga Floppy
5351 // Responsible method: rm260:init
5352 // Fixes bug: #10839
5353 static const uint16 longbowSignatureTownMapSunsetFix[] = {
5354 	SIG_MAGICDWORD,
5355 	0x39, 0x05,                     // pushi 05
5356 	0x81, 0x82,                     // lag global[82] [ day ]
5357 	0x24,                           // le?
5358 	0x30, SIG_UINT16(0x0089),       // bnt 0089 [ no sunset if day < 5 ]
5359 	0x60,                           // pprev
5360 	0x35, 0x06,                     // ldi 06
5361 	0x24,                           // le?
5362 	0x30, SIG_UINT16(0x0082),       // bnt 0082 [ no sunset if day > 6 ]
5363 	0x89, 0x8e,                     // lsg global[8e]
5364 	0x35, 0x01,                     // ldi 01
5365 	SIG_END
5366 };
5367 
5368 static const uint16 longbowPatchTownMapSunsetFix[] = {
5369 	0x89, 0x7e,                     // lsg global[7e] [ current disguise ]
5370 	0x35, 0x05,                     // ldi 05 [ abbey monk ]
5371 	0x1c,                           // ne?
5372 	0x2f, 0x06,                     // bt 06 [ no sunset if disguise != abbey monk ]
5373 	0x78,                           // push1
5374 	0x39, 0x38,                     // pushi 38
5375 	0x45, 0x05, 0x02,               // callb [export 5 of script 0], 02 [ is rescue flag set? ]
5376 	0x2e, PATCH_UINT16(0x0081),     // bt 0081 [ no sunset if rescue flag is set ]
5377 	0x81, 0x8e,                     // lag global[8e]
5378 	0x78,                           // push1 [ save a byte ]
5379 	PATCH_END
5380 };
5381 
5382 // Ending day 5 or 6 by choosing to attack the castle fails to set the rescue
5383 //  flag which tells the next day what to do. This flag is set when rescuing
5384 //  the boys yourself and when the outlaws rescue them at sunset. Without this
5385 //  flag, the sunset rescue can repeat the next day and break the game.
5386 //
5387 // We fix this by setting the flag when returning the boys to their mother in
5388 //  room 250 after the attack.
5389 //
5390 // Applies to: English PC Floppy, German PC Floppy, English Amiga Floppy
5391 // Responsible method: boysSaved:changeState(0)
5392 // Fixes bug: #10839
5393 static const uint16 longbowSignatureRescueFlagFix[] = {
5394 	0x3c,                           // dup
5395 	0x35, 0x00,                     // ldi 00
5396 	0x1a,                           // eq?
5397 	0x30, SIG_MAGICDWORD,           // bnt 0003 [ state 1 ]
5398 	      SIG_UINT16(0x0003),
5399 	0x32, SIG_UINT16(0x025b),       // jmp 025b [ end of method ]
5400 	SIG_END
5401 };
5402 
5403 static const uint16 longbowPatchRescueFlagFix[] = {
5404 	0x2f, 0x08,                     // bt 08 [ state 1 ]
5405 	0x78,                           // push1
5406 	0x39, 0x38,                     // pushi 38
5407 	0x45, 0x06, 0x02,               // callb [export 6 of script 0], 02 [ set rescue flag ]
5408 	0x3a,                           // toss
5409 	0x48,                           // ret
5410 	PATCH_END
5411 };
5412 
5413 // On day 7, Tuck can appear at camp to say that the widow wants to see you when
5414 //  she really doesn't. This scene is only supposed to occur if you haven't
5415 //  received the net but the script only tests if the net is currently in
5416 //  inventory, which it isn't if you've already used it or are in disguise.
5417 //
5418 // We fix this by testing the net's owner instead of inventory. If net:owner is
5419 //  non-zero then it's in inventory or in your cave or has been used.
5420 //
5421 // Applies to: English PC Floppy, German PC Floppy, English Amiga Floppy
5422 // Responsible method: local procedure #3 in script 150
5423 // Fixes bug: #10847
5424 static const uint16 longbowSignatureTuckNetFix[] = {
5425 	SIG_MAGICDWORD,
5426 	0x30, SIG_UINT16(0x03a2),       // bnt 03a2 [ end of method ]
5427 	0x38, SIG_SELECTOR16(has),      // pushi has
5428 	0x78,                           // push1
5429 	0x39, 0x04,                     // pushi 04
5430 	0x81, 0x00,                     // lag global[0]
5431 	0x4a, 0x06,                     // send 6 [ ego: has 4 ]
5432 	0x18,                           // not
5433 	0x30, SIG_UINT16(0x0394),       // bnt 0394 [ end of method if net not in inventory ]
5434 	0x78,                           // push1
5435 	0x39, 0x47,                     // pushi 47
5436 	0x45, 0x05, 0x02,               // callb [export 5 of script 0], 02 [ is flag 47 set? ]
5437 	0x18,                           // not
5438 	0x30, SIG_UINT16(0x038a),       // bnt 038a [ end of method ]
5439 	SIG_ADDTOOFFSET(+60),
5440 	0x32, SIG_UINT16(0x034b),       // jmp 034b [ end of method ]
5441 	SIG_END
5442 };
5443 
5444 static const uint16 longbowPatchTuckNetFix[] = {
5445 	0x31, 0x55,                     // bnt 55 [ skip scene, save a byte ]
5446 	0x39, PATCH_SELECTOR8(at),      // pushi at
5447 	0x78,                           // push1
5448 	0x39, 0x04,                     // pushi 04
5449 	0x81, 0x09,                     // lag global[9]
5450 	0x4a, 0x06,                     // send 6 [ Inv: at 4 ]
5451 	0x38, PATCH_SELECTOR16(owner),  // pushi owner
5452 	0x76,                           // push0
5453 	0x4a, 0x04,                     // send 4 [ net: owner? ]
5454 	0x2f, 0x44,                     // bt 44 [ skip scene if net:owner != 0 ]
5455 	0x78,                           // push1
5456 	0x39, 0x47,                     // pushi 47
5457 	0x45, 0x05, 0x02,               // callb [export 5 of script 0], 02 [ is flag 47 set? ]
5458 	0x2f, 0x3c,                     // bt 3c [ skip scene, save 2 bytes ]
5459 	PATCH_END
5460 };
5461 
5462 // On day 9, room 350 outside the cobbler's hut is initialized incorrectly if
5463 //  disguised as a monk. The entrance to the hut is broken and several minor
5464 //  messages are incorrect. This is due to the room's script assuming that the
5465 //  only disguises that day are yeoman and merchant. A monk disguise causes some
5466 //  tests to pass and others to fail, leaving the room in an inconsistent state.
5467 //
5468 // We fix this by changing the yeoman disguise tests in the script to include
5469 //  the monk disguises. The disguise global is set to 4 for yeoman and 5 or 6
5470 //  for monk disguises so we patch the tests to be greater than or equals to.
5471 //
5472 // Applies to: English PC Floppy, German PC Floppy, English Amiga Floppy
5473 // Responsible methods: rm350:init, lobbsHut:doVerb, lobbsDoor:doVerb,
5474 //                      lobbsCover:doVerb, tailorDoor:doVerb
5475 // Fixes bug: #10834
5476 static const uint16 longbowSignatureCobblerHut[] = {
5477 	SIG_MAGICDWORD,
5478 	0x89, 0x7e,                     // lsg global[7e] [ current disguise ]
5479 	0x35, 0x04,                     // ldi 04 [ yeoman ]
5480 	0x1a,                           // eq?    [ is current disguise yeoman? ]
5481 	SIG_END
5482 };
5483 
5484 static const uint16 longbowPatchCobblerHut[] = {
5485 	PATCH_ADDTOOFFSET(+4),
5486 	0x20,                           // ge? [ is current disguise yeoman or monk? ]
5487 	PATCH_END
5488 };
5489 
5490 // The Amiga version of room 530 adds a broken fDrunk:onMe method which prevents
5491 //  messages when clicking on the drunk on the floor of the pub and causes a
5492 //  signature mismatch on every click in the room. fDrunk:onMe passes an Event
5493 //  object as an integer x coordinate and an uninitialized parameter as a y
5494 //  coordinate to kOnControl. This is a signature mismatch and would cause onMe
5495 //  to return false on every click and prevent hit testing from dispatching
5496 //  events to fDrunk. It's unclear why Sierra added this method to this one
5497 //  Feature in the room. Even if it worked, Feature:onMe already does this.
5498 //
5499 // We fix this by replacing fDrunk:onMe's contents with a call to super:onMe
5500 //  which calls kOnControl correctly and does proper hit testing, making its
5501 //  behavior consistent with the DOS version, which doesn't override onMe.
5502 //
5503 // Applies to: English Amiga Floppy
5504 // Responsible method: fDrunk:onMe
5505 // Fixes bug: #9688
5506 static const uint16 longbowSignatureAmigaPubFix[] = {
5507 	SIG_MAGICDWORD,
5508 	0x67, 0x20,                     // pTos onMeCheck
5509 	0x39, 0x03,                     // pushi 03
5510 	0x39, 0x04,                     // pushi 04
5511 	0x8f, 0x01,                     // lsp param[1]
5512 	0x8f, 0x02,                     // lsp param[2]
5513 	0x43, 0x4e, 0x06,               // callk OnControl, 6
5514 	SIG_END
5515 };
5516 
5517 static const uint16 longbowPatchAmigaPubFix[] = {
5518 	0x38, PATCH_UINT16(0x00c4),     // pushi 00c4 [ onMe, hard-coded for amiga ]
5519 	0x76,                           // push0
5520 	0x59, 0x01,                     // &rest 1
5521 	0x57, 0x2c, 0x04,               // super Feature, 4 [ super: onMe &rest ]
5522 	0x48,                           // ret
5523 	PATCH_END
5524 };
5525 
5526 // WORKAROUND: Script needed, because of differences in our pathfinding
5527 // algorithm
5528 // When the guards kick Robin out of archery room 320 the game locks up due to
5529 //  pathfinding algorithm differences. Ours sends ego in the wrong direction,
5530 //  colliding with a guard, and preventing the script from continuing.
5531 //
5532 // Applies to: English PC Floppy, German PC Floppy, English Amiga Floppy
5533 // Responsible method: takeHimOut:changeState(1)
5534 // Fixes bug: #10896
5535 static const uint16 longbowSignatureArcherPathfinding[] = {
5536 	SIG_MAGICDWORD,
5537 	0x38, SIG_UINT16(0x00c8),       // pushi 00c8 [ y = 200 ]
5538 	0x7c,                           // pushSelf
5539 	0x81, 0x00,                     // lag 00
5540 	0x4a, 0x0c,                     // send 0c [ ego setMotion: PolyPath (ego x?) 200 self ]
5541 	SIG_END
5542 };
5543 
5544 static const uint16 longbowPatchArcherPathfinding[] = {
5545 	0x38, PATCH_UINT16(0x00c4),     // pushi 00c4 [ y = 196 ]
5546 	PATCH_END
5547 };
5548 
5549 // Longbow 1.0 has two random but common game-breaking bugs: Green Man's riddle
5550 //  scene never ends and the Sheriff's men catch Robin too quickly when sweeping
5551 //  the forest. Both are due to reusing an uninitialized global variable.
5552 //
5553 // Global 137 is used by the abbey hedge maze to store ego's cel during room
5554 //  transitions. Exiting the maze leaves this as a random value between 0 and 5.
5555 //  The forest sweep also uses this global but as a counter it expects to start
5556 //  at 0. It increments as Robin changes rooms during a sweep until it reaches a
5557 //  a maximum and he is caught. This is usually 7 but in some rooms it's only 3.
5558 //  A high initial value can make this sequence impossible. rm180:doit also
5559 //  tests the sweep counter and doesn't allow scripts to respond to a hand code
5560 //  when greater than 2. This breaks the riddle scene after the first answer.
5561 //
5562 // We fix this by clearing global 137 at the start of days 1-7 and 11 so that
5563 //  stale hedge maze values from days 5/6 and 10 don't affect the day 7 riddles
5564 //  or the sweeps on days 9 and 12. Ideally we could just clear this at the
5565 //  start of each day but there's no day initialization script. Instead we add
5566 //  our day-specific code to Robin's cave (room 140), similar to Sierra's patch
5567 //  and later versions.
5568 //
5569 // Applies to: English PC Floppy 1.0
5570 // Responsible method: localproc_001a in script 140
5571 // Fixes bug #5036
5572 static const uint16 longbowSignatureGreenManForestSweepFix[] = {
5573 	0x89, SIG_MAGICDWORD, 0x82,     // lsg 82 [ day ]
5574 	0x35, 0x01,                     // ldi 01
5575 	0x1a,                           // eq?
5576 	0x30, SIG_UINT16(0x0019),       // bnt 0019 [ skip horn init ]
5577 	0x38, SIG_SELECTOR16(has),      // pushi has
5578 	0x78,                           // push1
5579 	0x78,                           // push1
5580 	0x81, 0x00,                     // lag 00
5581 	0x4a, 0x06,                     // send 06 [ ego has: 1 ]
5582 	0x18,                           // not
5583 	0x30, SIG_UINT16(0x000c),       // bnt 000c [ skip horn init ]
5584 	0x39, SIG_SELECTOR8(init),      // pushi init
5585 	0x76,                           // push0
5586 	0x38, SIG_ADDTOOFFSET(+2),      // pushi stopUpd
5587 	0x76,                           // push0
5588 	0x72, SIG_UINT16(0x19b2),       // lofsa horn
5589 	0x4a, 0x08,                     // send 08 [ horn init: stopUpd: ]
5590 	0x89, 0x7e,                     // lsg 7e
5591 	0x35, 0x00,                     // ldi 00
5592 	0x1a,                           // eq?
5593 	0x2e, SIG_UINT16(0005),         // bt 0005
5594 	SIG_ADDTOOFFSET(+19),
5595 	0x39, SIG_SELECTOR8(init),      // push init
5596 	0x76,                           // push0
5597 	0x38, SIG_ADDTOOFFSET(+2),      // pushi stopUpd
5598 	0x76,                           // push0
5599 	0x72, SIG_UINT16(0x1912),       // lofsa bow
5600 	SIG_END
5601 };
5602 
5603 static const uint16 longbowPatchGreenManForestSweepFix[] = {
5604 	0x39, 0x07,                     // pushi 07
5605 	0x81, 0x82,                     // lag 82 [ day ]
5606 	0x22,                           // lt?
5607 	0x31, 0x06,                     // bnt 06
5608 	0x60,                           // pprev  [ day ]
5609 	0x35, 0x0b,                     // ldi 0b
5610 	0x1c,                           // ne?
5611 	0x2f, 0x02,                     // bt 02
5612 	0xa1, 0x89,                     // sag 89 [ sweep-count = 0 if day <= 7 or day == 11 ]
5613 	0x81, 0x82,                     // lag 82 [ day ]
5614 	0x78,                           // push1
5615 	0x1a,                           // eq?
5616 	0x31, 0x10,                     // bnt 10 [ skip horn init ]
5617 	0x38, PATCH_SELECTOR16(has),    // pushi has
5618 	0x78,                           // push1
5619 	0x78,                           // push1
5620 	0x81, 0x00,                     // lag 00
5621 	0x4a, 0x06,                     // send 06 [ ego has: 1 ]
5622 	0x2f, 0x05,                     // bt 05 [ skip horn init ]
5623 	0x72, PATCH_UINT16(0x19b2),     // lofsa horn
5624 	0x33, 0x1a,                     // jmp 1c [ continue horn init ]
5625 	0x81, 0x7e,                     // lag 7e
5626 	0x31, 0x08,                     // bnt 08
5627 	PATCH_ADDTOOFFSET(+19),
5628 	0x72, PATCH_UINT16(0x1912),     // lofsa bow
5629 	0x39, PATCH_SELECTOR8(init),    // push init
5630 	0x76,                           // push0
5631 	0x38, PATCH_GETORIGINALUINT16(+25), // pushi stopUpd
5632 	0x76,                           // push0
5633 	PATCH_END
5634 };
5635 
5636 // After rescuing Fulk in the Amiga version, rescueOfFulk stores the boat speed
5637 //  in a temporary variable during one state and expects it to still be there in
5638 //  a later state, which only worked by accident in Sierra's interpreter. This
5639 //  Amiga tweak was made so that on slower machines the boat would animate after
5640 //  Fulk and Robin leave the screen. We fix this by using the script's register
5641 //  property for storage instead of a temporary variable.
5642 //
5643 // Applies to: English Amiga Floppy
5644 // Responsible method: rescueOfFulk:changeState
5645 // Fixes bug: #11137
5646 static const uint16 longbowSignatureAmigaFulkRescue[] = {
5647 	SIG_MAGICDWORD,
5648 	0xa5, 0x00,                     // sat 00
5649 	0x89, 0x57,                     // lsg 87
5650 	SIG_ADDTOOFFSET(+10),
5651 	0x8d, 0x00,                     // lst 00
5652 	SIG_ADDTOOFFSET(+635),
5653 	0x8d, 0x00,                     // lst 00
5654 	SIG_END
5655 };
5656 
5657 static const uint16 longbowPatchAmigaFulkRescue[] = {
5658 	0x65, 0x1a,                     // aTop register
5659 	PATCH_ADDTOOFFSET(+12),
5660 	0x67, 0x1a,                     // pTos register
5661 	PATCH_ADDTOOFFSET(+635),
5662 	0x67, 0x1a,                     // pTos register
5663 	PATCH_END
5664 };
5665 
5666 // The Amiga version has an unusual speed test which takes 10 seconds to run in
5667 //  ScummVM, causing the test to assume a slow machine speed and reduce details
5668 //  throughout the game. We disable the speed test and its long delay before the
5669 //  the Sierra logo so that the fastest machine speed is used.
5670 //
5671 // Applies to: English Amiga Floppy
5672 // Responsible method: speedScript:changeState
5673 static const uint16 longbowSignatureAmigaSpeedTest[] = {
5674 	// state 1
5675 	0x32, SIG_UINT16(0x0164),       // jmp 0164 [ end of method ]
5676 	SIG_ADDTOOFFSET(+0xe9),
5677 	// state 2
5678 	SIG_MAGICDWORD,
5679 	0x35, 0x02,                     // ldi 02   [ fastest machine speed ]
5680 	0x32, SIG_UINT16(0x000f),       // jmp 000f [ set machine speed ]
5681 	SIG_END
5682 };
5683 
5684 static const uint16 longbowPatchAmigaSpeedTest[] = {
5685 	0x32, PATCH_UINT16(0x00e9),     // jmp 00e9 [ skip test, use fastest machine speed ]
5686 	PATCH_END
5687 };
5688 
5689 //          script, description,                                      signature                                patch
5690 static const SciScriptPatcherEntry longbowSignatures[] = {
5691 	{  true,   140, "green man riddles and forest sweep fix",      1, longbowSignatureGreenManForestSweepFix,  longbowPatchGreenManForestSweepFix },
5692 	{  true,   150, "day 5/6 camp sunset fix",                     2, longbowSignatureCampSunsetFix,           longbowPatchCampSunsetFix },
5693 	{  true,   150, "day 7 tuck net fix",                          1, longbowSignatureTuckNetFix,              longbowPatchTuckNetFix },
5694 	{  true,   210, "hand code crash",                             5, longbowSignatureShowHandCode,            longbowPatchShowHandCode },
5695 	{  true,   225, "arithmetic berry bush fix",                   1, longbowSignatureBerryBushFix,            longbowPatchBerryBushFix },
5696 	{  true,   250, "day 5/6 rescue flag fix",                     1, longbowSignatureRescueFlagFix,           longbowPatchRescueFlagFix },
5697 	{  true,   260, "day 5/6 town map sunset fix",                 1, longbowSignatureTownMapSunsetFix,        longbowPatchTownMapSunsetFix },
5698 	{  true,   320, "day 8 archer pathfinding workaround",         1, longbowSignatureArcherPathfinding,       longbowPatchArcherPathfinding },
5699 	{  true,   350, "day 9 cobbler hut fix",                      10, longbowSignatureCobblerHut,              longbowPatchCobblerHut },
5700 	{  true,   530, "amiga pub fix",                               1, longbowSignatureAmigaPubFix,             longbowPatchAmigaPubFix },
5701 	{  true,   600, "amiga fulk rescue fix",                       1, longbowSignatureAmigaFulkRescue,         longbowPatchAmigaFulkRescue },
5702 	{  true,   803, "amiga speed test",                            1, longbowSignatureAmigaSpeedTest,          longbowPatchAmigaSpeedTest },
5703 	SCI_SIGNATUREENTRY_TERMINATOR
5704 };
5705 
5706 // ===========================================================================
5707 // Leisure Suit Larry 1 (Spanish)
5708 //
5709 // It seems originally the Spanish version of Larry 1 used some beta code at
5710 // least for the man wearing a barrel, who walks around in front of the casino.
5711 // The script inside the resource files even uses a class, that does not exist
5712 // inside those resource files, which causes a hard error.
5713 // The patch files included with the Spanish version (300.scr,300.tex, 927.scr)
5714 // add this class, but at least inside ScummVM a write to a non-existent selector
5715 // happens right after the player tries to buy an apple from that man.
5716 //
5717 // In the original English releases (2.0+2.1) this was handled differently.
5718 // Which is why this script patch changes that code to work just like in the English release.
5719 //
5720 // Attention: for at least some release of this game, view 302 (man wearing a barrel) is fully
5721 //            broken! Which also causes a crash. The original interpreter crashes as well.
5722 //            The only way to fix this is to dump that view from another release of Larry 1
5723 //            and then use the view patch file on this release.
5724 //
5725 // Applies to at least: Spanish floppy
5726 // Responsible method: sBuyApple::changeScript(2)
5727 // Fixes bug: #10240
5728 static const uint16 larry1SignatureBuyApple[] = {
5729 	// end of state 0
5730 	0x35, 0x01,                      // ldi 01
5731 	0x65, 0x10,                      // aTop cycles
5732 	0x32, SIG_UINT16(0x0248),        // jmp [ret]
5733 	0x3c,                            // dup
5734 	0x35, 0x01,                      // ldi 01
5735 	0x1a,                            // eq?
5736 	0x30, SIG_UINT16(0x0007),        // bnt [step 2 check]
5737 	// state 1 code
5738 	0x35, 0x01,                      // ldi 01
5739 	0x65, 0x10,                      // aTop cycles
5740 	0x32, SIG_UINT16(0x023a),        // jmp [ret]
5741 	0x3c,                            // dup
5742 	0x35, 0x02,                      // ldi 02
5743 	0x1a,                            // eq?
5744 	0x30, SIG_UINT16(0x0036),        // bnt [step 3 check]
5745 	// state 2 code
5746 	0x35, 0x02,                      // ldi 02
5747 	0x38, SIG_UINT16(0x0091),        // pushi setCycle
5748 	0x78,                            // push1
5749 	0x51, 0x18,                      // class Walk
5750 	0x36,                            // push
5751 	0x38, SIG_UINT16(0x0126),        // pushi setAvoider
5752 	0x78,                            // push1
5753 	0x51, SIG_ADDTOOFFSET(+1),       // class PAvoider (original 0x25, w/ patch file 0x6d)
5754 	0x36,                            // push
5755 	0x38, SIG_UINT16(0x0116),        // pushi setMotion
5756 	SIG_MAGICDWORD,
5757 	0x39, 0x04,                      // pushi 04
5758 	0x51, 0x24,                      // class PolyPath
5759 	0x36,                            // push
5760 	0x39, 0x04,                      // pushi 04
5761 	0x76,                            // push0
5762 	0x72, SIG_UINT16(0x0f4e),        // lofsa aAppleMan
5763 	0x4a, 0x04,                      // send 04
5764 	0x36,                            // push
5765 	0x35, 0x1d,                      // ldi 1Dh
5766 	0x02,                            // add
5767 	0x36,                            // push
5768 	0x39, 0x03,                      // pushi 03
5769 	0x76,                            // push0
5770 	0x72, SIG_UINT16(0x0f4e),        // lofsa aAppleMan
5771 	0x4a, 0x04,                      // send 04
5772 	0x36,                            // push
5773 	0x7c,                            // pushSelf
5774 	0x81, 0x00,                      // lag global[0]
5775 	0x4a, 0x18,                      // send 18h
5776 	0x32, SIG_UINT16(0x01fd),        // jmp [ret]
5777 	SIG_END
5778 };
5779 
5780 static const uint16 larry1PatchBuyApple[] = {
5781 	PATCH_ADDTOOFFSET(+11),
5782 	0x2f, 0xf3,                        // bt [jump to end of step 1 code], saves 8 bytes
5783 	0x3c,                              // dup
5784 	0x35, 0x02,                        // ldi 02
5785 	0x1a,                              // eq?
5786 	0x31, 0x3f,                        // bnt [step 3 check]
5787 	0x38, PATCH_UINT16(0x00e1),        // pushi distanceTo
5788 	0x78,                              // push1
5789 	0x72, PATCH_UINT16(0x0f4e),        // lofsa sAppleMan
5790 	0x36,                              // push
5791 	0x81, 0x00,                        // lag global[0]
5792 	0x4a, 0x06,                        // send 06
5793 	0x36,                              // push
5794 	0x35, 0x1e,                        // ldi 1Eh
5795 	0x1e,                              // gt?
5796 	0x31, 0xdb,                        // bnt [jump to end of step 1 code]
5797 	0x38, PATCH_SELECTOR16(setCycle),  // pushi setCycle
5798 	0x78,                              // push1
5799 	0x51, 0x18,                        // class Walk
5800 	0x36,                              // push
5801 	0x38, PATCH_SELECTOR16(setMotion), // pushi setMotion
5802 	0x39, 0x04,                        // pushi 04
5803 	0x51, 0x24,                        // class PolyPath
5804 	0x36,                              // push
5805 	0x39, 0x04,                        // pushi 04
5806 	0x76,                              // push0
5807 	0x72, PATCH_UINT16(0x0f4e),        // lofsa aAppleMan
5808 	0x4a, 0x04,                        // send 04
5809 	0x36,                              // push
5810 	0x35, 0x1d,                        // ldi 1Dh
5811 	0x02,                              // add
5812 	0x36,                              // push
5813 	0x39, 0x03,                        // pushi 03
5814 	0x76,                              // push0
5815 	0x72, PATCH_UINT16(0x0f4e),        // lofsa aAppleMan
5816 	0x4a, 0x04,                        // send 04
5817 	0x36,                              // push
5818 	0x7c,                              // pushSelf
5819 	0x81, 0x00,                        // lag global[0]
5820 	0x4a, 0x12,                        // send 12h
5821 	PATCH_END
5822 };
5823 
5824 //          script, description,                               signature                patch
5825 static const SciScriptPatcherEntry larry1Signatures[] = {
5826 	{  true,   300, "Spanish: buy apple from barrel man",    1, larry1SignatureBuyApple, larry1PatchBuyApple },
5827 	SCI_SIGNATUREENTRY_TERMINATOR
5828 };
5829 
5830 // ===========================================================================
5831 // Leisure Suit Larry 2
5832 // On the plane, Larry is able to wear the parachute. This grants 4 points.
5833 // In early versions of LSL2, it was possible to get "unlimited" points by
5834 //  simply wearing it multiple times.
5835 // They fixed it in later versions by remembering, if the parachute was already
5836 //  used before.
5837 // But instead of adding it properly, it seems they hacked the script / forgot
5838 //  to replace script 0 as well, which holds information about how many global
5839 //  variables are allocated at the start of the game.
5840 // The script tries to read an out-of-bounds global variable, which somewhat
5841 //  "worked" in SSCI, but ScummVM/SCI doesn't allow that.
5842 // That's why those points weren't granted here at all.
5843 // We patch to use global[5a], which seems to be unused in the whole game.
5844 // Applies to at least: English floppy
5845 // Responsible method: rm63Script::handleEvent
5846 // Fixes bug: #6346
5847 static const uint16 larry2SignatureWearParachutePoints[] = {
5848 	0x35, 0x01,                      // ldi 01
5849 	0xa1, SIG_MAGICDWORD, 0x8e,      // sag global[8e]
5850 	0x80, SIG_UINT16(0x01e0),        // lag global[1e0]
5851 	0x18,                            // not
5852 	0x30, SIG_UINT16(0x000f),        // bnt [don't give points]
5853 	0x35, 0x01,                      // ldi 01
5854 	0xa0, 0xe0, 0x01,                // sag global[1e0]
5855 	SIG_END
5856 };
5857 
5858 static const uint16 larry2PatchWearParachutePoints[] = {
5859 	PATCH_ADDTOOFFSET(+4),
5860 	0x80, PATCH_UINT16(0x005a),      // lag global[5a]
5861 	PATCH_ADDTOOFFSET(+6),
5862 	0xa0, PATCH_UINT16(0x005a),      // sag global[5a]
5863 	PATCH_END
5864 };
5865 
5866 //          script, description,                                      signature                           patch
5867 static const SciScriptPatcherEntry larry2Signatures[] = {
5868 	{  true,    63, "plane: no points for wearing parachute",      1, larry2SignatureWearParachutePoints, larry2PatchWearParachutePoints },
5869 	SCI_SIGNATUREENTRY_TERMINATOR
5870 };
5871 
5872 // ===========================================================================
5873 // Leisure Suit Larry 5
5874 // In Miami the player can call the green card telephone number and get
5875 //  green card including limo at the same time in the English 1.000 PC release.
5876 // This results later in a broken game in case the player doesn't read
5877 //  the second telephone number for the actual limousine service, because
5878 //  in that case it's impossible for the player to get back to the airport.
5879 //
5880 // We disable the code, that is responsible to make the limo arrive.
5881 //
5882 // This bug was fixed in the European (dual language) versions of the game.
5883 //
5884 // Applies to at least: English PC floppy (1.000)
5885 // Responsible method: sPhone::changeState(40)
5886 static const uint16 larry5SignatureGreenCardLimoBug[] = {
5887 	0x7a,                               // push2
5888 	SIG_MAGICDWORD,
5889 	0x39, 0x07,                         // pushi 07
5890 	0x39, 0x0c,                         // pushi 0Ch
5891 	0x45, 0x0a, 0x04,                   // callb [export 10 of script 0], 04
5892 	0x78,                               // push1
5893 	0x39, 0x26,                         // pushi 26h (limo arrived flag)
5894 	0x45, 0x07, 0x02,                   // callb [export 7 of script 0], 02 (sets flag)
5895 	SIG_END
5896 };
5897 
5898 static const uint16 larry5PatchGreenCardLimoBug[] = {
5899 	PATCH_ADDTOOFFSET(+8),
5900 	0x34, PATCH_UINT16(0x0000),         // ldi 0000 (dummy)
5901 	0x34, PATCH_UINT16(0x0000),         // ldi 0000 (dummy)
5902 	PATCH_END
5903 };
5904 
5905 // In one of the conversations near the end (to be exact - room 380 and the text
5906 //  about using champagne on Reverse Biaz - only used when you actually did that
5907 //  in the game), the German text is too large, causing the textbox to get too large.
5908 // Because of that the talking head of Patti is drawn over the textbox. A translation oversight.
5909 // Applies to at least: German floppy
5910 // Responsible method: none, position of talker object on screen needs to get modified
5911 static const uint16 larry5SignatureGermanEndingPattiTalker[] = {
5912 	SIG_MAGICDWORD,
5913 	SIG_UINT16(0x006e),                 // object pattiTalker::x (110)
5914 	SIG_UINT16(0x00b4),                 // object pattiTalker::y (180)
5915 	SIG_ADDTOOFFSET(+469),              // verify that it's really the German version
5916 	0x59, 0x6f, 0x75,                   // (object name) "You"
5917 	0x23, 0x47, 0x44, 0x75,             // "#GDu"
5918 	SIG_END
5919 };
5920 
5921 static const uint16 larry5PatchGermanEndingPattiTalker[] = {
5922 	PATCH_UINT16(0x005a),               // object pattiTalker::x (90)
5923 	PATCH_END
5924 };
5925 
5926 //          script, description,                                      signature                               patch
5927 static const SciScriptPatcherEntry larry5Signatures[] = {
5928 	{  true,   280, "English-only: fix green card limo bug",       1, larry5SignatureGreenCardLimoBug,        larry5PatchGreenCardLimoBug },
5929 	{  true,   380, "German-only: Enlarge Patti Textbox",          1, larry5SignatureGermanEndingPattiTalker, larry5PatchGermanEndingPattiTalker },
5930 	SCI_SIGNATUREENTRY_TERMINATOR
5931 };
5932 
5933 // ===========================================================================
5934 // This is called on every death dialog. Problem is at least the German
5935 //  version of lsl6 gets title text that is far too long for the
5936 //  available temp space resulting in temp space corruption. This patch
5937 //  moves the title text around, so this overflow doesn't happen anymore. We
5938 //  would otherwise get a crash calling for invalid views (this happens of
5939 //  course also in sierra sci).
5940 // Applies to at least: German PC-CD
5941 // Responsible method: unknown
5942 static const uint16 larry6SignatureDeathDialog[] = {
5943 	SIG_MAGICDWORD,
5944 	0x3e, SIG_UINT16(0x0133),        // link 0133 (offset 0x20)
5945 	0x35, 0xff,                      // ldi ff
5946 	0xa3, 0x00,                      // sal local[0]
5947 	SIG_ADDTOOFFSET(+680),           // ...
5948 	0x8f, 0x01,                      // lsp param[1] (offset 0x2cf)
5949 	0x7a,                            // push2
5950 	0x5a, SIG_UINT16(0x0004), SIG_UINT16(0x010e), // lea temp[010e]
5951 	0x36,                            // push
5952 	0x43, 0x7c, 0x0e,                // callk Message[7c], 0e
5953 	SIG_ADDTOOFFSET(+90),            // ...
5954 	0x38, SIG_UINT16(0x00d6),        // pushi 00d6 (offset 0x335)
5955 	0x78,                            // push1
5956 	0x5a, SIG_UINT16(0x0004), SIG_UINT16(0x010e), // lea temp[010e]
5957 	0x36,                            // push
5958 	SIG_ADDTOOFFSET(+76),            // ...
5959 	0x38, SIG_UINT16(0x00cd),        // pushi 00cd (offset 0x38b)
5960 	0x39, 0x03,                      // pushi 03
5961 	0x5a, SIG_UINT16(0x0004), SIG_UINT16(0x010e), // lea temp[010e]
5962 	0x36,
5963 	SIG_END
5964 };
5965 
5966 static const uint16 larry6PatchDeathDialog[] = {
5967 	0x3e, 0x00, 0x02,                                 // link 0200
5968 	PATCH_ADDTOOFFSET(+687),
5969 	0x5a, PATCH_UINT16(0x0004), PATCH_UINT16(0x0140), // lea temp[0140]
5970 	PATCH_ADDTOOFFSET(+98),
5971 	0x5a, PATCH_UINT16(0x0004), PATCH_UINT16(0x0140), // lea temp[0140]
5972 	PATCH_ADDTOOFFSET(+82),
5973 	0x5a, PATCH_UINT16(0x0004), PATCH_UINT16(0x0140), // lea temp[0140]
5974 	PATCH_END
5975 };
5976 
5977 //          script, description,                                      signature                   patch
5978 static const SciScriptPatcherEntry larry6Signatures[] = {
5979 	{  true,    82, "death dialog memory corruption",              1, larry6SignatureDeathDialog, larry6PatchDeathDialog },
5980 	SCI_SIGNATUREENTRY_TERMINATOR
5981 };
5982 
5983 #ifdef ENABLE_SCI32
5984 #pragma mark -
5985 #pragma mark Leisure Suit Larry 6 Hires
5986 
5987 // When entering room 270 (diving board) from room 230, a typo in the game
5988 // script means that `setScale` is called accidentally instead of `setScaler`.
5989 // In SSCI this did not do much because the first argument happened to be
5990 // smaller than the y-position of `ego`, but in ScummVM the first argument is
5991 // larger and so a debug message "y value less than vanishingY" is displayed.
5992 static const uint16 larry6HiresSetScaleSignature[] = {
5993 	SIG_MAGICDWORD,
5994 	0x38, SIG_SELECTOR16(setScale), // pushi setScale ($14b)
5995 	0x38, SIG_UINT16(0x0005),       // pushi 5
5996 	0x51, 0x2c,                     // class Scaler
5997 	SIG_END
5998 };
5999 
6000 static const uint16 larry6HiresSetScalePatch[] = {
6001 	0x38, PATCH_SELECTOR16(setScaler), // pushi setScaler ($14f)
6002 	PATCH_END
6003 };
6004 
6005 // The init code that runs when LSL6hires starts up unconditionally resets the
6006 // master music volume to 12 (and the volume dial to 11), but the game should
6007 // always use the volume stored in ScummVM.
6008 // Applies to at least: English CD
6009 // Fixes bug: #9700
6010 static const uint16 larry6HiresVolumeResetSignature[] = {
6011 	SIG_MAGICDWORD,
6012 	0x35, 0x0b,                         // ldi $0b
6013 	0xa1, 0xc2,                         // sag global[$c2]
6014 	SIG_END
6015 };
6016 
6017 static const uint16 larry6HiresVolumeResetPatch[] = {
6018 	0x32, PATCH_UINT16(0x0001),         // jmp 1 [past volume change]
6019 	PATCH_END
6020 };
6021 
6022 //          script, description,                                      signature                         patch
6023 static const SciScriptPatcherEntry larry6HiresSignatures[] = {
6024 	{  true,    71, "disable volume reset on startup (1/2)",       1, sci2VolumeResetSignature,         sci2VolumeResetPatch },
6025 	{  true,    71, "disable volume reset on startup (2/2)",       1, larry6HiresVolumeResetSignature,  larry6HiresVolumeResetPatch },
6026 	{  true,   270, "fix incorrect setScale call",                 1, larry6HiresSetScaleSignature,     larry6HiresSetScalePatch },
6027 	{  true, 64908, "disable video benchmarking",                  1, sci2BenchmarkSignature,           sci2BenchmarkPatch },
6028 	{  true, 64990, "increase number of save games (1/2)",         1, sci2NumSavesSignature1,           sci2NumSavesPatch1 },
6029 	{  true, 64990, "increase number of save games (2/2)",         1, sci2NumSavesSignature2,           sci2NumSavesPatch2 },
6030 	{  true, 64990, "disable change directory button",             1, sci2ChangeDirSignature,           sci2ChangeDirPatch },
6031 	SCI_SIGNATUREENTRY_TERMINATOR
6032 };
6033 
6034 #pragma mark -
6035 #pragma mark Leisure Suit Larry 7
6036 
6037 // The init code that runs when LSL7 starts up unconditionally resets the audio
6038 // volumes to defaults, but the game should always use the volume stored in
6039 // ScummVM. This patch is basically identical to the patch for Torin, except
6040 // that they left line numbers in the LSL7 scripts and changed the music volume.
6041 // Applies to at least: English CD
6042 static const uint16 larry7VolumeResetSignature1[] = {
6043 	SIG_MAGICDWORD,
6044 	0x35, 0x41,                         // ldi $41
6045 	0xa1, 0xe3,                         // sag global[$e3] (music volume)
6046 	0x7e, SIG_ADDTOOFFSET(+2),          // (line whatever)
6047 	0x35, 0x3c,                         // ldi $3c
6048 	0xa1, 0xe4,                         // sag global[$e4] (sfx volume)
6049 	0x7e, SIG_ADDTOOFFSET(+2),          // (line whatever)
6050 	0x35, 0x64,                         // ldi $64
6051 	0xa1, 0xe5,                         // sag global[$e5] (speech volume)
6052 	SIG_END
6053 };
6054 
6055 static const uint16 larry7VolumeResetPatch1[] = {
6056 	0x33, 0x10,                         // jmp [past volume resets]
6057 	PATCH_END
6058 };
6059 
6060 // The init code that runs when LSL7 starts up unconditionally resets the
6061 // audio volumes to values stored in larry7.prf, but the game should always use
6062 // the volume stored in ScummVM. This patch is basically identical to the patch
6063 // for Torin, except that they left line numbers in the LSL7 scripts.
6064 // Applies to at least: English CD
6065 static const uint16 larry7VolumeResetSignature2[] = {
6066 	SIG_MAGICDWORD,
6067 	0x38, SIG_SELECTOR16(readWord),     // pushi readWord
6068 	0x76,                               // push0
6069 	SIG_ADDTOOFFSET(+6),                // ...
6070 	0xa1, 0xe3,                         // sag global[$e3] (music volume)
6071 	SIG_ADDTOOFFSET(+3),                // (line whatever)
6072 	SIG_ADDTOOFFSET(+10),               // ...
6073 	0xa1, 0xe4,                         // sag global[$e4] (sfx volume)
6074 	SIG_ADDTOOFFSET(+3),                // (line whatever)
6075 	SIG_ADDTOOFFSET(+10),               // ...
6076 	0xa1, 0xe5,                         // sag global[$e5] (speech volume)
6077 	SIG_END
6078 };
6079 
6080 static const uint16 larry7VolumeResetPatch2[] = {
6081 	PATCH_ADDTOOFFSET(+10),
6082 	0x18, 0x18,                         // (waste bytes)
6083 	PATCH_ADDTOOFFSET(+3),              // (line whatever)
6084 	PATCH_ADDTOOFFSET(+10),             // ...
6085 	0x18, 0x18,                         // (waste bytes)
6086 	PATCH_ADDTOOFFSET(+3),              // (line whatever)
6087 	PATCH_ADDTOOFFSET(+10),             // ...
6088 	0x18, 0x18,                         // (waste bytes)
6089 	PATCH_END
6090 };
6091 
6092 // In room 540 of Leisure Suit Larry 7, when using the cheese maker,
6093 // `soMakeCheese::changeState(6)` incorrectly pushes `self` as the end cel
6094 // instead of a cel number to the End cycler. In SSCI, this bad argument would
6095 // get corrected down to the final cel in the loop by `CycleCueList::init`, but
6096 // because ScummVM currently always sorts numbers higher than objects, the
6097 // comparison fails and the cel number is not corrected, so the cycler never
6098 // calls back and the game softlocks.
6099 // Here, we fix the call so a proper cel number is given for the second argument
6100 // instead of a bogus object pointer.
6101 //
6102 // Applies to at least: English PC-CD, German PC-CD
6103 static const uint16 larry7MakeCheeseCyclerSignature[] = {
6104 	0x38, SIG_UINT16(0x04), // pushi 4
6105 	0x51, 0xc4,             // class End
6106 	0x36,                   // push
6107 	SIG_MAGICDWORD,
6108 	0x7c,                   // pushSelf
6109 	0x39, 0x04,             // pushi 4
6110 	0x7c,                   // pushSelf
6111 	SIG_END
6112 };
6113 
6114 static const uint16 larry7MakeCheeseCyclerPatch[] = {
6115 	0x39, 0x04, // pushi 4 - save 1 byte
6116 	0x51, 0xc4, // class End
6117 	0x36,       // push
6118 	0x7c,       // pushSelf
6119 	0x39, 0x04, // pushi 4
6120 	0x39, 0x10, // pushi $10 (last cel of view 54007, loop 0)
6121 	PATCH_END
6122 };
6123 
6124 // During the cheese maker cutscene, `soMakeCheese::changeState(2)` sets the
6125 // priority of ego to 500 to draw him over the cheese maker, but this is also
6126 // above the guillotine (view 54000, cel 7, priority 400), so ego gets
6127 // incorrectly drawn on top of the guillotine as well. The cheese maker has a
6128 // priority of 373, so use priority 374 instead of 500.
6129 // Applies to at least: English PC-CD, German PC-CD
6130 // Responsible method: soMakeCheese::changeState(2) in script 540
6131 static const uint16 larry7MakeCheesePrioritySignature[] = {
6132 	0x38, SIG_SELECTOR16(setPri),    // pushi setPri
6133 	SIG_MAGICDWORD,
6134 	0x78,                            // push1
6135 	0x38, SIG_UINT16(0x01f4),        // pushi 500
6136 	SIG_END
6137 };
6138 
6139 static const uint16 larry7MakeCheesePriorityPatch[] = {
6140 	PATCH_ADDTOOFFSET(+4),           // pushi setPri, push1
6141 	0x38, PATCH_UINT16(0x0176),      // pushi 374
6142 	PATCH_END
6143 };
6144 
6145 // LSL7 tries to reset the message type twice at startup, first with a default
6146 // value in script 0, then with a stored value from larry7.prf (if that file
6147 // exists) or the same default value (if it does not) in script 64000. Since
6148 // message type sync relies on the game only setting this value once at startup,
6149 // we must stop the second attempt or the value from ScummVM will be
6150 // overwritten.
6151 // Applies to at least: English CD
6152 static const uint16 larry7MessageTypeResetSignature[] = {
6153 	SIG_MAGICDWORD,
6154 	0x35, 0x02, // ldi 2
6155 	0xa1, 0x5a, // sag global[$5a]
6156 	SIG_END
6157 };
6158 
6159 static const uint16 larry7MessageTypeResetPatch[] = {
6160 	0x33, 0x02, // jmp [past reset]
6161 	PATCH_END
6162 };
6163 
6164 //          script, description,                                signature                           patch
6165 static const SciScriptPatcherEntry larry7Signatures[] = {
6166 	{  true,     0, "disable message type reset on startup", 1, larry7MessageTypeResetSignature,    larry7MessageTypeResetPatch },
6167 	{  true,   540, "fix make cheese cutscene (cycler)",     1, larry7MakeCheeseCyclerSignature,    larry7MakeCheeseCyclerPatch },
6168 	{  true,   540, "fix make cheese cutscene (priority)",   1, larry7MakeCheesePrioritySignature,  larry7MakeCheesePriorityPatch },
6169 	{  true, 64000, "disable volume reset on startup (1/2)", 1, larry7VolumeResetSignature1,        larry7VolumeResetPatch1 },
6170 	{  true, 64000, "disable volume reset on startup (2/2)", 1, larry7VolumeResetSignature2,        larry7VolumeResetPatch2 },
6171 	{  true, 64866, "increase number of save games",         1, torinLarry7NumSavesSignature,       torinLarry7NumSavesPatch },
6172 	SCI_SIGNATUREENTRY_TERMINATOR
6173 };
6174 
6175 #endif
6176 
6177 // ===========================================================================
6178 // Laura Bow 1 - Colonel's Bequest
6179 //
6180 // This is basically just a broken easter egg in Colonel's Bequest.
6181 // A plane can show up in room 4, but that only happens really rarely.
6182 // Anyway the Sierra developer seems to have just entered the wrong loop,
6183 // which is why the statue view is used instead (loop 0).
6184 // We fix it to use the correct loop.
6185 //
6186 // This is only broken in the PC version. It was fixed for Amiga + Atari ST.
6187 //
6188 // Credits to OmerMor, for finding it.
6189 //
6190 // Applies to at least: English PC Floppy
6191 // Responsible method: room4::init
6192 static const uint16 laurabow1SignatureEasterEggViewFix[] = {
6193 	0x78,                               // push1
6194 	0x76,                               // push0
6195 	SIG_MAGICDWORD,
6196 	0x38, SIG_SELECTOR16(setLoop),      // pushi setLoop
6197 	0x78,                               // push1
6198 	0x39, 0x03,                         // pushi 3 (loop 3, view only has 3 loops)
6199 	SIG_END
6200 };
6201 
6202 static const uint16 laurabow1PatchEasterEggViewFix[] = {
6203 	PATCH_ADDTOOFFSET(+7),
6204 	0x02,                               // (change loop to 2)
6205 	PATCH_END
6206 };
6207 
6208 // When oiling the armor or opening the visor of the armor, the scripts first
6209 //  check if Laura/ego is near the armor and if she is not, they will move her
6210 //  to the armor. After that, further code is executed.
6211 //
6212 // The current location is checked by a ego::inRect() call.
6213 //
6214 // The given rect for the inRect call inside openVisor::changeState was made
6215 //  larger for Atari ST/Amiga versions. We change the PC version to use the
6216 //  same rect.
6217 //
6218 // Additionally, the coordinate that Laura is moved to (152, 107) may not be
6219 //  reachable depending on where Laura was when "use oil on helmet of armor"
6220 //  or "open visor of armor" got entered. Bad coordinates such as (82, 110),
6221 //  cause collisions and effectively an endless loop, effectively freezing the
6222 //  game. The user is only able to restore a previous game.
6223 //
6224 //  We change the destination coordinate to (152, 110), which seems to be
6225 //   reachable all the time.
6226 //
6227 // The following patch fixes the rect for the PC version of the game.
6228 //
6229 // Applies to at least: English PC Floppy
6230 // Responsible method: openVisor::changeState (script 37)
6231 // Fixes bug: #7119
6232 static const uint16 laurabow1SignatureArmorOpenVisorFix[] = {
6233 	0x39, 0x04,                         // pushi 04
6234 	SIG_MAGICDWORD,
6235 	0x39, 0x6a,                         // pushi 6a (106d)
6236 	0x38, SIG_UINT16(0x0096),           // pushi 0096 (150d)
6237 	0x39, 0x6c,                         // pushi 6c (108d)
6238 	0x38, SIG_UINT16(0x0098),           // pushi 0098 (152d)
6239 	SIG_END
6240 };
6241 
6242 static const uint16 laurabow1PatchArmorOpenVisorFix[] = {
6243 	PATCH_ADDTOOFFSET(+2),
6244 	0x39, 0x68,                         // pushi 68 (104d)   (-2)
6245 	0x38, PATCH_UINT16(0x0094),         // pushi 0094 (148d) (-2)
6246 	0x39, 0x6f,                         // pushi 6f (111d)   (+3)
6247 	0x38, PATCH_UINT16(0x009a),         // pushi 009a (154d) (+2)
6248 	PATCH_END
6249 };
6250 
6251 // This here fixes the destination coordinate (exact details are above).
6252 //
6253 // Applies to at least: English PC Floppy, English Atari ST Floppy, English Amiga Floppy
6254 // Responsible method: openVisor::changeState, oiling::changeState (script 37)
6255 // Fixes bug: #7119
6256 static const uint16 laurabow1SignatureArmorMoveToFix[] = {
6257 	SIG_MAGICDWORD,
6258 	0x36,                               // push
6259 	0x39, 0x6b,                         // pushi 6B (107d)
6260 	0x38, SIG_UINT16(0x0098),           // pushi 98 (152d)
6261 	0x7c,                               // pushSelf
6262 	0x81, 0x00,                         // lag global[0]
6263 	SIG_END
6264 };
6265 
6266 static const uint16 laurabow1PatchArmorMoveToFix[] = {
6267 	PATCH_ADDTOOFFSET(+1),
6268 	0x39, 0x6e,                         // pushi 6E (110d) - adjust x, so that no collision can occur anymore
6269 	PATCH_END
6270 };
6271 
6272 // In some cases like for example when the player oils the arm of the armor,
6273 // command input stays disabled, even when the player exits fast enough, so
6274 // that Laura doesn't die.
6275 //
6276 // This is caused by the scripts only enabling control (directional movement),
6277 // but do not enable command input as well.
6278 //
6279 // This bug also happens, when using the original interpreter. It was fixed for
6280 // the Atari ST + Amiga versions of the game.
6281 //
6282 // Applies to at least: English PC Floppy
6283 // Responsible method: 2nd subroutine in script 37, called by oiling::changeState(7)
6284 // Fixes bug: #7154
6285 static const uint16 laurabow1SignatureArmorOilingArmFix[] = {
6286 	0x38, SIG_UINT16(0x0089),           // pushi 89h
6287 	0x76,                               // push0
6288 	SIG_MAGICDWORD,
6289 	0x72, SIG_UINT16(0x1a5c),           // lofsa "Can" - offsets are not skipped to make sure only the PC version gets patched
6290 	0x4a, 0x04,                         // send 04
6291 	0x38, SIG_UINT16(0x0089),           // pushi 89h
6292 	0x76,                               // push0
6293 	0x72, SIG_UINT16(0x19a1),           // lofsa "Visor"
6294 	0x4a, 0x04,                         // send 04
6295 	0x38, SIG_UINT16(0x0089),           // pushi 89h
6296 	0x76,                               // push0
6297 	0x72, SIG_UINT16(0x194a),           // lofsa "note"
6298 	0x4a, 0x04,                         // send 04
6299 	0x38, SIG_UINT16(0x0089),           // pushi 89h
6300 	0x76,                               // push0
6301 	0x72, SIG_UINT16(0x18f3),           // lofsa "valve"
6302 	0x4a, 0x04,                         // send 04
6303 	0x8b, 0x34,                         // lsl local[34h]
6304 	0x35, 0x02,                         // ldi 02
6305 	0x1c,                               // ne?
6306 	0x30, SIG_UINT16(0x0014),           // bnt [to ret]
6307 	0x8b, 0x34,                         // lsl local[34h]
6308 	0x35, 0x05,                         // ldi 05
6309 	0x1c,                               // ne?
6310 	0x30, SIG_UINT16(0x000c),           // bnt [to ret]
6311 	0x8b, 0x34,                         // lsl local[34h]
6312 	0x35, 0x06,                         // ldi 06
6313 	0x1c,                               // ne?
6314 	0x30, SIG_UINT16(0x0004),           // bnt [to ret]
6315 	// followed by code to call script 0 export to re-enable controls and call setMotion
6316 	SIG_END
6317 };
6318 
6319 static const uint16 laurabow1PatchArmorOilingArmFix[] = {
6320 	PATCH_ADDTOOFFSET(+3),              // skip over pushi 89h
6321 	0x3c,                               // dup
6322 	0x3c,                               // dup
6323 	0x3c,                               // dup
6324 	// saves a total of 6 bytes
6325 	0x76,                               // push0
6326 	0x72, PATCH_UINT16(0x1a59),         // lofsa "Can"
6327 	0x4a, 0x04,                         // send 04
6328 	0x76,                               // push0
6329 	0x72, PATCH_UINT16(0x19a1),         // lofsa "Visor"
6330 	0x4a, 0x04,                         // send 04
6331 	0x76,                               // push0
6332 	0x72, PATCH_UINT16(0x194d),         // lofsa "note"
6333 	0x4a, 0x04,                         // send 04
6334 	0x76,                               // push0
6335 	0x72, PATCH_UINT16(0x18f9),         // lofsa "valve" 18f3
6336 	0x4a, 0x04,                         // send 04
6337 	// new code to enable input as well, needs 9 spare bytes
6338 	0x38, PATCH_UINT16(0x00e2),         // pushi canInput
6339 	0x78,                               // push1
6340 	0x78,                               // push1
6341 	0x51, 0x2b,                         // class User
6342 	0x4a, 0x06,                         // send 06 -> call User::canInput(1)
6343 	// original code, but changed a bit to save some more bytes
6344 	0x8b, 0x34,                         // lsl local[34h]
6345 	0x35, 0x02,                         // ldi 02
6346 	0x04,                               // sub
6347 	0x31, 0x12,                         // bnt [to ret]
6348 	0x36,                               // push
6349 	0x35, 0x03,                         // ldi 03
6350 	0x04,                               // sub
6351 	0x31, 0x0c,                         // bnt [to ret]
6352 	0x78,                               // push1
6353 	0x1a,                               // eq?
6354 	0x2f, 0x08,                         // bt [to ret]
6355 	// saves 7 bytes, we only need 3, so waste 4 bytes
6356 	0x35, 0x00,                         // ldi 0
6357 	0x35, 0x00,                         // ldi 0
6358 	PATCH_END
6359 };
6360 
6361 // Jeeves lights the chapel candles (room 58) in act 2 but they don't stay
6362 //  lit when re-entering until the next act. This is due to Room58:init
6363 //  incorrectly testing the global variable that tracks Jeeves' act 2 state.
6364 //
6365 // We fix this by changing the test from if global[155] equals 11, which it
6366 //  never does, to if it's greater than 11. The global is set to 12 in
6367 //  lightCandles:changeState(11) and it continues to increment as Jeeves'
6368 //  chore sequence progresses, ending with 17.
6369 //
6370 // Applies to: DOS, Amiga, Atari ST
6371 // Responsible method: Room58:init
6372 // Fixes bug: #10743
6373 static const uint16 laurabow1SignatureChapelCandlesPersistence[] = {
6374 	SIG_MAGICDWORD,
6375 	0x89, 0x9b,                         // lsg global[155] [ Jeeves' act 2 state ]
6376 	0x35, 0x0b,                         // ldi b
6377 	0x1a,                               // eq?
6378 	SIG_END
6379 };
6380 
6381 static const uint16 laurabow1PatchChapelCandlesPersistence[] = {
6382 	PATCH_ADDTOOFFSET(+4),
6383 	0x1e,                               // gt?
6384 	PATCH_END
6385 };
6386 
6387 // LB1 DOS doesn't acknowledge Lillian's presence in room 44 when she's sitting
6388 //  on the bed in act 4. Look, talk, etc respond that she's not there.
6389 //  This is due to not setting global[195] which tracks who is in the room.
6390 //  We fix this by setting the global as Amiga and Atari ST versions do.
6391 //
6392 // Applies to: DOS only
6393 // Responsible method: Room44:init
6394 // Fixes bug: #10742
6395 static const uint16 laurabow1SignatureLillianBedFix[] = {
6396 	SIG_MAGICDWORD,
6397 	0x72, SIG_UINT16(0x10f8),           // lofsa suit2 [ only matches DOS version ]
6398 	0x4a, 0x14,                         // send 14
6399 	SIG_ADDTOOFFSET(+8),
6400 	0x89, 0x76,                         // lsg global[118]
6401 	0x35, 0x02,                         // ldi 2
6402 	0x12,                               // and
6403 	0x30, SIG_UINT16(0x000d),           // bnt d [ haven't seen Lillian in study ]
6404 	0x35, 0x01,                         // ldi 1
6405 	SIG_END
6406 };
6407 
6408 static const uint16 laurabow1PatchLillianBedFix[] = {
6409 	PATCH_ADDTOOFFSET(+13),
6410 	0x81, 0x76,                         // lag global[118]
6411 	0x7a,                               // push2
6412 	0x12,                               // and
6413 	0x31, 0x0f,                         // bnt f [ haven't seen Lillian in study ]
6414 	0x35, 0x20,                         // ldi 20 [ Lillian ]
6415 	0xa1, 0xc3,                         // sag global[195] [ set Lillian as in the room ]
6416 	PATCH_END
6417 };
6418 
6419 // When you tell Lilly about Gertie in room 35, Lilly will then walk to the
6420 // left and off the screen. If Laura (ego) is in the way, the whole game will
6421 // basically block and you won't be able to do anything except saving or
6422 // restoring the game.
6423 //
6424 // If this happened already, the player can enter "send Lillian ignoreActors 1"
6425 // inside the debugger to fix this situation.
6426 //
6427 // This issue is very difficult to solve, because Lilly also walks diagonally
6428 // after walking to the left right under the kitchen table. This means that
6429 // even if we added a few more rectangle checks, there could still be spots,
6430 // where the game would block.
6431 //
6432 // Also the mover "PathOut" is used for Lillian instead of the regular
6433 // "MoveTo", which would avoid other actors by itself.
6434 //
6435 // So instead we set Lilly to ignore other actors during that cutscene, which
6436 // is the least invasive solution.
6437 //
6438 // Applies to at least: English PC Floppy, English Amiga Floppy, English Atari ST Floppy
6439 // Responsible method: goSee::changeState(1) in script 236
6440 // Fixes bug: (happened during GOG Let's Play)
6441 static const uint16 laurabow1SignatureTellLillyAboutGerieBlockingFix1[] = {
6442 	0x7a,                               // puah2
6443 	SIG_MAGICDWORD,
6444 	0x38, SIG_UINT16(0x00c1),           // pushi 00C1h
6445 	0x38, SIG_UINT16(0x008f),           // pushi 008Fh
6446 	0x38, SIG_SELECTOR16(ignoreActors), // pushi ignoreActors
6447 	0x78,                               // push1
6448 	0x76,                               // push0
6449 	SIG_END
6450 };
6451 
6452 static const uint16 laurabow1PatchTellLillyAboutGertieBlockingFix1[] = {
6453 	PATCH_ADDTOOFFSET(+11),             // skip over until push0
6454 	0x78,                               // push1 (change push0 to push1)
6455 	PATCH_END
6456 };
6457 
6458 // a second patch to call Lillian::ignoreActors(1) on goSee::changeState(9) in script 236
6459 static const uint16 laurabow1SignatureTellLillyAboutGerieBlockingFix2[] = {
6460 	0x3c,                               // dup
6461 	0x35, 0x09,                         // ldi 09
6462 	0x1a,                               // eq?
6463 	0x30, SIG_UINT16(0x003f),           // bnt [ret]
6464 	0x39, SIG_ADDTOOFFSET(+1),          // pushi view
6465 	0x78,                               // push1
6466 	0x38, SIG_UINT16(0x0203),           // pushi 203h (515d)
6467 	0x38, SIG_ADDTOOFFSET(+2),          // pushi posn
6468 	0x7a,                               // push2
6469 	0x38, SIG_UINT16(0x00c9),           // pushi C9h (201d)
6470 	SIG_MAGICDWORD,
6471 	0x38, SIG_UINT16(0x0084),           // pushi 84h (132d)
6472 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa Lillian (different offsets for different platforms)
6473 	0x4a, 0x0e,                         // send 0Eh
6474 	SIG_END
6475 };
6476 
6477 static const uint16 laurabow1PatchTellLillyAboutGertieBlockingFix2[] = {
6478 	0x38, PATCH_SELECTOR16(ignoreActors), // pushi ignoreActors
6479 	0x78,                                 // push1
6480 	0x76,                                 // push0
6481 	0x33, 0x00,                           // ldi 00 (waste 2 bytes)
6482 	PATCH_ADDTOOFFSET(+19),               // skip over until send
6483 	0x4a, 0x14,                           // send 14h
6484 	PATCH_END
6485 };
6486 
6487 // LB1 contains over 20 commands which lockup the game if entered while ego is
6488 //  colliding with an obstacle. Opening and moving closets in room 43 are prime
6489 //  examples. They are all symptoms of a global bug.
6490 
6491 // Every cycle, CB1:doit checks to see if ego appears to be walking and blocked,
6492 //  and if so it stops ego's motion and sets ego's view to 11 (standing). If ego
6493 //  has just collided with an obstacle but is still displaying view 1 (walking)
6494 //  when a command is entered which disables input and sets ego's motion without
6495 //  setting an avoider then CB1:doit will stop that new motion at the start of
6496 //  the next cycle and lockup the game. This occurs in part because CB1:doit
6497 //  tests potentially stale values that the new motion hasn't yet had a chance
6498 //  to set. Scripts which set an avoider aren't vulnerable because CB1:doit
6499 //  won't stop ego if one is set. Other SCI games don't have this kind of code
6500 //  in their Game's doit and so they don't have this bug.
6501 
6502 // We fix this by clearing the kSignalHitObstacle flag whenever Act:setMotion is
6503 //  called with a new motion. This causes CB1:doit's subsequent call to
6504 //  ego:isBlocked to return false, instead of a stale true value, preventing
6505 //  CB1:doit from stopping the new motion before it's had a chance to start.
6506 //  Should the new motion actually be blocked then the interpreter will then set
6507 //  the flag when processing the motion as usual. This patch closes the short
6508 //  window during which this value is stale and CB1:doit tests it.
6509 
6510 // Applies to: DOS, Amiga, Atari ST and occurs in Sierra's interpreter.
6511 // Responsible method: Act:setMotion
6512 // Fixes bug: #10733
6513 static const uint16 laurabow1SignatureObstacleCollisionLockupsFix[] = {
6514 	SIG_MAGICDWORD,
6515 	0x30, SIG_UINT16(0x002f),           // bnt 2f
6516 	0x38, SIG_UINT16(0x00a3),           // pushi a3 [ startUpd ]
6517 	0x76,                               // push0
6518 	0x54, 0x04,                         // self 4
6519 	0x7a,                               // push2 [ -info- ]
6520 	0x76,                               // push0
6521 	0x87, 0x01,                         // lap param[1]
6522 	0x4a, 0x04,                         // send 4
6523 	0x36,                               // push
6524 	0x34, SIG_UINT16(0x8000),           // ldi 8000
6525 	0x12,                               // and
6526 	0x30, SIG_UINT16(0x000a),           // bnt a
6527 	0x39, 0x56,                         // pushi 56 [ new ]
6528 	0x76,                               // push0
6529 	0x87, 0x01,                         // lap param[1]
6530 	0x4a, 0x04,                         // send 4
6531 	0x32, SIG_UINT16(0x0002),           // jmp 2
6532 	0x87, 0x01,                         // lap param[1]
6533 	0x65, 0x4c,                         // aTop mover
6534 	0x39, 0x57,                         // pushi 57 [ init ]
6535 	0x78,                               // push1
6536 	0x7c,                               // pushSelf
6537 	0x59, 0x02,                         // &rest 2
6538 	0x63, 0x4c,                         // pToa mover
6539 	0x4a, 0x06,                         // send 6
6540 	0x32, SIG_UINT16(0x0004),           // jmp 4
6541 	0x35, 0x00,                         // ldi 0
6542 	SIG_END
6543 };
6544 
6545 static const uint16 laurabow1PatchObstacleCollisionLockupsFix[] = {
6546 	0x31, 0x32,                         // bnt 32 [ save 1 byte ]
6547 
6548 	0x63, 0x1c,                         // pToa signal
6549 	0x38, PATCH_UINT16(0xfbff),         // pushi fbff
6550 	0x12,                               // and
6551 	0x65, 0x1c,                         // aTop signal [ clear kSignalHitObstacle (0400) ]
6552 
6553 	0x38, PATCH_UINT16(0x00a3),         // pushi a3 [ startUpd ]
6554 	0x76,                               // push0
6555 	0x54, 0x04,                         // self 4
6556 	0x7a,                               // push2 [ -info- ]
6557 	0x76,                               // push0
6558 	0x87, 0x01,                         // lap param[1]
6559 	0x4a, 0x04,                         // send 4
6560 	0x38, PATCH_UINT16(0x8000),         // pushi 8000 [ save 1 byte ]
6561 	0x12,                               // and
6562 	0x31, 0x09,                         // bnt 9 [ save 1 byte ]
6563 	0x39, 0x56,                         // pushi 56 [ new ]
6564 	0x76,                               // push0
6565 	0x87, 0x01,                         // lap param[1]
6566 	0x4a, 0x04,                         // send 4
6567 	0x33, 0x02,                         // jmp 2 [ save 1 byte ]
6568 	0x87, 0x01,                         // lap param[1]
6569 	0x65, 0x4c,                         // aTop mover
6570 	0x39, 0x57,                         // pushi 57 [ init ]
6571 	0x78,                               // push1
6572 	0x7c,                               // pushSelf
6573 	0x59, 0x02,                         // &rest 2
6574 	0x63, 0x4c,                         // pToa mover
6575 	0x4a, 0x06,                         // send 6
6576 	0x48,                               // ret  [ save 4 bytes ]
6577 	PATCH_END
6578 };
6579 
6580 // Laura can get stuck walking up the attic stairs diagonally in room 47 and
6581 //  lockup the game. This also occurs in the original. Room47:doit loads the
6582 //  attic when ego is on control $10 at the base of the stairs and facing north.
6583 //  Room47:handleEvent prevents the user from moving ego when on control $10.
6584 //  Walking up the stairs diagonally puts ego in control $10 facing left or
6585 //  right and so the room never changes and the user never regains control.
6586 //
6587 // We fix this by allowing ego to face any direction except south to trigger the
6588 //  attic room change. This also fixes an edge case that allows walking through
6589 //  the staircase wall into Clarence's room.
6590 //
6591 // Applies to: DOS, Amiga, Atari ST
6592 // Responsible method: Room47:doit
6593 // Fixes bug: #9949
6594 static const uint16 laurabow1SignatureAtticStairsLockupFix[] = {
6595 	SIG_MAGICDWORD,
6596 	0x39, SIG_SELECTOR8(loop),          // pushi loop
6597 	0x76,                               // push0
6598 	0x81, 0x00,                         // lag global[0]
6599 	0x4a, 0x04,                         // send 4 [ ego:loop? ]
6600 	0x36,                               // push
6601 	0x35, 0x03,                         // ldi 03 [ facing north ]
6602 	0x1a,                               // eq?
6603 	SIG_END
6604 };
6605 
6606 static const uint16 laurabow1PatchAtticStairsLockupFix[] = {
6607 	PATCH_ADDTOOFFSET(+8),
6608 	0x35, 0x02,                         // ldi 02 [ facing south ]
6609 	0x1c,                               // ne?
6610 	PATCH_END
6611 };
6612 
6613 // Laura can get stuck at the top of the left stairs in room 47 and lockup the
6614 //  game. This also occurs in the original. There is a 30x2 control area at the
6615 //  top of the stairs in which Room47:handleEvent prevents input. This assumes
6616 //  that ego can't be interrupted when walking through the area, but there is a
6617 //  notch in the left wall that ego can collide with, leaving ego stuck with
6618 //  input disabled. The right wall doesn't have a notch.
6619 //
6620 // We fix this by allowing input at the top of the stairs. Up and down movements
6621 //  are allowed when on the staircase's control area ($0200) and we extend that
6622 //  to include the top of the stairs ($0800).
6623 //
6624 // Applies to: DOS, Amiga, Atari ST
6625 // Responsible method: Room47:handleEvent
6626 // Fixes bug #10879
6627 static const uint16 laurabow1SignatureLeftStairsLockupFix[] = {
6628 	SIG_MAGICDWORD,
6629 	0x34, SIG_UINT16(0x0200),           // ldi 0200 [ left stairs ]
6630 	0x1a,                               // eq? [ is ego entirely on the stairs? ]
6631 	SIG_END
6632 };
6633 
6634 static const uint16 laurabow1PatchLeftStairsLockupFix[] = {
6635 	0x34, PATCH_UINT16(0x0a00),         // ldi 0a00 [ left stairs | top of left stairs ]
6636 	0x12,                               // and [ is ego touching the stairs or the top? ]
6637 	PATCH_END
6638 };
6639 
6640 // LB1's fingerprint copy protection randomly rejects the correct answer and may
6641 //  even fail to draw a fingerprint. myCopy:init selects the fingerprint by
6642 //  generating random loop and cel numbers for view 553, but it passes incorrect
6643 //  ranges to kRandom. If kRandom returns the maximum value then the loop or cel
6644 //  overflow and a different image is displayed than what was intended.
6645 //
6646 // We correct the ranges from 0-600 and 1-1000 to 0-599 and 0-999 so that
6647 //  invalid cel 6 and loop 10 are never used after the script divides by 10.
6648 //
6649 // Applies to: DOS, Amiga, Atari ST
6650 // Responsible method: myCopy:init
6651 static const uint16 laurabow1SignatureCopyProtectionRandomFix[] = {
6652 	0x38, SIG_UINT16(0x0258),           // pushi 600d
6653 	SIG_ADDTOOFFSET(+10),
6654 	SIG_MAGICDWORD,
6655 	0x78,                               // push1
6656 	0x38, SIG_UINT16(0x03e8),           // pushi 1000d
6657 	SIG_END
6658 };
6659 
6660 static const uint16 laurabow1PatchCopyProtectionRandomFix[] = {
6661 	0x38, PATCH_UINT16(0x0257),         // pushi 599d
6662 	PATCH_ADDTOOFFSET(+10),
6663 	0x76,                               // push0
6664 	0x38, PATCH_UINT16(0x03e7),         // pushi 999d
6665 	PATCH_END
6666 };
6667 
6668 //          script, description,                                signature                                             patch
6669 static const SciScriptPatcherEntry laurabow1Signatures[] = {
6670 	{  true,     4, "easter egg view fix",                      1, laurabow1SignatureEasterEggViewFix,                laurabow1PatchEasterEggViewFix },
6671 	{  true,    37, "armor open visor fix",                     1, laurabow1SignatureArmorOpenVisorFix,               laurabow1PatchArmorOpenVisorFix },
6672 	{  true,    37, "armor move to fix",                        2, laurabow1SignatureArmorMoveToFix,                  laurabow1PatchArmorMoveToFix },
6673 	{  true,    37, "allowing input, after oiling arm",         1, laurabow1SignatureArmorOilingArmFix,               laurabow1PatchArmorOilingArmFix },
6674 	{  true,    44, "lillian bed fix",                          1, laurabow1SignatureLillianBedFix,                   laurabow1PatchLillianBedFix },
6675 	{  true,    47, "attic stairs lockup fix",                  1, laurabow1SignatureAtticStairsLockupFix,            laurabow1PatchAtticStairsLockupFix },
6676 	{  true,    47, "left stairs lockup fix",                   3, laurabow1SignatureLeftStairsLockupFix,             laurabow1PatchLeftStairsLockupFix },
6677 	{  true,    58, "chapel candles persistence",               1, laurabow1SignatureChapelCandlesPersistence,        laurabow1PatchChapelCandlesPersistence },
6678 	{  true,   236, "tell Lilly about Gertie blocking fix 1/2", 1, laurabow1SignatureTellLillyAboutGerieBlockingFix1, laurabow1PatchTellLillyAboutGertieBlockingFix1 },
6679 	{  true,   236, "tell Lilly about Gertie blocking fix 2/2", 1, laurabow1SignatureTellLillyAboutGerieBlockingFix2, laurabow1PatchTellLillyAboutGertieBlockingFix2 },
6680 	{  true,   414, "copy protection random fix",               1, laurabow1SignatureCopyProtectionRandomFix,         laurabow1PatchCopyProtectionRandomFix },
6681 	{  true,   998, "obstacle collision lockups fix",           1, laurabow1SignatureObstacleCollisionLockupsFix,     laurabow1PatchObstacleCollisionLockupsFix },
6682 	SCI_SIGNATUREENTRY_TERMINATOR
6683 };
6684 
6685 // ===========================================================================
6686 // Laura Bow 2
6687 //
6688 // Moving away the painting in the room with the hidden safe is problematic
6689 //  for the CD version of the game. safePic::doVerb gets triggered by the mouse-click.
6690 // This method sets local[0] as signal, which is only meant to get handled, when
6691 //  the player clicks again to move the painting back. This signal is processed by
6692 //  the room doit-script.
6693 // That doit-script checks safePic::cel to be not equal 0 and would then skip over
6694 //  the "close painting" trigger code. On very fast computers this script may
6695 //  get called too early (which is the case when running under ScummVM and when
6696 //  running the game using Sierra SCI in DOS-Box with cycles 15000) and thinks
6697 //  that it's supposed to move the painting back. Which then results in the painting
6698 //  getting moved to its original position immediately (which means it won't be possible
6699 //  to access the safe behind it).
6700 //
6701 // We patch the script, so that we check for cel to be not equal 4 (the final cel) and
6702 //  we also reset the safePic-signal immediately as well.
6703 //
6704 // In the floppy version Laura's coordinates are checked directly in rm560::doit
6705 //  and as soon as she moves, the painting will automatically move to its original position.
6706 //  This is not the case for the CD version of the game. The painting will only "move" back,
6707 //  when the player actually exits the room and re-enters.
6708 //
6709 // Applies to at least: English PC-CD
6710 // Responsible method: rm560::doit
6711 // Fixes bug: #6460
6712 static const uint16 laurabow2CDSignaturePaintingClosing[] = {
6713 	0x39, 0x04,                         // pushi 04 (cel)
6714 	0x76,                               // push0
6715 	SIG_MAGICDWORD,
6716 	0x7a,                               // push2
6717 	0x38, SIG_UINT16(0x0231),           // pushi 0231h (561)
6718 	0x76,                               // push0
6719 	0x43, 0x02, 0x04,                   // callk ScriptID, 04 (get export 0 of script 561)
6720 	0x4a, 0x04,                         // send 04 (gets safePicture::cel)
6721 	0x18,                               // not
6722 	0x31, 0x21,                         // bnt [exit]
6723 	0x38, SIG_UINT16(0x0283),           // pushi 0283h
6724 	0x76,                               // push0
6725 	0x7a,                               // push2
6726 	0x39, 0x20,                         // pushi 20
6727 	0x76,                               // push0
6728 	0x43, 0x02, 0x04,                   // callk ScriptID, 04 (get export 0 of script 32)
6729 	0x4a, 0x04,                         // send 04 (get sHeimlich::room)
6730 	0x36,                               // push
6731 	0x81, 0x0b,                         // lag global[b] (current room)
6732 	0x1c,                               // ne?
6733 	0x31, 0x0e,                         // bnt [exit]
6734 	0x35, 0x00,                         // ldi 00
6735 	0xa3, 0x00,                         // sal local[0] (reset safePic signal)
6736 	SIG_END
6737 };
6738 
6739 static const uint16 laurabow2CDPatchPaintingClosing[] = {
6740 	PATCH_ADDTOOFFSET(+2),
6741 	0x3c,                               // dup (1 additional byte)
6742 	0x76,                               // push0
6743 	0x3c,                               // dup (1 additional byte)
6744 	0xab, 0x00,                         // ssl local[0] (reset safePic signal)
6745 	0x7a,                               // push2
6746 	0x38, PATCH_UINT16(0x0231),         // pushi 0231h (561)
6747 	0x76,                               // push0
6748 	0x43, 0x02, 0x04,                   // callk ScriptID, 04 (get export 0 of script 561)
6749 	0x4a, 0x04,                         // send 04 (gets safePicture::cel)
6750 	0x1a,                               // eq?
6751 	0x31, 0x1d,                         // bnt [exit]
6752 	0x38, PATCH_UINT16(0x0283),         // pushi 0283h
6753 	0x76,                               // push0
6754 	0x7a,                               // push2
6755 	0x39, 0x20,                         // pushi 20
6756 	0x76,                               // push0
6757 	0x43, 0x02, 0x04,                   // callk ScriptID, 04 (get export 0 of script 32)
6758 	0x4a, 0x04,                         // send 04 (get sHeimlich::room)
6759 	0x36,                               // push
6760 	0x81, 0x0b,                         // lag global[b] (current room)
6761 	0x1a,                               // eq? (2 opcodes changed, to save 2 bytes)
6762 	0x2f, 0x0a,                         // bt [exit]
6763 	PATCH_END
6764 };
6765 
6766 // In the CD version the system menu is disabled for certain rooms. LB2::handsOff is called,
6767 //  when leaving the room (and in other cases as well). This method remembers the disabled
6768 //  icons of the icon bar. In the new room LB2::handsOn will get called, which then enables
6769 //  all icons, but also disabled the ones, that were disabled before.
6770 //
6771 // Because of this behaviour certain rooms, that should have the system menu enabled, have
6772 //  it disabled, when entering those rooms from rooms, where the menu is supposed to be
6773 //  disabled.
6774 //
6775 // We patch this by injecting code into LB2::newRoom (which is called right after a room change)
6776 //  and reset the global variable there, that normally holds the disabled buttons.
6777 //
6778 // This patch may cause side-effects and it's difficult to test, because it affects every room
6779 //  in the game. At least for the intro, the speakeasy and plenty of rooms in the beginning it
6780 //  seems to work correctly.
6781 //
6782 // Applies to at least: English PC-CD
6783 // Responsible method: LB2::newRoom, LB2::handsOff, LB2::handsOn
6784 // Fixes bug: #6440
6785 static const uint16 laurabow2CDSignatureFixProblematicIconBar[] = {
6786 	SIG_MAGICDWORD,
6787 	0x38, SIG_UINT16(0x00f1),           // pushi 00f1 (disable) - hardcoded, we only want to patch the CD version
6788 	0x76,                               // push0
6789 	0x81, 0x45,                         // lag global[45]
6790 	0x4a, 0x04,                         // send 04
6791 	SIG_END
6792 };
6793 
6794 static const uint16 laurabow2CDPatchFixProblematicIconBar[] = {
6795 	0x35, 0x00,                      // ldi 00
6796 	0xa1, 0x74,                      // sag global[74]
6797 	0x35, 0x00,                      // ldi 00 (waste bytes)
6798 	0x35, 0x00,                      // ldi 00
6799 	PATCH_END
6800 };
6801 
6802 // LB2 CD responds with the wrong message when asking Yvette about Tut in acts 3+.
6803 //
6804 // aYvette:doVerb(6) tests flag 134, which is set when Pippin dies, to determine
6805 //  which Tut message to display but they got it backwards. One of the messages
6806 //  has additional dialogue about Pippin's murder.
6807 //
6808 // This is a regression introduced by Sierra when they fixed a bug from the floppy
6809 //  versions where asking Yvette about Tut in act 2 responds with the message
6810 //  about Pippin's murder which hasn't occurred yet, bug #10723. Sierra correctly
6811 //  fixed that in Yvette:doVerb in script 93, which applies to act 2, but then went
6812 //  on to add incorrect code to aYvette:doVerb in script 90, which applies to the
6813 //  later acts after the murder.
6814 //
6815 // We fix this by reversing the flag test so that the correct message is displayed.
6816 //
6817 // Applies to: CD version, at least English
6818 // Responsible method: aYvette:doVerb
6819 // Fixes bug: #10724
6820 static const uint16 laurabow2CDSignatureFixYvetteTutResponse[] = {
6821 	SIG_MAGICDWORD,
6822 	0x34, SIG_UINT16(0x010f),           // ldi 010f [ tut ]
6823 	0x1a,                               // eq? [ asked about tut? ]
6824 	0x30, SIG_UINT16(0x0036),           // bnt 0036
6825 	0x78,                               // push1
6826 	0x38, SIG_UINT16(0x0086),           // pushi 0086 [ pippin-dead flag ]
6827 	0x45, 0x02, 0x02,                   // callb [export 2 of script 0], 02 [ is pippin-dead flag set? ]
6828 	0x30, SIG_UINT16(0x0016),           // bnt 0016 [ pippin-dead message ]
6829 	SIG_END
6830 };
6831 
6832 static const uint16 laurabow2CDPatchFixYvetteTutResponse[] = {
6833 	PATCH_ADDTOOFFSET(+14),
6834 	0x2e,                               // bt (replace bnt)
6835 	PATCH_END
6836 };
6837 
6838 // When entering the main musem party room (w/ the golden Egyptian head), Laura
6839 // is walking a bit into the room automatically. If you press a mouse button
6840 // while this is happening, you will get stuck inside that room and won't be
6841 // able to exit it anymore.
6842 //
6843 // Users, who played the game w/ a previous version of ScummVM can simply enter
6844 // the debugger and then enter "send rm350 script 0:0", which will fix the
6845 // script state.
6846 //
6847 // This is caused by the user controls not being locked at that point. Pressing
6848 // a button will cause the cue from the PolyPath walker to never happen, which
6849 // then causes sEnterSouth to never dispose itself.
6850 //
6851 // User controls are locked in the previous room 335, but controls are unlocked
6852 // by frontDoor::cue. We do not want to change this, because it could have
6853 // side-effects. We instead add another LB2::handsOff call inside the script
6854 // responsible for Laura's walk into the room (sEnterSouth::changeState(0).
6855 //
6856 // Applies to at least: English PC-CD, English PC-Floppy, German PC-Floppy
6857 // Responsible method: sEnterSouth::changeState
6858 // Fixes bug: (no bug report, from GOG forum post)
6859 static const uint16 laurabow2SignatureMuseumPartyFixEnteringSouth1[] = {
6860 	0x3c,                              // dup
6861 	0x35, 0x00,                        // ldi 00
6862 	0x1a,                              // eq?
6863 	0x30, SIG_UINT16(0x0097),          // bnt [state 1 code]
6864 	SIG_ADDTOOFFSET(+141),             // skip to end of follow-up code
6865 	0x32, SIG_ADDTOOFFSET(+2),         // jmp [ret] (0x008d for CD, 0x007d for floppy)
6866 	0x35, 0x01,                        // ldi 01
6867 	0x65, 0x1a,                        // aTop cycles
6868 	0x32, SIG_ADDTOOFFSET(+2),         // jmp [ret] (0x0086 for CD, 0x0076 for floppy)
6869 	// state 1 code
6870 	0x3c,                              // dup
6871 	0x35, 0x01,                        // ldi 01
6872 	0x1a,                              // eq?
6873 	SIG_MAGICDWORD,
6874 	0x31, 0x05,                        // bnt [state 2 code]
6875 	0x35, 0x00,                        // ldi 00
6876 	0x32, SIG_ADDTOOFFSET(+2),         // jmp [ret] (0x007b for CD, 0x006b for floppy)
6877 	// state 2 code
6878 	0x3c,                              // dup
6879 	SIG_END
6880 };
6881 
6882 static const uint16 laurabow2PatchMuseumPartyFixEnteringSouth1[] = {
6883 	0x2e, PATCH_UINT16(0x00a6),        // bt [state 2 code] (we skip state 1, because it's a NOP anyways)
6884 	// state 0 processing
6885 	0x32, PATCH_UINT16(0x0097),        // jmp 151d [after this ret]
6886 	PATCH_ADDTOOFFSET(+149),           // skip to end of follow-up code
6887 	// save 1 byte by replacing jump to [ret] into straight toss/ret
6888 	0x3a,                              // toss
6889 	0x48,                              // ret
6890 
6891 	// additional code, that gets called right at the start of step 0 processing
6892 	0x18,                              // not -- this here is where pushi handsOff will be inserted by the second patch
6893 	0x18,                              // not    offset and handsOff is different for floppy + CD, that's why we do this
6894 	0x18,                              // not    floppy also does not have a selector table, so we can't go by "handsOff" name
6895 	0x18,                              // not
6896 	0x76,                              // push0
6897 	0x81, 0x01,                        // lag global[1]
6898 	0x4a, 0x04,                        // send 04
6899 	0x32, PATCH_UINT16(0xff5e),        // jmp [back to start of step 0 processing]
6900 	PATCH_END
6901 };
6902 
6903 // Second patch, which only inserts pushi handsOff inside our new code. There
6904 // is no other way to do this except making 2 full patches for floppy + CD,
6905 // because handsOff/handsOn is not the same value between floppy + CD *and*
6906 // floppy doesn't even have a vocab, so we can't figure out the id by
6907 // ourselves.
6908 static const uint16 laurabow2SignatureMuseumPartyFixEnteringSouth2[] = {
6909 	0x18,                              // our injected code
6910 	0x18,
6911 	0x18,
6912 	SIG_ADDTOOFFSET(+92),              // skip to the handsOn code, that we are interested in
6913 	0x38, SIG_ADDTOOFFSET(+2),         // pushi handsOn (0x0189 for CD, 0x024b for floppy)
6914 	0x76,                              // push0
6915 	0x81, 0x01,                        // lag global[1]
6916 	0x4a, 0x04,                        // send 04
6917 	0x38, SIG_ADDTOOFFSET(+2),         // pushi 0274h
6918 	SIG_MAGICDWORD,
6919 	0x78,                              // push1
6920 	0x38, SIG_UINT16(0x033f),          // pushi 033f
6921 	SIG_END
6922 };
6923 
6924 static const uint16 laurabow2PatchMuseumPartyFixEnteringSouth2[] = {
6925 	0x38, PATCH_GETORIGINALUINT16ADJUST(+96, -1), // pushi handsOff (@handsOn - 1)
6926 	PATCH_END
6927 };
6928 
6929 // Opening/Closing the east door in the pterodactyl room doesn't check, if it's
6930 //  locked and will open/close the door internally even when it is.
6931 //
6932 // It will get wired shut later in the game by Laura Bow and will be "locked"
6933 //  because of this. We patch in a check for the locked state. We also add
6934 //  code, that will set the "locked" state in case our eastDoor-wired-global is
6935 //  set. This makes the locked state effectively persistent.
6936 //
6937 // Applies to at least: English PC-CD, English PC-Floppy
6938 // Responsible method (CD): eastDoor::doVerb
6939 // Responsible method (Floppy): eastDoor::<noname300>
6940 // Fixes bug: #6458 (partly, see additional patch below)
6941 static const uint16 laurabow2SignatureFixWiredEastDoor[] = {
6942 	0x30, SIG_UINT16(0x0022),           // bnt [skip hand action]
6943 	0x67, SIG_ADDTOOFFSET(+1),          // pTos (CD: doorState, Floppy: state)
6944 	0x35, 0x00,                         // ldi 00
6945 	0x1a,                               // eq?
6946 	0x31, 0x08,                         // bnt [close door code]
6947 	0x78,                               // push1
6948 	SIG_MAGICDWORD,
6949 	0x39, 0x63,                         // pushi 63h
6950 	0x45, 0x04, 0x02,                   // callb [export 4 of script 0], 02 (sets door-bitflag)
6951 	0x33, 0x06,                         // jmp [super-code]
6952 	0x78,                               // push1
6953 	0x39, 0x63,                         // pushi 63h
6954 	0x45, 0x03, 0x02,                   // callb [export 3 of script 0], 02 (resets door-bitflag)
6955 	0x38, SIG_ADDTOOFFSET(+2),          // pushi (CD: 011dh, Floppy: 012ch)
6956 	0x78,                               // push1
6957 	0x8f, 0x01,                         // lsp param[1]
6958 	0x59, 0x02,                         // rest 02
6959 	0x57, SIG_ADDTOOFFSET(+1), 0x06,    // super (CD: LbDoor, Floppy: Door), 06
6960 	0x33, 0x0b,                         // jmp [ret]
6961 	SIG_END
6962 };
6963 
6964 static const uint16 laurabow2PatchFixWiredEastDoor[] = {
6965 	0x31, 0x23,                         // bnt [skip hand action] (saves 1 byte)
6966 	0x81, 0x61,                         // lag global[97d] (get our eastDoor-wired-global)
6967 	0x31, 0x04,                         // bnt [skip setting locked property]
6968 	0x35, 0x01,                         // ldi 01
6969 	0x65, 0x6a,                         // aTop locked (set eastDoor::locked to 1)
6970 	0x63, 0x6a,                         // pToa locked (get eastDoor::locked)
6971 	0x2f, 0x17,                         // bt [skip hand action]
6972 	0x63, PATCH_GETORIGINALBYTE(+4),    // pToa (CD: doorState, Floppy: state)
6973 	0x78,                               // push1
6974 	0x39, 0x63,                         // pushi 63h
6975 	0x2f, 0x05,                         // bt [close door code]
6976 	0x45, 0x04, 0x02,                   // callb [export 4 of script 0], 02 (sets door-bitflag)
6977 	0x33, 0x0b,                         // jmp [super-code]
6978 	0x45, 0x03, 0x02,                   // callb [export 3 of script 0], 02 (resets door-bitflag)
6979 	0x33, 0x06,                         // jmp [super-code]
6980 	PATCH_END
6981 };
6982 
6983 // We patch in code, so that our eastDoor-wired-global will get set to 1.
6984 //  This way the wired-state won't get lost when exiting room 430.
6985 //
6986 // Applies to at least: English PC-CD, English PC-Floppy
6987 // Responsible method (CD): sWireItShut::changeState
6988 // Responsible method (Floppy): sWireItShut::<noname144>
6989 // Fixes bug: #6458 (partly, see additional patch above)
6990 static const uint16 laurabow2SignatureRememberWiredEastDoor[] = {
6991 	SIG_MAGICDWORD,
6992 	0x33, 0x27,                         // jmp [ret]
6993 	0x3c,                               // dup
6994 	0x35, 0x06,                         // ldi 06
6995 	0x1a,                               // eq?
6996 	0x31, 0x21,                         // bnt [skip step]
6997 	SIG_END
6998 };
6999 
7000 static const uint16 laurabow2PatchRememberWiredEastDoor[] = {
7001 	PATCH_ADDTOOFFSET(+2),              // skip jmp [ret]
7002 	0x34, PATCH_UINT16(0x0001),         // ldi 0001
7003 	0xa1, PATCH_UINT16(0x0061),         // sag global[97d] (set our eastDoor-wired-global)
7004 	PATCH_END
7005 };
7006 
7007 // WORKAROUND: Script needed, because of differences in our pathfinding
7008 // algorithm
7009 // It's possible to walk through the closed door in room 448 and enter the crate
7010 //  room before act 5 due to differences in our pathfinding algorithm from Sierra's.
7011 //  This edge case appears to be due to one of the door's points being one pixel
7012 //  within the room's walkable boundary, allowing ego to walk through this edge
7013 //  from certain positions. We work around this by moving the door's point by
7014 //  two pixels so that it's outside the room's walkable boundary.
7015 //
7016 // Applies to: All Floppy and CD versions
7017 // Responsible method: transomDoor:createPoly
7018 // Fixes bug: #9952
7019 static const uint16 laurabow2SignatureFixArmorHallDoorPathfinding[] = {
7020 	SIG_MAGICDWORD,
7021 	0x39, 0x50,                         // pushi 50 [ x =  80 ]
7022 	0x39, 0x7d,                         // pushi 7d [ y = 125 ]
7023 	SIG_END
7024 };
7025 
7026 static const uint16 laurabow2PatchFixArmorHallDoorPathfinding[] = {
7027 	PATCH_ADDTOOFFSET(+2),
7028 	0x39, 0x7f,                         // pushi 7f [ y = 127 ]
7029 	PATCH_END
7030 };
7031 
7032 // The crate room (room 460) in act 5 locks up the game if you enter from the
7033 //  elevator (room 660), swing the hanging crate, and then attempt to leave
7034 //  back through the elevator door.
7035 //
7036 // The state of the wall crate that blocks the elevator door is tracked by
7037 //  setting local[0] to 1 when you push it out of the way, but Sierra forgot
7038 //  to reinitialize local[0] when you re-enter via the elevator door, causing
7039 //  it to be out of sync with the room state. When you then swing the hanging
7040 //  crate, sSwingIt:changeState(6) tests local[0] to see which polygon it
7041 //  should set as the room's obstacle and incorrectly uses the one that blocks
7042 //  both doors. Attempting to use the elevator door then locks up the game as
7043 //  the obstacle polygon prevents ego from reaching the destination.
7044 //
7045 // Someone noticed that local[0] wasn't always initialized as
7046 //  shoveCrate:doVerb(4) tests both local[0] and the previous room to see if it
7047 //  was the elevator.
7048 //
7049 // We fix this by setting local[0] to 1 if the previous room was the elevator
7050 //  during sSwingIt:changeState(3), just in time before it gets tested in
7051 //  sSwingIt:changeState(6). Luckily for us, the handlers for states 3 and 4
7052 //  don't do anything but load zero, making them two consecutive conditions
7053 //  of no-ops. By merging them into a single condition for state 3 we have
7054 //  a whopping 13 bytes available to add code to set local[0] correctly.
7055 //
7056 // Applies to: All Floppy and CD versions
7057 // Fixes bug: #10701
7058 static const uint16 laurabow2SignatureFixCrateRoomEastDoorLockup[] = {
7059 	0x1a,                               // eq? [ state 3? ]
7060 	SIG_MAGICDWORD,
7061 	0x31, 0x05,                         // bnt [ state 4 ]
7062 	0x35, 0x00,                         // ldi 0
7063 	0x32, SIG_ADDTOOFFSET(+2),          // jmp [ exit switch. floppy: b3, cd: bb ]
7064 	0x3c,                               // dup
7065 	0x35, 0x04,                         // ldi 4
7066 	0x1a,                               // eq? [ state 4? ]
7067 	0x31, 0x05,                         // bnt [ state 5 ]
7068 	SIG_END
7069 };
7070 
7071 static const uint16 laurabow2PatchFixCrateRoomEastDoorLockup[] = {
7072 	PATCH_ADDTOOFFSET(+1),              // eq? [ state 3? ]
7073 	0x31, 0x10,                         // bnt [ state 5 ]
7074 	0x89, 0x0c,                         // lsg global[0c] [ previous room # ]
7075 	0x34, PATCH_UINT16(0x0294),         // ldi 660d [ elevator room # ]
7076 	0x1a,                               // eq?
7077 	0x8b, 0x00,                         // lsl local[0]
7078 	0x02,                               // add
7079 	0xa3, 0x00,                         // sal local[0] [ local[0] += (global[0c] == 660d) ]
7080 	PATCH_END
7081 };
7082 
7083 // Ego can get stuck in the elevator (room 660) by walking to the lower left.
7084 //  This also happens in Sierra's interpreter. We adjust the room's obstacle
7085 //  polygon so that ego can't reach the problematic corner positions.
7086 //  This is a heap patch for the coordinates used in poly2660a:points.
7087 //
7088 // Applies to: All Floppy and CD versions
7089 // Fixes bug: #10702
7090 static const uint16 laurabow2SignatureFixElevatorLockup[] = {
7091 	SIG_MAGICDWORD,
7092 	SIG_UINT16(0x008b),                 // x = 139d
7093 	SIG_UINT16(0x0072),                 // y = 114d
7094 	SIG_UINT16(0x007b),                 // x = 123d
7095 	SIG_UINT16(0x008d),                 // y = 141d
7096 	SIG_END
7097 };
7098 
7099 static const uint16 laurabow2PatchFixElevatorLockup[] = {
7100 	PATCH_UINT16(0x008f),               // x = 143d
7101 	PATCH_ADDTOOFFSET(+4),
7102 	PATCH_UINT16(0x00aa),               // y = 170d
7103 	PATCH_END
7104 };
7105 
7106 // The act 4 back rub scene in Yvette's (room 550) locks up the game when
7107 //  entered from Carrington's (room 560) instead of the hallway (room 510).
7108 //
7109 // The difference is that entering from the hallway sets the room script to
7110 //  eRS (Enter Room Script) and entering from Carrington's doesn't set any
7111 //  room script. When sBackRubInterrupted moves ego off screen to the south,
7112 //  lRS (Leave Room Script) is run by LBRoom:doit if a room script isn't set,
7113 //  and lRS:changState(0) calls handsOff. Since control is already disabled,
7114 //  this unexpected second handsOff causes handsOn(1) to restore the disabled
7115 //  state in the hallway and the user never regains control.
7116 //
7117 // We fix this by setting sBackRubInterrupted as the room's script instead of
7118 //  backRub's script in backRub:doVerb/<noname300>(0). The script executes the
7119 //  same but having it set as the room script prevents LBRoom:doit from running
7120 //  lRS which prevents the extra handsOff. This patch overwrites backRub's
7121 //  default verb handler but that's okay because that code never executes.
7122 //  doVerb is only called by sBackRubViewing:changeState(6) which passes verb 0.
7123 //  The entire scene is in handsOff mode so the user can't send any verbs.
7124 //
7125 // Affects: All Floppy and CD versions
7126 // Responsible method: backRub:doVerb/<noname300> in script 550
7127 // Fixes bug: #10729
7128 static const uint16 laurabow2SignatureFixBackRubEastEntranceLockup[] = {
7129 	SIG_MAGICDWORD,
7130 	0x31, 0x0c,                         // bnt 0c    [ unused default verb handler ]
7131 	0x38, SIG_UINT16(0x0092),           // pushi 0092 [ setScript/<noname146> ]
7132 	0x78,                               // push1
7133 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa sBackRubInterrupted [ cd: 0c94, floppy: 0c70 ]
7134 	0x36,                               // push
7135 	0x54, 0x06,                         // self 6 [ self:setScript sBackRubInterrupted ]
7136 	0x33, 0x09,                         // jmp 9  [ exit switch ]
7137 	0x38, SIG_ADDTOOFFSET(+2),          // pushi doVerb/<noname300> [ cd: 011d, floppy: 012c ]
7138 	SIG_END
7139 };
7140 
7141 static const uint16 laurabow2PatchFixBackRubEastEntranceLockup[] = {
7142 	PATCH_ADDTOOFFSET(+10),
7143 	0x81, 0x02,                         // lag global[2] [ rm550 ]
7144 	0x4a, 0x06,                         // send 6 [ rm550:setScript sBackRubInterrupted ]
7145 	0x32, PATCH_UINT16(0x0006),         // jmp 6  [ exit switch ]
7146 	PATCH_END
7147 };
7148 
7149 // The act 4 back rub scene in Yvette's (room 550) doesn't draw the typewriter,
7150 //  desk lamp, and wastebasket when returning from Laura's close-up. These views
7151 //  are added to the room pic and so they are disposed of when the pic changes.
7152 //  This also occurs in Sierra's interpreter.
7153 //
7154 // We fix this by removing the addToPic call from these views so that they are
7155 //  members of the cast like the other views in the room and survive pic change.
7156 //
7157 // Applies to: All Floppy and CD versions
7158 // Responsible method: rm550:init/<noname110>
7159 // Fixes bug #10894
7160 static const uint16 laurabow2SignatureFixDisappearingDeskItems[] = {
7161 	SIG_MAGICDWORD,
7162 	0x39, 0x64,                         // pushi 64
7163 	0x39, 0x7e,                         // pushi 7e
7164 	0x38, SIG_ADDTOOFFSET(+2),          // pushi addToPic
7165 	0x76,                               // push0
7166 	SIG_ADDTOOFFSET(+3),
7167 	0x4a, 0x20,                         // send 20 [ typewriter ... addToPic: ]
7168 	SIG_ADDTOOFFSET(+26),
7169 	0x38, SIG_ADDTOOFFSET(+2),          // pushi addToPic
7170 	0x76,                               // push0
7171 	SIG_ADDTOOFFSET(+3),
7172 	0x4a, 0x18,                         // send 18 [ deskLamp ... addToPic: ]
7173 	SIG_ADDTOOFFSET(+27),
7174 	0x38, SIG_ADDTOOFFSET(+2),          // pushi addToPic
7175 	0x76,                               // push0
7176 	SIG_ADDTOOFFSET(+17),
7177 	0x4a, 0x16,                         // send 16 [ wasteBasket ... addToPic: ... ]
7178 	SIG_END
7179 };
7180 
7181 static const uint16 laurabow2PatchFixDisappearingDeskItems[] = {
7182 	PATCH_ADDTOOFFSET(+4),
7183 	0x32, PATCH_UINT16(0x0001),         // jmp 0001
7184 	PATCH_ADDTOOFFSET(+4),
7185 	0x4a, 0x1c,                         // send 1c [ init typewriter without addToPic ]
7186 	PATCH_ADDTOOFFSET(+26),
7187 	0x32, PATCH_UINT16(0x0001),         // jmp 0001
7188 	PATCH_ADDTOOFFSET(+4),
7189 	0x4a, 0x14,                         // send 14 [ init deskLamp without addToPic ]
7190 	PATCH_ADDTOOFFSET(+27),
7191 	0x32, PATCH_UINT16(0x0001),         // jmp 0001
7192 	PATCH_ADDTOOFFSET(+18),
7193 	0x4a, 0x12,                         // send 12 [ init wasteBasket without addToPic ]
7194 	PATCH_END
7195 };
7196 
7197 // LB2 Floppy 1.0 doesn't initialize act 4 correctly when triggered by finding
7198 //  the dagger, causing the act 4 scene in Yvette's (room 550) to lockup.
7199 //
7200 // The Yvette/Olympia/Steve scene in act 4 (rooms 550 and 510) expects
7201 //  global[111] to be 11. This global tracks Yvette's state throughout acts 3
7202 //  and 4, incrementing as you listen to her conversations and witness her
7203 //  scenes. Some of these are optional, so at the end of act 3 it can be less
7204 //  than 11. rm510:init initializes global[111] to 11 when act 4 is triggered
7205 //  by reporting Ernie's death, but no such initialization occurs when act 4 is
7206 //  triggered by finding the dagger (rooms 610 and 620). What happens when the
7207 //  global isn't 11 depends on its value. Some values, such as 8, cause the act
7208 //  4 scene to never complete and never restore control to the user.
7209 //
7210 // We fix this the way Sierra did in floppy 1.1 and cd versions by setting
7211 //  global[111] to 11 in actBreak:init when act 4 starts so that it's always
7212 //  initialized.
7213 //
7214 // Applies to: Floppy 1.0 English only
7215 // Responsible method: actBreak:<noname150> which is really init
7216 // Fixes bug: #10716
7217 static const uint16 laurabow2SignatureFixAct4Initialization[] = {
7218 	SIG_MAGICDWORD,
7219 	0xa3, 0x08,                         // sal local[8]   [ 1.0 floppy only ]
7220 	0x89, 0x0c,                         // lsg global[0c] [ previous room ]
7221 	0x34, SIG_UINT16(0x026c),           // ldi 026c       [ room 620 ]
7222 	0x1a,                               // eq?
7223 	0x31, 0x05,                         // bnt 5
7224 	0x34, SIG_UINT16(0x0262),           // ldi 0262 [ room 610 ]
7225 	0x33, 0x03,                         // jmp 3
7226 	0x34, SIG_UINT16(0x01fe),           // ldi 01fe [ room 510 ]
7227 	0xa3, 0x00,                         // sal local[0]   [ (previous room == 620) ? 610 : 510 ]
7228 	0x33, 0x2d,                         // jmp 2d   [ exit switch ]
7229 	SIG_END
7230 };
7231 
7232 static const uint16 laurabow2PatchFixAct4Initialization[] = {
7233 	PATCH_ADDTOOFFSET(+2),
7234 	0x35, 0x0b,                         // ldi 0b
7235 	0xa1, 0x6f,                         // sag global[6f] [ global[111d] = 11 ]
7236 	0x89, 0x0c,                         // lsg global[0c] [ previous room ]
7237 	0x34, PATCH_UINT16(0x026c),         // ldi 026c       [ room 620 ]
7238 	0x1a,                               // eq?
7239 	0x39, 0x64,                         // pushi 64
7240 	0x06,                               // mul
7241 	0x38, PATCH_UINT16(0x01fe),         // pushi 01fe
7242 	0x02,                               // add      [ acc = ((previous room == 620) * 100) + 510 ]
7243 	0x32, PATCH_UINT16(0x0013),         // jmp 0013 [ jmp to: sal 0, jmp exit switch ]
7244 	PATCH_END
7245 };
7246 
7247 // The armor exhibit rooms (440 and 448) have event handlers that fail to handle
7248 //  all events, preventing messages from being displayed.
7249 //
7250 // Both armor rooms implement handleEvent to handle joystick events in certain
7251 //  situations, but they only pass move events on to super:handleEvent, blocking
7252 //  all other event types. Clicking on either room does nothing even though both
7253 //  have messages to respond with. This also prevents messages when clicking on
7254 //  Pippin Carter's armor inset. Sierra fixed the armor problem after the first
7255 //  floppy release by adding code to detect and handle the inset's events,
7256 //  instead of handling all events, leaving the room messages broken.
7257 //
7258 // We fix this by handling verb events in both rooms. This fixes room messages
7259 //  in all versions and fixes armor inset messages in English floppy 1.0.
7260 //
7261 // Applies to: All Floppy and CD versions
7262 // Responsible methods: rm440:handleEvent/<noname133>, rm448:handleEvent/<noname133>
7263 // Fixes bugs #10709, #10895
7264 static const uint16 laurabow2SignatureHandleArmorRoomEvents[] = {
7265 	0x87, 0x01,                         // lap 01
7266 	0x4a, 0x04,                         // send 04 [ event type? ]
7267 	SIG_MAGICDWORD,
7268 	0x36,                               // push
7269 	0x34, SIG_UINT16(0x1000),           // ldi 1000 [ move event ]
7270 	0x12,                               // and
7271 	0x31,                               // bnt [ don't handle event ]
7272 	SIG_END
7273 };
7274 
7275 static const uint16 laurabow2PatchHandleArmorRoomEvents[] = {
7276 	PATCH_ADDTOOFFSET(+5),
7277 	0x34, PATCH_UINT16(0x5000),         // ldi 5000 [ move event | verb event ]
7278 	PATCH_END
7279 };
7280 
7281 // The "bugs with meat" in the basement hallway (room 600) can lockup the game
7282 //  if they appear while ego is leaving the room through one of the doors.
7283 //
7284 // bugsWithMeat cues after 5 seconds in the room and runs sDoMeat if no room
7285 //  script is set. sDoMeat:changeState(0) calls handsOff. Ego might already be
7286 //  leaving through the north door in handsOff mode, which is managed by lRS
7287 //  (Leave Room Script), which doesn't prevent sDoMeat from running because lRS
7288 //  isn't set as the room script. If the door is animating when the timer goes
7289 //  off then ego will continue to Wolfe's (room 650) and the unexpected second
7290 //  handsOff will cause handsOn(1) to restore the disabled state and the user
7291 //  will never regain control. If sDoMeat runs after the door animates then
7292 //  ego's movement will be interrupted and the door will be left open and broken.
7293 //  Similar problems occur with the other door in the room.
7294 //
7295 // We fix this by patching bugsWithMeat:cue from testing if the room has no
7296 //  script to instead testing if the user has control before running sDoMeat.
7297 //  All of the room's scripts call handsOff in state 0 and handsOn in their
7298 //  final state so this change just extends the interruption test to include
7299 //  other handsOff scripts.
7300 //
7301 // The signature and patch are duplicated for floppy and cd versions due to
7302 //  User:canControl having different selector values between versions, floppy
7303 //  versions not including selector names, and User:canControl's selector
7304 //  values not appearing in the script being patched.
7305 //
7306 // Applies to: All Floppy and CD versions
7307 // Responsible method: bugsWithMeat:cue/<noname145>
7308 // Fixes bug: #10730
7309 static const uint16 laurabow2FloppySignatureFixBugsWithMeat[] = {
7310 	SIG_MAGICDWORD,
7311 	0x57, 0x32, 0x06,                   // super Actor[32], 6 [ floppy: 32, cd: 31 ]
7312 	0x3a,                               // toss
7313 	0x48,                               // ret [ end of bugsWithMeat:<noname300> aka doVerb ]
7314 	0x38, SIG_UINT16(0x008e),           // pushi 008e [ <noname142> aka script ]
7315 	0x76,                               // push0
7316 	0x81, 0x02,                         // lag global[2] [ rm600 ]
7317 	0x4a, 0x04,                         // send 4
7318 	0x31, 0x0e,                         // bnt 0e [ run sDoMeat if not rm600:<noname142>? ]
7319 	SIG_END
7320 };
7321 
7322 static const uint16 laurabow2FloppyPatchFixBugsWithMeat[] = {
7323 	PATCH_ADDTOOFFSET(+5),
7324 	0x38, PATCH_UINT16(0x00ed),         // pushi 00ed [ <noname237> aka canControl ]
7325 	0x76,                               // push0
7326 	0x81, 0x50,                         // lag global[50] [ User ]
7327 	0x4a, 0x04,                         // send 4
7328 	0x2f, 0x0e,                         // bt 0e [ run sDoMeat if User:<noname237>? ]
7329 	PATCH_END
7330 };
7331 
7332 // cd version of the above signature/patch
7333 static const uint16 laurabow2CDSignatureFixBugsWithMeat[] = {
7334 	SIG_MAGICDWORD,
7335 	0x57, 0x31, 0x06,                   // super Actor[31], 6 [ floppy: 32, cd: 31 ]
7336 	0x3a,                               // toss
7337 	0x48,                               // ret [ end of bugsWithMeat:doVerb ]
7338 	0x38, SIG_UINT16(0x008e),           // pushi 008e [ script ]
7339 	0x76,                               // push0
7340 	0x81, 0x02,                         // lag global[2] [ rm600 ]
7341 	0x4a, 0x04,                         // send 4
7342 	0x31, 0x0e,                         // bnt 0e [ run sDoMeat if not rm600:script? ]
7343 	SIG_END
7344 };
7345 
7346 static const uint16 laurabow2CDPatchFixBugsWithMeat[] = {
7347 	PATCH_ADDTOOFFSET(+5),
7348 	0x38, PATCH_UINT16(0x00f6),         // pushi 00f6 [ canControl ]
7349 	0x76,                               // push0
7350 	0x81, 0x50,                         // lag global[50] [ User ]
7351 	0x4a, 0x04,                         // send 4
7352 	0x2f, 0x0e,                         // bt 0e [ run sDoMeat if User:canControl? ]
7353 	PATCH_END
7354 };
7355 
7356 // LB2 CD ends act 5 in the middle of the finale music instead of waiting for
7357 //  it to complete. This is a script bug and occurs in Sierra's interpreter.
7358 //
7359 // When catching the killer in room 480, sWrapMusic is used to play the chomp
7360 //  sound followed by the finale music. sWrapMusic uses localSound to play both
7361 //  resources. sOrileyCaught:doit waits for the music to complete by testing
7362 //  localSound:prevSignal for -1 before proceeding to act 6. This worked in
7363 //  floppy versions.
7364 //
7365 // The problem is that CD versions include a newer Sound class with different
7366 //  behavior. This new Sound:play doesn't call Sound:init on subsequent plays.
7367 //  Sound:init is what initializes prevSignal to 0, and so localSound:prevSignal
7368 //  is no longer re-initialized from -1 to 0 after playing the chomp, causing
7369 //  sOrileyCaught:doit to treat the music as having immediately completed.
7370 //
7371 // We fix this by changing sOrileyCaught:doit to instead test localSound:handle
7372 //  to determine if the music has completed. Sound:handle is always set when
7373 //  playing and cleared when stopped or disposed.
7374 //
7375 // Applies to: All CD versions
7376 // Responsible method: sOrileyCaught:doit
7377 // Fixes bug: #10808
7378 static const uint16 laurabow2CDSignatureFixAct5FinaleMusic[] = {
7379 	SIG_MAGICDWORD,
7380 	0x38, SIG_UINT16(0x00ab),           // pushi 00ab [ prevSignal ]
7381 	0x76,                               // push0
7382 	0x72, SIG_UINT16(0x083a),           // lofsa localSound
7383 	0x4a, 0x04,                         // send 4
7384 	0x36,                               // push
7385 	0x35, 0xff,                         // ldi ff
7386 	SIG_END
7387 };
7388 
7389 static const uint16 laurabow2CDPatchFixAct5FinaleMusic[] = {
7390 	0x38, PATCH_UINT16(0x005a),         // pushi 005a [ handle ]
7391 	PATCH_ADDTOOFFSET(+7),
7392 	0x35, 0x00,                         // ldi 00
7393 	PATCH_END
7394 };
7395 
7396 // LB2 does a speed test during startup (room 28) to determine the initial
7397 //  detail level and to use for pacing later scenes. Even with the highest
7398 //  score the detail level is only set to medium instead of highest like
7399 //  other SCI games.
7400 //
7401 // Platforms such as iOS can introduce a lag during game initialization that
7402 //  causes the speed test to occasionally get the lowest score, causing
7403 //  detail to be initialized to lowest and subsequent scenes such as the act 5
7404 //  chase scene to go very slow.
7405 //
7406 // We patch startGame:doit to ignore the score and always set the highest detail
7407 //  level and cpu speed so that detail needn't be manually increased and act 5
7408 //  behaves consistently. This also helps touchscreen devices as the game's
7409 //  detail slider is prohibitively difficult to manually set to highest without
7410 //  switching from the default touch mode.
7411 //
7412 // Applies to: All Floppy and CD versions
7413 // Responsible method: startGame:doit/<noname57>
7414 // Fixes bug: #10761
7415 static const uint16 laurabow2SignatureDisableSpeedTest[] = {
7416 	0x89, 0x57,                         // lsg global[57] [ speed test result ]
7417 	SIG_MAGICDWORD,
7418 	0x35, 0x03,                         // ldi 03 [ low-speed threshold ]
7419 	0x24,                               // le?
7420 	0x31, 0x04,                         // bnt 04
7421 	0x35, 0x01,                         // ldi 01 [ lowest detail ]
7422 	0x33, 0x0d,                         // jmp 0d
7423 	0x89, 0x57,                         // lsg global[57] [ speed test result ]
7424 	SIG_END
7425 };
7426 
7427 static const uint16 laurabow2PatchDisableSpeedTest[] = {
7428 	0x38, PATCH_UINT16(0x0005),         // pushi 0005
7429 	0x81, 0x01,                         // lag global[1]
7430 	0x4a, 0x06,                         // send 06 [ LB2:detailLevel = 5, max detail ]
7431 	0x35, 0x0f,                         // ldi 0f
7432 	0xa1, 0x57,                         // sag global[57] [ global[57] = f, max cpu speed ]
7433 	0x33, 0x10,                         // jmp 10 [ continue init ]
7434 	PATCH_END
7435 };
7436 
7437 // LB2CD reduces the music volume significantly during the introduction when
7438 //  characters talk while disembarking the ship in room 120. This is done so
7439 //  that their speech can be heard but it also occurs in text-only mode.
7440 //
7441 // Interestingly, this is the only script that manually reduces volume during
7442 //  speech, as it's a workaround for bugs in the Narrator and Talker scripts.
7443 //  They're supposed to automatically reduce music volume, but this scheme fails
7444 //  when multiple appear at once, which seems to only occur in the introduction.
7445 //
7446 // We patch the introduction to skip the volume reduction when in text-only mode
7447 //  so that the music doesn't abruptly go away and come back.
7448 //
7449 // Applies to: All CD versions
7450 // Responsible method: sDisembark:changeState
7451 // Fixes bug #10916
7452 static const uint16 laurabow2CDSignatureIntroVolumeChange[] = {
7453 	0x31, 0x2a,                         // bnt 2a [ state 3 ]
7454 	SIG_ADDTOOFFSET(+2),
7455 	0x31, 0x1f,                         // bnt 1f
7456 	SIG_ADDTOOFFSET(+28),
7457 	0x32, SIG_UINT16(0x00f7),           // jmp 00f7 [ end of method ]
7458 	0x35, 0x02,                         // ldi 02
7459 	0x65, 0x1a,                         // aTop cycles [ cycles = 2 ]
7460 	0x32, SIG_UINT16(0x00f0),           // jmp 00f0 [ end of method ]
7461 	0x3c,                               // dup
7462 	0x35, 0x03,                         // ldi 03
7463 	0x1a,                               // eq?
7464 	0x31, 0x25,                         // bnt 25 [ state 4 ]
7465 	0x38, SIG_SELECTOR16(setVol),       // pushi setVol
7466 	0x78,                               // push1
7467 	SIG_MAGICDWORD,
7468 	0x39, 0x28,                         // pushi 28
7469 	0x81, 0x66,                         // lag 66
7470 	0x4a, 0x06,                         // send 06 [ gameMusic1 setVol: 40 ]
7471 	SIG_END
7472 };
7473 
7474 static const uint16 laurabow2CDPatchIntroVolumeChange[] = {
7475 	0x31, 0x25,                         // bnt 25 [ state 3 ]
7476 	PATCH_ADDTOOFFSET(+2),
7477 	0x31, 0x1e,                         // bnt 1e
7478 	PATCH_ADDTOOFFSET(+28),
7479 	0x3a,                               // toss
7480 	0x48,                               // ret
7481 	0x7a,                               // push2
7482 	0x69, 0x1a,                         // sTop cycles [ cycles = 2 ]
7483 	0x3c,                               // dup
7484 	0x35, 0x03,                         // ldi 03
7485 	0x1a,                               // eq?
7486 	0x31, 0x2a,                         // bnt 2a [ state 4 ]
7487 	0x89, 0x5a,                         // lsg 5a [ message mode ]
7488 	0x1a,                               // eq?    [ is text-only mode? ]
7489 	0x2f, 0x0a,                         // bt 0a  [ skip volume reduction ]
7490 	PATCH_END
7491 };
7492 
7493 // Laura Bow 2 CD resets the audio mode to speech on init/restart
7494 //  We already sync the settings from ScummVM (see SciEngine::syncIngameAudioOptions())
7495 //  and this script code would make it impossible to see the intro using "dual" mode w/o using debugger command
7496 //  That's why we remove the corresponding code
7497 // Patched method: LB2::init, rm100::init
7498 static const uint16 laurabow2CDSignatureAudioTextSupportModeReset[] = {
7499 	SIG_MAGICDWORD,
7500 	0x35, 0x02,                         // ldi 02
7501 	0xa1, 0x5a,                         // sag global[5a]
7502 	SIG_END
7503 };
7504 
7505 static const uint16 laurabow2CDPatchAudioTextSupportModeReset[] = {
7506 	0x34, PATCH_UINT16(0x0001),         // ldi 0001 (waste bytes)
7507 	0x18,                               // not (waste bytes)
7508 	PATCH_END
7509 };
7510 
7511 // Directly use global[5a] for view-cel id
7512 //  That way it's possible to use a new "dual" mode view in the game menu
7513 // View 995, loop 13, cel 0 -> "text"
7514 // View 995, loop 13, cel 1 -> "speech"
7515 // View 995, loop 13, cel 2 -> "dual" (this view is injected by us into the game)
7516 // Patched method: gcWin::open
7517 static const uint16 laurabow2CDSignatureAudioTextMenuSupport1[] = {
7518 	SIG_MAGICDWORD,
7519 	0x89, 0x5a,                         // lsg global[5a]
7520 	0x35, 0x02,                         // ldi 02
7521 	0x1a,                               // eq?
7522 	0x36,                               // push
7523 	SIG_END
7524 };
7525 
7526 static const uint16 laurabow2CDPatchAudioTextMenuSupport1[] = {
7527 	PATCH_ADDTOOFFSET(+2),
7528 	0x35, 0x01,                         // ldi 01
7529 	0x04,                               // sub
7530 	PATCH_END
7531 };
7532 
7533 //  Adds another button state for the text/audio button. We currently use the "speech" view for "dual" mode.
7534 // Patched method: iconMode::doit
7535 static const uint16 laurabow2CDSignatureAudioTextMenuSupport2[] = {
7536 	SIG_MAGICDWORD,
7537 	0x89, 0x5a,                         // lsg global[5a]
7538 	0x3c,                               // dup
7539 	0x1a,                               // eq?
7540 	0x31, 0x0a,                         // bnt [set text mode]
7541 	0x35, 0x02,                         // ldi 02
7542 	0xa1, 0x5a,                         // sag global[5a]
7543 	0x35, 0x01,                         // ldi 01
7544 	0xa5, 0x00,                         // sat temp[0]
7545 	0x33, 0x0e,                         // jmp [draw cel code]
7546 	0x3c,                               // dup
7547 	0x35, 0x02,                         // ldi 02
7548 	0x1a,                               // eq?
7549 	0x31, 0x08,                         // bnt [draw cel code]
7550 	0x35, 0x01,                         // ldi 01
7551 	0xa1, 0x5a,                         // sag global[5a]
7552 	0x35, 0x00,                         // ldi 00
7553 	0xa5, 0x00,                         // sat temp[0]
7554 	0x3a,                               // toss
7555 	SIG_END
7556 };
7557 
7558 static const uint16 laurabow2CDPatchAudioTextMenuSupport2[] = {
7559 	0x81, 0x5a,                         // lag global[5a]
7560 	0x78,                               // push1
7561 	0x02,                               // add
7562 	0xa1, 0x5a,                         // sag global[5a]
7563 	0x36,                               // push
7564 	0x35, 0x03,                         // ldi 03
7565 	0x1e,                               // gt?
7566 	0x31, 0x03,                         // bnt [skip over]
7567 	0x78,                               // push1
7568 	0xa9, 0x5a,                         // ssg global[5a]
7569 	0x89, 0x5a,                         // lsg global[5a]
7570 	0x35, 0x01,                         // ldi 01
7571 	0x04,                               // sub
7572 	0xa5, 0x00,                         // sat temp[0] [ calculate global[5a] - 1 to use as view cel id ]
7573 	0x33, 0x07,                         // jmp [draw cel code, don't do toss]
7574 	PATCH_END
7575 };
7576 
7577 //          script, description,                                      signature                                      patch
7578 static const SciScriptPatcherEntry laurabow2Signatures[] = {
7579 	{  true,   560, "CD: painting closing immediately",               1, laurabow2CDSignaturePaintingClosing,            laurabow2CDPatchPaintingClosing },
7580 	{  true,     0, "CD: fix problematic icon bar",                   1, laurabow2CDSignatureFixProblematicIconBar,      laurabow2CDPatchFixProblematicIconBar },
7581 	{  true,    90, "CD: fix yvette's tut response",                  1, laurabow2CDSignatureFixYvetteTutResponse,       laurabow2CDPatchFixYvetteTutResponse },
7582 	{  true,   350, "CD/Floppy: museum party fix entering south 1/2", 1, laurabow2SignatureMuseumPartyFixEnteringSouth1, laurabow2PatchMuseumPartyFixEnteringSouth1 },
7583 	{  true,   350, "CD/Floppy: museum party fix entering south 2/2", 1, laurabow2SignatureMuseumPartyFixEnteringSouth2, laurabow2PatchMuseumPartyFixEnteringSouth2 },
7584 	{  true,   430, "CD/Floppy: make wired east door persistent",     1, laurabow2SignatureRememberWiredEastDoor,        laurabow2PatchRememberWiredEastDoor },
7585 	{  true,   430, "CD/Floppy: fix wired east door",                 1, laurabow2SignatureFixWiredEastDoor,             laurabow2PatchFixWiredEastDoor },
7586 	{  true,   448, "CD/Floppy: fix armor hall door pathfinding",     1, laurabow2SignatureFixArmorHallDoorPathfinding,  laurabow2PatchFixArmorHallDoorPathfinding },
7587 	{  true,   460, "CD/Floppy: fix crate room east door lockup",     1, laurabow2SignatureFixCrateRoomEastDoorLockup,   laurabow2PatchFixCrateRoomEastDoorLockup },
7588 	{  true,  2660, "CD/Floppy: fix elevator lockup",                 1, laurabow2SignatureFixElevatorLockup,            laurabow2PatchFixElevatorLockup },
7589 	{  true,   550, "CD/Floppy: fix back rub east entrance lockup",   1, laurabow2SignatureFixBackRubEastEntranceLockup, laurabow2PatchFixBackRubEastEntranceLockup },
7590 	{  true,   550, "CD/Floppy: fix disappearing desk items",         1, laurabow2SignatureFixDisappearingDeskItems,     laurabow2PatchFixDisappearingDeskItems },
7591 	{  true,    26, "Floppy: fix act 4 initialization",               1, laurabow2SignatureFixAct4Initialization,        laurabow2PatchFixAct4Initialization },
7592 	{  true,   440, "CD/Floppy: handle armor room events",            1, laurabow2SignatureHandleArmorRoomEvents,        laurabow2PatchHandleArmorRoomEvents },
7593 	{  true,   448, "CD/Floppy: handle armor hall room events",       1, laurabow2SignatureHandleArmorRoomEvents,        laurabow2PatchHandleArmorRoomEvents },
7594 	{  true,   600, "Floppy: fix bugs with meat",                     1, laurabow2FloppySignatureFixBugsWithMeat,        laurabow2FloppyPatchFixBugsWithMeat },
7595 	{  true,   600, "CD: fix bugs with meat",                         1, laurabow2CDSignatureFixBugsWithMeat,            laurabow2CDPatchFixBugsWithMeat },
7596 	{  true,   480, "CD: fix act 5 finale music",                     1, laurabow2CDSignatureFixAct5FinaleMusic,         laurabow2CDPatchFixAct5FinaleMusic },
7597 	{  true,    28, "CD/Floppy: disable speed test",                  1, laurabow2SignatureDisableSpeedTest,             laurabow2PatchDisableSpeedTest },
7598 	{  true,   120, "CD: disable intro volume change in text mode",   1, laurabow2CDSignatureIntroVolumeChange,          laurabow2CDPatchIntroVolumeChange },
7599 	// King's Quest 6 and Laura Bow 2 share basic patches for audio + text support
7600 	{ false,   924, "CD: audio + text support 1",                     1, kq6laurabow2CDSignatureAudioTextSupport1,       kq6laurabow2CDPatchAudioTextSupport1 },
7601 	{ false,   924, "CD: audio + text support 2",                     1, kq6laurabow2CDSignatureAudioTextSupport2,       kq6laurabow2CDPatchAudioTextSupport2 },
7602 	{ false,   924, "CD: audio + text support 3",                     1, kq6laurabow2CDSignatureAudioTextSupport3,       kq6laurabow2CDPatchAudioTextSupport3 },
7603 	{ false,   928, "CD: audio + text support 4",                     1, kq6laurabow2CDSignatureAudioTextSupport4,       kq6laurabow2CDPatchAudioTextSupport4 },
7604 	{ false,   928, "CD: audio + text support 5",                     2, kq6laurabow2CDSignatureAudioTextSupport5,       kq6laurabow2CDPatchAudioTextSupport5 },
7605 	{ false,     0, "CD: audio + text support disable mode reset",    1, laurabow2CDSignatureAudioTextSupportModeReset,  laurabow2CDPatchAudioTextSupportModeReset },
7606 	{ false,   100, "CD: audio + text support disable mode reset",    1, laurabow2CDSignatureAudioTextSupportModeReset,  laurabow2CDPatchAudioTextSupportModeReset },
7607 	{ false,    24, "CD: audio + text support LB2 menu 1",            1, laurabow2CDSignatureAudioTextMenuSupport1,      laurabow2CDPatchAudioTextMenuSupport1 },
7608 	{ false,    24, "CD: audio + text support LB2 menu 2",            1, laurabow2CDSignatureAudioTextMenuSupport2,      laurabow2CDPatchAudioTextMenuSupport2 },
7609 	SCI_SIGNATUREENTRY_TERMINATOR
7610 };
7611 
7612 // ===========================================================================
7613 // Mother Goose SCI1/SCI1.1
7614 // MG::replay somewhat calculates the savedgame-id used when saving again
7615 //  this doesn't work right and we remove the code completely.
7616 //  We set the savedgame-id directly right after restoring in kRestoreGame.
7617 //  We also draw the background picture in here instead.
7618 //  This Mixed Up Mother Goose draws the background picture before restoring,
7619 //  instead of doing it properly in MG::replay. This fixes graphic issues,
7620 //  when restoring from GMM.
7621 //
7622 // Applies to at least: English SCI1 CD, English SCI1.1 floppy, Japanese FM-Towns
7623 // Responsible method: MG::replay (script 0)
7624 static const uint16 mothergoose256SignatureReplay[] = {
7625 	0x7a,                            // push2
7626 	0x78,                            // push1
7627 	0x5b, 0x00, 0xbe,                // lea global[BEh]
7628 	0x36,                            // push
7629 	0x43, 0x70, 0x04,                // callk MemorySegment
7630 	0x7a,                            // push2
7631 	0x5b, 0x00, 0xbe,                // lea global[BEh]
7632 	0x36,                            // push
7633 	0x76,                            // push0
7634 	0x43, 0x62, 0x04,                // callk StrAt
7635 	0xa1, 0xaa,                      // sag global[AAh]
7636 	0x7a,                            // push2
7637 	0x5b, 0x00, 0xbe,                // lea global[BEh]
7638 	0x36,                            // push
7639 	0x78,                            // push1
7640 	0x43, 0x62, 0x04,                // callk StrAt
7641 	0x36,                            // push
7642 	0x35, 0x20,                      // ldi 20
7643 	0x04,                            // sub
7644 	0xa1, SIG_ADDTOOFFSET(+1),       // sag global[57h], for FM-Towns: sag global[9Dh]
7645 	// 35 bytes
7646 	0x39, 0x03,                      // pushi 03
7647 	0x89, SIG_ADDTOOFFSET(+1),       // lsg global[1Dh], for FM-Towns: lsg global[1Eh]
7648 	0x76,                            // push0
7649 	0x7a,                            // push2
7650 	0x5b, 0x00, 0xbe,                // lea global[BEh]
7651 	0x36,                            // push
7652 	0x7a,                            // push2
7653 	0x43, 0x62, 0x04,                // callk StrAt
7654 	0x36,                            // push
7655 	0x35, 0x01,                      // ldi 01
7656 	0x04,                            // sub
7657 	0x36,                            // push
7658 	0x43, 0x62, 0x06,                // callk StrAt
7659 	// 22 bytes
7660 	0x7a,                            // push2
7661 	0x5b, 0x00, 0xbe,                // lea global[BEh]
7662 	0x36,                            // push
7663 	0x39, 0x03,                      // pushi 03
7664 	0x43, 0x62, 0x04,                // callk StrAt
7665 	// 10 bytes
7666 	0x36,                            // push
7667 	0x35, SIG_MAGICDWORD, 0x20,      // ldi 20
7668 	0x04,                            // sub
7669 	0xa1, 0xb3,                      // sag global[B3h]
7670 	// 6 bytes
7671 	SIG_END
7672 };
7673 
7674 static const uint16 mothergoose256PatchReplay[] = {
7675 	0x39, 0x06,                      // pushi 06
7676 	0x76,                            // push0
7677 	0x76,                            // push0
7678 	0x38, PATCH_UINT16(200),         // pushi 200d
7679 	0x38, PATCH_UINT16(320),         // pushi 320d
7680 	0x76,                            // push0
7681 	0x76,                            // push0
7682 	0x43, 0x15, 0x0c,                // callk SetPort -> set picture port to full screen
7683 	// 15 bytes
7684 	0x39, 0x04,                      // pushi 04
7685 	0x3c,                            // dup
7686 	0x76,                            // push0
7687 	0x38, PATCH_UINT16(255),         // pushi 255d
7688 	0x76,                            // push0
7689 	0x43, 0x6f, 0x08,                // callk Palette -> set intensity to 0 for all colors
7690 	// 11 bytes
7691 	0x7a,                            // push2
7692 	0x38, PATCH_UINT16(800),         // pushi 800
7693 	0x76,                            // push0
7694 	0x43, 0x08, 0x04,                // callk DrawPic -> draw picture 800
7695 	// 8 bytes
7696 	0x39, 0x06,                      // pushi 06
7697 	0x39, 0x0c,                      // pushi 0Ch
7698 	0x76,                            // push0
7699 	0x76,                            // push0
7700 	0x38, PATCH_UINT16(200),         // pushi 200
7701 	0x38, PATCH_UINT16(320),         // pushi 320
7702 	0x78,                            // push1
7703 	0x43, 0x6c, 0x0c,                // callk Graph -> send everything to screen
7704 	// 16 bytes
7705 	0x39, 0x06,                      // pushi 06
7706 	0x76,                            // push0
7707 	0x76,                            // push0
7708 	0x38, PATCH_UINT16(156),         // pushi 156d
7709 	0x38, PATCH_UINT16(258),         // pushi 258d
7710 	0x39, 0x03,                      // pushi 03
7711 	0x39, 0x04,                      // pushi 04
7712 	0x43, 0x15, 0x0c,                // callk SetPort -> set picture port back
7713 	// 17 bytes
7714 	0x34, PATCH_UINT16(0x0000),      // ldi 0000 (dummy)
7715 	0x34, PATCH_UINT16(0x0000),      // ldi 0000 (dummy)
7716 	PATCH_END
7717 };
7718 
7719 // when saving, it also checks if the savegame ID is below 13.
7720 //  we change this to check if below 113 instead
7721 //
7722 // Applies to at least: English SCI1 CD, English SCI1.1 floppy, Japanese FM-Towns
7723 // Responsible method: Game::save (script 994 for SCI1), MG::save (script 0 for SCI1.1)
7724 static const uint16 mothergoose256SignatureSaveLimit[] = {
7725 	0x89, SIG_MAGICDWORD, 0xb3,      // lsg global[b3]
7726 	0x35, 0x0d,                      // ldi 0d
7727 	0x20,                            // ge?
7728 	SIG_END
7729 };
7730 
7731 static const uint16 mothergoose256PatchSaveLimit[] = {
7732 	PATCH_ADDTOOFFSET(+2),
7733 	0x35, 0x0d + SAVEGAMEID_OFFICIALRANGE_START, // ldi 113d
7734 	PATCH_END
7735 };
7736 
7737 // Clicking a button on the main menu as the screen fades in causes a message to
7738 //  be sent to a non-object when coming from the Sierra logo. This also crashes
7739 //  the original. If returning from a menu then the previous button is clicked.
7740 //
7741 // After the main menu screen fades in, IconBar:doit polls for events in a loop
7742 //  until a mouse click or key press occurs over a button. choices:show then
7743 //  calls highlightedIcon:message to run the correct action. IconBar:doit
7744 //  updates this property on each iteration unless the event is a mouse click.
7745 //  If a button is clicked before polling begins then IconBar:doit exits on its
7746 //  first iteration without setting highlightedIcon. This property is never
7747 //  reset and its stale value can get reused when returning from a menu.
7748 //
7749 // These problems would be simple to fix in the event polling loop, but that
7750 //  code is in the IconBar and GameControls classes that affect each screen.
7751 //  Instead we surround the loop with fixes. The event queue is now drained of
7752 //  mouse clicks and highlightedIcon is cleared before polling. Afterwards,
7753 //  highlightedIcon is verified and the loop is repeated if it's not set. This
7754 //  discards clicks during fade-in, resets state when canceling a subsequent
7755 //  menu, and prevents timing edge cases from crashing should a click ever
7756 //  register on the loop's first iteration. We make room for this by overwriting
7757 //  the code that initializes the cursor when kHaveMouse reports no mouse, as
7758 //  that doesn't apply to ScummVM. The CD version's menu was rewritten and
7759 //  doesn't have these problems.
7760 //
7761 // Applies to: English PC Floppy
7762 // Responsible method: choices:show
7763 // Fixes bug #9681
7764 static const uint16 mothergoose256SignatureMainMenuCrash[] = {
7765 	0x43, SIG_MAGICDWORD, 0x27, 0x00,   // callk HaveMouse
7766 	0x31, 0x0e,                         // bnt 0e [ no mouse ]
7767 	SIG_ADDTOOFFSET(+0x0b),
7768 	0x32, SIG_UINT16(0x0036),           // jmp 0036 [ skip no-mouse code ]
7769 	SIG_ADDTOOFFSET(+0x1a),
7770 	// no mouse
7771 	0x76,                               // push0
7772 	0x63, 0x1e,                         // pToa curIcon
7773 	0x4a, 0x04,                         // send 04
7774 	0x04,                               // sub
7775 	0x36,                               // push
7776 	0x35, 0x02,                         // ldi 02
7777 	0x08,                               // div
7778 	0x02,                               // add
7779 	0x36,                               // push
7780 	0x39, 0x08,                         // pushi 08
7781 	0x76,                               // push0
7782 	0x63, 0x1e,                         // pToa curIcon
7783 	0x4a, 0x04,                         // send 04
7784 	0x36,                               // push
7785 	0x35, 0x03,                         // ldi 03
7786 	0x04,                               // sub
7787 	// event polling loop
7788 	0x36,                               // push
7789 	0x81, 0x01,                         // lag 01
7790 	0x4a, 0x0c,                         // send 0c
7791 	0x39, SIG_SELECTOR8(doit),          // pushi doit
7792 	0x76,                               // push0
7793 	0x39, SIG_SELECTOR8(hide),          // pushi hide
7794 	0x76,                               // push0
7795 	0x54, 0x08,                         // self 08 [ self doit: hide: ]
7796 	SIG_END
7797 };
7798 
7799 static const uint16 mothergoose256PatchMainMenuCrash[] = {
7800 	PATCH_ADDTOOFFSET(+3),
7801 	0x33, 0x00,                         // jmp 00 [ always run mouse code ]
7802 	PATCH_ADDTOOFFSET(+0x0b),
7803 	0x32, PATCH_UINT16(0x001a),         // jmp 001a [ skip no-mouse code ]
7804 	PATCH_ADDTOOFFSET(+0x1a),
7805 	0x76,                               // push0
7806 	0x69, 0x20,                         // sTop highlightedIcon [ highlightedIcon = 0 ]
7807 	0x39, PATCH_SELECTOR8(new),         // pushi new
7808 	0x78,                               // push1
7809 	0x39, 0x03,                         // pushi 03 [ mouse down/up ]
7810 	0x51, 0x07,                         // class Event
7811 	0x4a, 0x06,                         // send 06 [ Event new: 3 ]
7812 	0x39, PATCH_SELECTOR8(dispose),     // pushi dispose
7813 	0x76,                               // push0
7814 	0x39, PATCH_SELECTOR8(type),        // pushi type
7815 	0x76,                               // push0
7816 	0x4a, 0x08,                         // send 08 [ event dispose: type? ]
7817 	0x2f, 0xed,                         // bt ed [ loop until no more mouse down/up events ]
7818 	0x39, PATCH_SELECTOR8(doit),        // pushi doit
7819 	0x76,                               // push0
7820 	0x54, 0x04,                         // self 04 [ self doit: ]
7821 	0x63, 0x20,                         // pToa highlightedIcon
7822 	0x31, 0xf7,                         // bnt f7 [ repeat event loop until highlightedIcon is set ]
7823 	PATCH_ADDTOOFFSET(+3),
7824 	0x54, 0x04,                         // self 04 [ self hide: ]
7825 	PATCH_END
7826 };
7827 
7828 //          script, description,                                      signature                             patch
7829 static const SciScriptPatcherEntry mothergoose256Signatures[] = {
7830 	{  true,     0, "replay save issue",                           1, mothergoose256SignatureReplay,        mothergoose256PatchReplay },
7831 	{  true,     0, "save limit dialog (SCI1.1)",                  1, mothergoose256SignatureSaveLimit,     mothergoose256PatchSaveLimit },
7832 	{  true,   994, "save limit dialog (SCI1)",                    1, mothergoose256SignatureSaveLimit,     mothergoose256PatchSaveLimit },
7833 	{  true,    90, "main menu button crash",                      1, mothergoose256SignatureMainMenuCrash, mothergoose256PatchMainMenuCrash },
7834 	SCI_SIGNATUREENTRY_TERMINATOR
7835 };
7836 
7837 #ifdef ENABLE_SCI32
7838 #pragma mark -
7839 #pragma mark Mixed-up Mother Goose Deluxe
7840 
7841 // The game uses pic 10005 to render the Sierra logo, but then it also
7842 // initialises a logo object with view 502 on the same priority as the pic. In
7843 // the original interpreter, it is dumb luck which is drawn first (based on the
7844 // order of the memory IDs), though usually the pic is drawn first because not
7845 // many objects have been created at the start of the game. In ScummVM, the
7846 // renderer guarantees a sort order based on the creation order of screen items,
7847 // and since the view is created after the pic, it wins and is drawn on top.
7848 // This patch stops the view object from being created at all.
7849 //
7850 // Applies to at least: English CD from King's Quest Collection
7851 // Responsible method: sShowLogo::changeState
7852 static const uint16 mothergooseHiresLogoSignature[] = {
7853 	0x38, SIG_SELECTOR16(init),      // pushi init ($8e)
7854 	SIG_MAGICDWORD,
7855 	0x76,                            // push0
7856 	0x72, SIG_UINT16(0x0082),        // lofsa logo[82]
7857 	0x4a, SIG_UINT16(0x0004),        // send $4
7858 	SIG_END
7859 };
7860 
7861 static const uint16 mothergooseHiresLogoPatch[] = {
7862 	0x33, 0x08, // jmp [past bad logo init]
7863 	PATCH_END
7864 };
7865 
7866 // After finishing the rhyme at the fountain, a horse will appear and walk
7867 // across the screen. The priority of the horse is set too high, so it is
7868 // rendered in front of the fountain instead of behind the fountain. This patch
7869 // corrects the priority so the horse draws behind the fountain.
7870 //
7871 // Applies to at least: English CD from King's Quest Collection
7872 // Responsible method: rhymeScript::changeState
7873 static const uint16 mothergooseHiresHorseSignature[] = {
7874 	SIG_MAGICDWORD,
7875 	0x39, SIG_SELECTOR8(setPri), // pushi setPri ($4a)
7876 	0x78,                        // push1
7877 	0x38, SIG_UINT16(0x00b7),    // pushi $b7
7878 	SIG_END
7879 };
7880 
7881 static const uint16 mothergooseHiresHorsePatch[] = {
7882 	PATCH_ADDTOOFFSET(+3),    // pushi setPri, push1
7883 	0x38, PATCH_UINT16(0x59), // pushi $59
7884 	PATCH_END
7885 };
7886 
7887 //          script, description,                                      signature                         patch
7888 static const SciScriptPatcherEntry mothergooseHiresSignatures[] = {
7889 	{  true,     0, "disable volume reset on startup (1/2)",       2, sci2VolumeResetSignature,         sci2VolumeResetPatch },
7890 	{  true,    90, "disable volume reset on startup (2/2)",       1, sci2VolumeResetSignature,         sci2VolumeResetPatch },
7891 	{  true,   108, "fix bad logo rendering",                      1, mothergooseHiresLogoSignature,    mothergooseHiresLogoPatch },
7892 	{  true,   318, "fix bad horse z-index",                       1, mothergooseHiresHorseSignature,   mothergooseHiresHorsePatch },
7893 	SCI_SIGNATUREENTRY_TERMINATOR
7894 };
7895 
7896 #pragma mark -
7897 #pragma mark Phantasmagoria
7898 
7899 // Phantasmagoria persists audio volumes in the save games, but ScummVM manages
7900 // game volumes through the launcher, so stop the game from overwriting the
7901 // ScummVM volumes with volumes from save games.
7902 // Applies to at least: English CD
7903 // Fixes bug: #9700
7904 static const uint16 phant1SavedVolumeSignature[] = {
7905 	0x7a,                           // push2
7906 	0x39, 0x08,                     // pushi 8
7907 	0x38, SIG_SELECTOR16(readWord), // pushi readWord ($20b)
7908 	0x76,                           // push0
7909 	0x72, SIG_UINT16(0x13c),        // lofsa $13c (PREF.DAT)
7910 	0x4a, SIG_UINT16(0x04),         // send 4
7911 	SIG_MAGICDWORD,
7912 	0xa1, 0xbc,                     // sag global[$bc]
7913 	0x36,                           // push
7914 	0x43, 0x76, SIG_UINT16(0x04),   // callk DoAudio[76], 4
7915 	0x7a,                           // push2
7916 	0x76,                           // push0
7917 	0x38, SIG_SELECTOR16(readWord), // pushi readWord ($20b)
7918 	0x76,                           // push0
7919 	0x72, SIG_UINT16(0x13c),        // lofsa $13c (PREF.DAT)
7920 	0x4a, SIG_UINT16(0x04),         // send 4
7921 	0xa1, 0xbb,                     // sag global[$bb]
7922 	0x36,                           // push
7923 	0x43, 0x75, SIG_UINT16(0x04),   // callk DoSound[75], 4
7924 	SIG_END
7925 };
7926 
7927 static const uint16 phant1SavedVolumePatch[] = {
7928 	0x32, PATCH_UINT16(0x0024),     // jmp [skip the whole sig, to prefFile::close]
7929 	PATCH_END
7930 };
7931 
7932 // Phantasmagoria performs an incomplete initialisation of a rat view when
7933 // exiting the alcove in the basement any time after chapter 3 when flag 26 is
7934 // not set. This causes the rat view to be rendered with the same origin and
7935 // priority as the background picture for the game plane, triggering last ditch
7936 // sorting of the screen items in the renderer. This happens to work OK most of
7937 // the time in SSCI because the last ditch sort uses memory handle indexes, and
7938 // the index of the rat seems to usually end up below the index for the
7939 // background pic, so the rat's screen item is submitted before the background,
7940 // ensuring that the background palette overrides the rat view's palette. In
7941 // ScummVM, last ditch sorting operates using the creation order of screen
7942 // items, so the rat ends up always sorting above the background, which causes
7943 // the background palette to get replaced by the rat palette, which corrupts the
7944 // background. This patch stops the game script from initialising the bad rat
7945 // view entirely.
7946 // Applies to at least: English CD, French CD
7947 // Fixes bug: #9957
7948 static const uint16 phant1RatSignature[] = {
7949 	SIG_MAGICDWORD,
7950 	0x78,                               // push1
7951 	0x39, 0x1a,                         // pushi $1a
7952 	0x45, 0x03, SIG_UINT16(0x0002),     // callb [export 3 of script 0], 02
7953 	0x18,                               // not
7954 	0x31, 0x18,                         // bnt $18
7955 	SIG_END
7956 };
7957 
7958 static const uint16 phant1RatPatch[] = {
7959 	0x33, 0x20, // jmp [past rat condition + call]
7960 	PATCH_END
7961 };
7962 
7963 // In Phantasmagoria the cursor's hover state will not trigger on any of the
7964 // buttons in the main menu after returning to the main menu from a game, or
7965 // when choosing "Quit" on the main menu and then cancelling the quit in the
7966 // confirmation dialogue, until another button has been hovered and unhovered
7967 // once.
7968 // This happens because the quit confirmation dialogue creates its own
7969 // event handling loop which prevents the main event loop from handling the
7970 // cursor leaving the button (which would reset global[193] to 0), and the
7971 // dialogue does not reset global[193] itself, so it remains at 2 until a new
7972 // button gets hovered and unhovered.
7973 // There is not enough space in the confirmation dialogue code to add a reset
7974 // of global[193], so we just remove the check entirely, since it is only used
7975 // to avoid resetting the cursor's view on every mouse movement, and this
7976 // button type is only used on the main menu and the in-game control panel.
7977 //
7978 // Applies to at least: English CD
7979 // Fixes bug: #9977
7980 static const uint16 phant1RedQuitCursorSignature[] = {
7981 	SIG_MAGICDWORD,
7982 	0x89, 0xc1,                   // lsg global[193]
7983 	0x35, 0x02,                   // ldi 02
7984 	SIG_END
7985 };
7986 
7987 static const uint16 phant1RedQuitCursorPatch[] = {
7988 	0x33, 0x05,                   // jmp [past global[193] check]
7989 	PATCH_END
7990 };
7991 
7992 // In chapter 5, the wine casks in room 20200 shimmer to indicate that they have
7993 //  a video to play but a script bug usually prevents them from being clicked.
7994 //  The code that determines whether to set a hotspot on the "spikot" object
7995 //  is incomplete and out of sync with the code that displays the shimmer and
7996 //  determines which script to run when the casks are clicked. As a result, the
7997 //  shimmering casks can't be clicked in chapter 5 if their contents were tasted
7998 //  in an earlier act. In chapter 6 the shimmering casks can be clicked again.
7999 //
8000 // We fix this by rewriting the logic that sets the hotspot to match the rest of
8001 //  the code in the room. The casks can now always be clicked when they shimmer.
8002 //  To make room we remove a call to approachVerbs: 0 as that has no effect.
8003 //
8004 // Applies to: All versions
8005 // Responsible method: rm20200:init
8006 static const uint16 phant1WineCaskHotspotSignature[] = {
8007 	0x89, 0x6a,                           // lsg 6a
8008 	0x35, 0x06,                           // ldi 06
8009 	0x1a,                                 // eq? [ is chapter 6? ]
8010 	0x31, 0x09,                           // bnt 09
8011 	SIG_MAGICDWORD,
8012 	0x78,                                 // push1
8013 	0x38, SIG_UINT16(0x00d8),             // pushi 00d8 [ flag 216 ]
8014 	0x45, 0x03, SIG_UINT16(0x0002),       // callb proc0_3 [ seen barrel video? ]
8015 	0x18,                                 // not
8016 	0x2f, 0x0f,                           // bt 0f
8017 	0x89, 0x6a,                           // lsg 6a
8018 	0x35, 0x06,                           // ldi 06
8019 	0x1c,                                 // ne? [ is not chapter 6? ]
8020 	0x31, 0x21,                           // bnt 21 [ skip hotspot ]
8021 	0x78,                                 // push1
8022 	0x39, 0x1d,                           // pushi 1d [ flag 29 ]
8023 	0x45, 0x03, SIG_UINT16(0x0002),       // callb proc0_3 [ tasted wine? ]
8024 	0x18,                                 // not
8025 	0x31, 0x17,                           // bnt 17 [ skip hotspot ]
8026 	0x38, SIG_SELECTOR16(init),           // pushi init
8027 	0x76,                                 // push0
8028 	0x38, SIG_SELECTOR16(approachVerbs),  // pushi approachVerbs
8029 	0x78,                                 // push1
8030 	0x76,                                 // push0
8031 	SIG_ADDTOOFFSET(+11),
8032 	0x4a, SIG_UINT16(0x0012),             // send 12 [ spikot init: approachVerbs: 0 ... ]
8033 	SIG_END
8034 };
8035 
8036 static const uint16 phant1WineCaskHotspotPatch[] = {
8037 	0x78,                                 // push1
8038 	0x38, PATCH_UINT16(0x00d8),           // pushi 00d8 [ flag 216 ]
8039 	0x45, 0x03, PATCH_UINT16(0x0002),     // callb proc0_3 [ seen barrel video? ]
8040 	0x2f, 0x30,                           // bt 30 [ skip hotspot ]
8041 	0x39, 0x06,                           // pushi 06
8042 	0x81, 0x6a,                           // lag 6a
8043 	0x04,                                 // sub
8044 	0x31, 0x17,                           // bnt 17 [ show hotspot if chapter 6 ]
8045 	0x78,                                 // push1
8046 	0x1a,                                 // eq?
8047 	0x31, 0x0a,                           // bnt 0a [ skip mirror test if not chapter 5 ]
8048 	0x78,                                 // push1
8049 	0x38, PATCH_UINT16(0x0123),           // pushi 0123 [ flag 291 ]
8050 	0x45, 0x03, PATCH_UINT16(0x0002),     // callb proc0_3 [ seen mirror video? ]
8051 	0x2f, 0x09,                           // bt 09 [ set hotspot ]
8052 	0x78,                                 // push1
8053 	0x39, 0x1d,                           // pushi 1d [ flag 29 ]
8054 	0x45, 0x03, PATCH_UINT16(0x0002),     // callb proc0_3 [ tasted wine? ]
8055 	0x2f, 0x12,                           // bt 12 [ skip hotspot ]
8056 	0x38, PATCH_SELECTOR16(init),         // pushi init
8057 	0x76,                                 // push0
8058 	PATCH_ADDTOOFFSET(+11),
8059 	0x4a, PATCH_UINT16(0x000c),           // send 0c [ spikot init: ... ]
8060 	PATCH_END
8061 };
8062 
8063 // The darkroom in the chapter 7 chase, room 45950, has a bug which deletes the
8064 //  chase history file and leaves the game in a state that can't be completed.
8065 //
8066 // Every action during the chase is written to chase.dat in order to support the
8067 //  review feature which plays back the entire sequence. rm45950:init tests
8068 //  several conditions to see how it should initialize the file. If a previous
8069 //  chase.dat exists and the current game was initially started in chapter 7
8070 //  then an unusual code path is taken which fails to clear flag 134. This flag
8071 //  tells the room that the chase is starting. Upon returning for the book with
8072 //  this flag incorrectly set, rm45950:init deletes chase.dat and all history is
8073 //  lost. Upon playback, items obtained prior to the darkroom will be skipped
8074 //  and become unobtainable, such as the glass shard.
8075 //
8076 // We fix this by clearing flag 134 in the unusual code path so that room 45950
8077 //  never tries to initialize the chase history upon returning.
8078 //
8079 // Applies to: All versions
8080 // Responsible method: rm45950:init
8081 static const uint16 phant1DeleteChaseFileSignature[] = {
8082 	0x32, SIG_UINT16(0x0148),       // jmp 0148 [ end of method ]
8083 	SIG_ADDTOOFFSET(+0x36),
8084 	0x78,                           // push1
8085 	0x38, SIG_MAGICDWORD,           // pushi 0086
8086 	      SIG_UINT16(0x0086),
8087 	0x45, 0x02, SIG_UINT16(0x0002), // callb proc0_2 [ clear flag 134 ]
8088 	0x32, SIG_UINT16(0x0107),       // jmp 0107 [ end of method ]
8089 	SIG_END
8090 };
8091 
8092 static const uint16 phant1DeleteChaseFilePatch[] = {
8093 	0x32, PATCH_UINT16(0x0036),     // jmp 0036 [ clear flag 134 ]
8094 	PATCH_END
8095 };
8096 
8097 // Quitting ScummVM, returning to the launcher, or terminating the game in any
8098 //  way outside of the game's menus during the chapter 7 chase leaves the saved
8099 //  game in a broken state which will crash ScummVM when loaded. The chase was
8100 //  programmed with the expectation that it could never end under unexpected
8101 //  circumstances, which is a bold stance for a Sierra game.
8102 //
8103 // Each saved game consists of the file phantsg.#, and once the chase beings, an
8104 //  additional chase file that records all actions for playback. This is named
8105 //  chase.dat when the chase is running and chasedun.# when it isn't. The file
8106 //  is renamed when transitioning to and from the main menu. The existence of
8107 //  chasedun.# tells the main menu that it should put the game in the chapter 7
8108 //  section. All of this depends on the chase formally ending through scripts
8109 //  that can restore the file name. If the game terminates without this cleanup
8110 //  then the chase file is never renamed and can't be associated with its slot.
8111 //  The save will then be incorrectly demoted to a non-chapter 7 save. Upon
8112 //  attempting to load this broken save, a chase script will attempt to play the
8113 //  missing chase.dat, seek beyond the beginning, and fail an assertion in a
8114 //  stream class. This never picks up the chase.dat from the previous run due to
8115 //  a startup script which deletes stray chase.dat files.
8116 //
8117 // We fix this by copying chasedun.# to chase.dat instead of renaming it. This
8118 //  leaves the original file intact along with phantsg.# in case the game
8119 //  terminates without the scripts getting a chance to cleanup the file system.
8120 //  This patch is currently the only known caller of kFileIOCopy.
8121 //
8122 // Applies to: All versions
8123 // Responsible method: Exported procedure #6 in script 45951
8124 static const uint16 phant1CopyChaseFileSignature[] = {
8125 	0x39, 0x03,                     // pushi 03
8126 	0x39, 0x0b,                     // pushi 0b [ kFileIORename ]
8127 	0x39, SIG_SELECTOR8(data),      // pushi data
8128 	0x76,                           // push0
8129 	0x85, 0x00,                     // lat 00
8130 	0x4a, SIG_UINT16(0x0004),       // send 04 [ temp0 data? ]
8131 	0x36,                           // push    [ "chasedun.#" ]
8132 	0x39, SIG_MAGICDWORD,           // pushi data
8133 	      SIG_SELECTOR8(data),
8134 	0x76,                           // push0
8135 	0x85, 0x02,                     // lat 02
8136 	0x4a, SIG_UINT16(0x0004),       // send 04 [ temp2 data? ]
8137 	0x36,                           // push    [ "chase.dat" ]
8138     0x43, 0x5d, SIG_UINT16(0x0006), // callk FileIO 06
8139 	SIG_END
8140 };
8141 
8142 static const uint16 phant1CopyChaseFilePatch[] = {
8143 	PATCH_ADDTOOFFSET(+2),
8144 	0x39, 0x0c,                     // pushi 0c [ kFileIOCopy ]
8145 	PATCH_END
8146 };
8147 
8148 // During the chase, the west exit in room 46980 has incorrect logic which kills
8149 //  the player if they went to the crypt with the crucifix, among other bugs.
8150 //
8151 // Room 46980 takes place in the chapel and connects the secret passages to the
8152 //  crypt. The player initially enters from the west and the crypt is to the
8153 //  east. The west exit has incorrect logic with several consequences, but the
8154 //  harshest is that if the player came from the crypt without beads then Don is
8155 //  waiting for them. This is wrong because if the player has the crucifix then
8156 //  there are no beads in the game, and also because Don is still in the crypt
8157 //  where the player pushed a statue on him in the previous room.
8158 //
8159 // Sierra eventually fixed this by removing the beads from the equation and
8160 //  swapping the transposed previous room test, but the fix only appears in the
8161 //  Italian version, which was the final CD release. We replace the incorrect
8162 //  logic with Sierra's final version.
8163 //
8164 // Applies to: All versions except Italian
8165 // Responsible method: westExit:doVerb
8166 static const uint16 phant1ChapelWestExitSignature[] = {
8167 	SIG_MAGICDWORD,
8168 	0x38, SIG_SELECTOR16(has),      // pushi has
8169 	0x78,                           // push1
8170 	0x39, 0x0f,                     // pushi 0f
8171 	0x81, 0x00,                     // lag 00
8172 	0x4a, SIG_UINT16(0x0006),       // send 06 [ ego has: 15 ]
8173 	0x2f, 0x06,                     // bt 06
8174 	0x89, 0x0c,                     // lsg 0c
8175 	0x34, SIG_UINT16(0xb680),       // ldi b680
8176 	0x1a,                           // eq? [ previous room == 46720 ]
8177 	SIG_END
8178 };
8179 
8180 static const uint16 phant1ChapelWestExitPatch[] = {
8181 	0x32, PATCH_UINT16(0x000a),     // jmp 000a [ skip inventory check ]
8182 	PATCH_ADDTOOFFSET(+15),
8183 	0x1c,                           // ne? [ previous room != 46720 ]
8184 	PATCH_END
8185 };
8186 
8187 // When reviewing the chapter 7 chase, the game fails to reset the flag that's
8188 //  set when Adrian stabs Don and escapes the nursery. This can only happen once
8189 //  and so the stale flag causes the review feature to play the wrong animation
8190 //  of Don overpowering Adrian instead of Adrian stabbing Don and escaping.
8191 //
8192 // We fix this as Sierra did in the Italian version by clearing flag 18 during
8193 //  the chase initialization.
8194 //
8195 // Applies to: All versions except Italian
8196 // Responsible method: sChaseBegin:changeState(0)
8197 static const uint16 phant1ResetStabDonFlagSignature[] = {
8198 	SIG_MAGICDWORD,
8199 	0x78,                           // push1
8200 	0x38, SIG_UINT16(0x019f),       // pushi 019f
8201 	SIG_ADDTOOFFSET(+4),
8202 	0x7e, SIG_ADDTOOFFSET(+2),      // line
8203 	0x78,                           // push1
8204 	0x39, 0x7b,                     // pushi 7b
8205 	0x45, 0x02, SIG_UINT16(0x0002), // callb proc0_2 [ clear flag 123 ]
8206 	0x7e, SIG_ADDTOOFFSET(+2),      // line
8207 	0x89, 0x0c,                     // lsg 0c
8208 	0x34, SIG_UINT16(0x0384),       // ldi 0384
8209 	0x1a,                           // eq?
8210 	0x18,                           // not
8211 	SIG_END
8212 };
8213 
8214 static const uint16 phant1ResetStabDonFlagPatch[] = {
8215 	PATCH_ADDTOOFFSET(+8),
8216 	0x78,                               // push1
8217 	0x39, 0x7b,                         // pushi 7b
8218 	0x45, 0x02, PATCH_UINT16(0x0002),   // callb proc0_2 [ clear flag 123 ]
8219 	0x78,                               // push1
8220 	0x39, 0x12,                         // pushi 12
8221 	0x45, 0x02, PATCH_UINT16(0x0002),	// callb proc0_2 [ clear flag 18 ]
8222 	0x89, 0x0c,                         // lsg 0c
8223 	0x34, PATCH_UINT16(0x0384),         // ldi 0384
8224 	0x1c,                               // ne?
8225 	PATCH_END
8226 };
8227 
8228 // The censorship for videos 1920 and 2020 are out of sync and the latter has
8229 //  an incorrect coordinate. The frame numbers for the blobs are wrong and so
8230 //  they appear during normal frames but not during the gore. These videos play
8231 //  in room 40100 when attempting to open or unbar the door. We fix the frame
8232 //  numbers to be in sync with the videos and use the correct coordinate from
8233 //  video 1920 which has the same censored frames.
8234 //
8235 // Applies to: All versions
8236 // Responsible method: myList:init
8237 static const uint16 phant1Video1920CensorSignature[] = {
8238 	0x38, SIG_UINT16(0x00dd),       // pushi 221 [ blob 1 start frame ]
8239 	SIG_ADDTOOFFSET(+43),
8240 	0x39, SIG_MAGICDWORD, 0xff,     // pushi -1
8241 	0x38, SIG_UINT16(0x00e1),       // pushi 225 [ blob 1 end frame ]
8242 	SIG_ADDTOOFFSET(+20),
8243 	0x38, SIG_UINT16(0x00fb),       // pushi 251 [ blob 2 start frame ]
8244 	SIG_ADDTOOFFSET(+46),
8245 	0x38, SIG_UINT16(0x0117),       // pushi 279 [ blob 2 end frame ]
8246 	SIG_END
8247 };
8248 
8249 static const uint16 phant1Video1920CensorPatch[] = {
8250 	0x38, PATCH_UINT16(0x00c6),     // pushi 198 [ blob 1 start frame ]
8251 	PATCH_ADDTOOFFSET(+45),
8252 	0x38, PATCH_UINT16(0x00ca),     // pushi 202 [ blob 1 end frame ]
8253 	PATCH_ADDTOOFFSET(+20),
8254 	0x38, PATCH_UINT16(0x00e4),     // pushi 228 [ blob 2 start frame ]
8255 	PATCH_ADDTOOFFSET(+46),
8256 	0x38, PATCH_UINT16(0x00fe),     // pushi 254 [ blob 2 end frame ]
8257 	PATCH_END
8258 };
8259 
8260 static const uint16 phant1Video2020CensorSignature[] = {
8261 	0x38, SIG_UINT16(0x014f),       // pushi 335 [ blob 1 start frame ]
8262 	SIG_ADDTOOFFSET(+18),
8263 	0x38, SIG_UINT16(0x0090),       // pushi 144 [ blob 1 left coordinate ]
8264 	SIG_ADDTOOFFSET(+23),
8265 	0x39, SIG_MAGICDWORD, 0xff,     // pushi -1
8266 	0x38, SIG_UINT16(0x0153),       // pushi 339 [ blob 1 end frame ]
8267 	SIG_ADDTOOFFSET(+20),
8268 	0x38, SIG_UINT16(0x0160),       // pushi 352 [ blob 2 start frame ]
8269 	SIG_ADDTOOFFSET(+46),
8270 	0x38, SIG_UINT16(0x016a),       // pushi 362 [ blob 2 end frame ]
8271 	SIG_END
8272 };
8273 
8274 static const uint16 phant1Video2020CensorPatch[] = {
8275 	0x38, PATCH_UINT16(0x012f),     // pushi 303 [ blob 1 start frame ]
8276 	PATCH_ADDTOOFFSET(+18),
8277 	0x38, PATCH_UINT16(0x0042),     // pushi 66  [ blob 1 left coordinate ]
8278 	PATCH_ADDTOOFFSET(+25),
8279 	0x38, PATCH_UINT16(0x0133),     // pushi 307 [ blob 1 end frame ]
8280 	PATCH_ADDTOOFFSET(+20),
8281 	0x38, PATCH_UINT16(0x014c),     // pushi 332 [ blob 2 start frame ]
8282 	PATCH_ADDTOOFFSET(+46),
8283 	0x38, PATCH_UINT16(0x0168),     // pushi 360 [ blob 2 end frame ]
8284 	PATCH_END
8285 };
8286 
8287 //          script, description,                                      signature                        patch
8288 static const SciScriptPatcherEntry phantasmagoriaSignatures[] = {
8289 	{  true,    23, "make cursor red after clicking quit",         1, phant1RedQuitCursorSignature,    phant1RedQuitCursorPatch },
8290 	{  true,    26, "fix video 1920 censorship",                   1, phant1Video1920CensorSignature,  phant1Video1920CensorPatch },
8291 	{  true,    26, "fix video 2020 censorship",                   1, phant1Video2020CensorSignature,  phant1Video2020CensorPatch },
8292 	{  true,   901, "fix invalid array construction",              1, sci21IntArraySignature,          sci21IntArrayPatch },
8293 	{  true,  1111, "ignore audio settings from save game",        1, phant1SavedVolumeSignature,      phant1SavedVolumePatch },
8294 	{  true, 20200, "fix broken rat init in sEnterFromAlcove",     1, phant1RatSignature,              phant1RatPatch },
8295 	{  true, 20200, "fix chapter 5 wine cask hotspot",             1, phant1WineCaskHotspotSignature,  phant1WineCaskHotspotPatch },
8296 	{  true, 45950, "fix chase file deletion",                     1, phant1DeleteChaseFileSignature,  phant1DeleteChaseFilePatch },
8297 	{  true, 45950, "reset stab don flag",                         1, phant1ResetStabDonFlagSignature, phant1ResetStabDonFlagPatch },
8298 	{  true, 45951, "copy chase file instead of rename",           1, phant1CopyChaseFileSignature,    phant1CopyChaseFilePatch },
8299 	{  true, 46980, "fix chapel chase west exit",                  1, phant1ChapelWestExitSignature,   phant1ChapelWestExitPatch },
8300 	{  true, 64908, "disable video benchmarking",                  1, sci2BenchmarkSignature,          sci2BenchmarkPatch },
8301 	SCI_SIGNATUREENTRY_TERMINATOR
8302 };
8303 
8304 #pragma mark -
8305 #pragma mark Phantasmagoria 2
8306 
8307 // The game uses a spin loop when navigating to and from Curtis's computer in
8308 // the office, and when entering passwords, which causes the mouse to appear
8309 // unresponsive. Replace the spin loop with a call to ScummVM kWait.
8310 // Applies to at least: US English
8311 // Responsible method: Script 3000 localproc 2ee4, script 63019 localproc 4f04
8312 static const uint16 phant2WaitParam1Signature[] = {
8313 	SIG_MAGICDWORD,
8314 	0x35, 0x00, // ldi 0
8315 	0xa5, 0x00, // sat temp[0]
8316 	0x8d, 0x00, // lst temp[0]
8317 	0x87, 0x01, // lap param[1]
8318 	SIG_END
8319 };
8320 
8321 static const uint16 phant2WaitParam1Patch[] = {
8322 	0x78,                                     // push1
8323 	0x8f, 0x01,                               // lsp param[1]
8324 	0x43, kScummVMWaitId, PATCH_UINT16(0x02), // callk Wait, 2
8325 	0x48,                                     // ret
8326 	PATCH_END
8327 };
8328 
8329 // The interface bars at the top and bottom of the screen fade in and out when
8330 // hovered over. This fade is performed by a script loop that calls kFrameOut
8331 // directly and uses global[227] as the fade delta for each frame. It normally
8332 // is set to 1, which means that these fades are quite slow. We replace the use
8333 // of global[227] with an immediate value for a reasonable fade speed.
8334 // Applies to at least: US English
8335 static const uint16 phant2SlowIFadeSignature[] = {
8336 	0x43, 0x21, SIG_UINT16(0x0000),     // callk FrameOut, 0
8337 	SIG_MAGICDWORD,
8338 	0x67, 0x03,                         // pTos 03 (scratch)
8339 	0x81, 0xe3,                         // lag global[227]
8340 	SIG_END
8341 };
8342 
8343 static const uint16 phant2SlowIFadePatch[] = {
8344 	PATCH_ADDTOOFFSET(+6),              // skip to lag
8345 	0x35, 0x05,                         // ldi 5
8346 	PATCH_END
8347 };
8348 
8349 // The game uses a spin loop during music transitions which causes the mouse to
8350 // appear unresponsive during scene changes. Replace the spin loop with a call
8351 // to ScummVM kWait.
8352 // Applies to at least: US English
8353 // Responsible method: P2SongPlyr::wait4Fade
8354 static const uint16 phant2Wait4FadeSignature[] = {
8355 	SIG_MAGICDWORD,
8356 	0x76,                               // push0
8357 	0x43, 0x79, SIG_UINT16(0x0000),     // callk GetTime, 0
8358 	0xa5, 0x01,                         // sat temp[1]
8359 	0x78,                               // push1
8360 	SIG_END
8361 };
8362 
8363 static const uint16 phant2Wait4FadePatch[] = {
8364 	0x78,                                     // push1
8365 	0x8d, 0x00,                               // lst temp[0]
8366 	0x43, kScummVMWaitId, PATCH_UINT16(0x02), // callk Wait, 2
8367 	0x48,                                     // ret
8368 	PATCH_END
8369 };
8370 
8371 // When reading the VERSION file, Phant2 sends a Str object instead of a
8372 // reference to a string (kernel signature violation), and flips the file handle
8373 // and size arguments, so the version file data never actually makes it into the
8374 // game.
8375 // Applies to at least: Phant2 US English CD
8376 static const uint16 phant2GetVersionSignature[] = {
8377 	0x36,                         // push
8378 	0x35, 0xff,                   // ldi $ff
8379 	0x1c,                         // ne?
8380 	0x31, 0x0e,                   // bnt $e
8381 	0x39, 0x04,                   // pushi 4
8382 	0x39, 0x05,                   // pushi 5
8383 	SIG_MAGICDWORD,
8384 	0x89, 0x1b,                   // lsg global[$1b]
8385 	0x8d, 0x05,                   // lst temp[5]
8386 	0x39, 0x09,                   // pushi 9
8387 	0x43, 0x5d, SIG_UINT16(0x08), // callk FileIO, 8
8388 	0x7a,                         // push2
8389 	0x78,                         // push1
8390 	0x8d, 0x05,                   // lst temp[5]
8391 	0x43, 0x5d, SIG_UINT16(0x04), // callk FileIO, 4
8392 	0x35, 0x01,                   // ldi 1
8393 	0xa1, 0xd8,                   // sag global[$d8]
8394 	SIG_END
8395 };
8396 
8397 static const uint16 phant2GetVersionPatch[] = {
8398 	0x39, 0x04,                     // pushi 4
8399 	0x39, 0x05,                     // pushi 5
8400 	0x81, 0x1b,                     // lag global[$1b]
8401 	0x39, PATCH_SELECTOR8(data),    // pushi data
8402 	0x76,                           // push0
8403 	0x4a, PATCH_UINT16(0x0004),     // send 4
8404 	0x36,                           // push
8405 	0x39, 0x09,                     // pushi 9
8406 	0x8d, 0x05,                     // lst temp[5]
8407 	0x43, 0x5d, PATCH_UINT16(0x08), // callk FileIO, 8
8408 	0x7a,                           // push2
8409 	0x78,                           // push1
8410 	0x8d, 0x05,                     // lst temp[5]
8411 	0x43, 0x5d, PATCH_UINT16(0x04), // callk FileIO, 4
8412 	0x78,                           // push1
8413 	0xa9, 0xd8,                     // ssg global[$d8]
8414 	PATCH_END
8415 };
8416 
8417 // The game uses a spin loop when displaying the success animation of the ratboy
8418 // puzzle, which causes the mouse to appear unresponsive. Replace the spin loop
8419 // with a call to ScummVM kWait.
8420 // Applies to at least: US English
8421 static const uint16 phant2RatboySignature[] = {
8422 	0x8d, 0x01,                   // lst temp[1]
8423 	0x35, 0x1e,                   // ldi $1e
8424 	0x22,                         // lt?
8425 	SIG_MAGICDWORD,
8426 	0x31, 0x17,                   // bnt $17 [0c3d]
8427 	0x76,                         // push0
8428 	0x43, 0x79, SIG_UINT16(0x00), // callk GetTime, 0
8429 	SIG_END
8430 };
8431 
8432 static const uint16 phant2RatboyPatch[] = {
8433 	0x78,                                     // push1
8434 	0x35, 0x1e,                               // ldi $1e
8435 	0x36,                                     // push
8436 	0x43, kScummVMWaitId, PATCH_UINT16(0x02), // callk Wait, $2
8437 	0x33, 0x14,                               // jmp [to next outer loop]
8438 	PATCH_END
8439 };
8440 
8441 // Phant2 has separate in-game volume controls for handling movie volume and
8442 // in-game volume (misleadingly labelled "music volume"), but really needs the
8443 // in-game volume to always be significantly lower than the movie volume in
8444 // order for dialogue in movies to be consistently audible, so patch the
8445 // in-game volume slider to limit it to our maximum.
8446 // Applies to at least: US English
8447 // Fixes bug: #10165
8448 static const uint16 phant2AudioVolumeSignature[] = {
8449 	SIG_MAGICDWORD,
8450 	0x39, 0x7f,           // pushi 127 (clientMax value)
8451 	0x39, 0x14,           // pushi 20  (clientPageSize value)
8452 	SIG_ADDTOOFFSET(+10), // skip other init arguments
8453 	0x51, 0x5e,           // class P2ScrollBar
8454 	SIG_ADDTOOFFSET(+3),  // skip send
8455 	0xa3, 0x06,           // sal local[6] (identifies correct slider)
8456 	SIG_END
8457 };
8458 
8459 static const uint16 phant2AudioVolumePatch[] = {
8460 	0x39, kPhant2VolumeMax,              // pushi (our custom volume max)
8461 	0x39, 0x14 * kPhant2VolumeMax / 127, // pushi (ratio of original value)
8462 	PATCH_END
8463 };
8464 
8465 // When censorship is disabled the game sticks <PROTECTED> at the end of every
8466 // save game name, and when it is enabled it pads the save game name with a
8467 // bunch of spaces. This is annoying and not helpful, so just disable all of
8468 // this nonsense.
8469 // Applies to at least: US English
8470 // Fixes bug: #10035
8471 static const uint16 phant2SaveNameSignature1[] = {
8472 	SIG_MAGICDWORD,
8473 	0x57, 0x4b, SIG_UINT16(0x06), // super SREdit, 6
8474 	0x63,                         // pToa (plane)
8475 	SIG_END
8476 };
8477 
8478 static const uint16 phant2SaveNamePatch1[] = {
8479 	PATCH_ADDTOOFFSET(+4), // super SREdit, 6
8480 	0x48,                  // ret
8481 	PATCH_END
8482 };
8483 
8484 static const uint16 phant2SaveNameSignature2[] = {
8485 	SIG_MAGICDWORD,
8486 	0xa5, 0x00,                  // sat temp[0]
8487 	0x39, SIG_SELECTOR8(format), // pushi format
8488 	SIG_END
8489 };
8490 
8491 static const uint16 phant2SaveNamePatch2[] = {
8492 	PATCH_ADDTOOFFSET(+2), // sat temp[0]
8493 	0x33, 0x68,            // jmp [past name mangling]
8494 	PATCH_END
8495 };
8496 
8497 // Phant2-specific version of sci2NumSavesSignature1/2
8498 // Applies to at least: English CD
8499 static const uint16 phant2NumSavesSignature1[] = {
8500 	SIG_MAGICDWORD,
8501 	0x8d, 0x01, // lst temp[1]
8502 	0x35, 0x14, // ldi 20
8503 	0x1a,       // eq?
8504 	SIG_END
8505 };
8506 
8507 static const uint16 phant2NumSavesPatch1[] = {
8508 	PATCH_ADDTOOFFSET(+2), // lst temp[1]
8509 	0x35, 0x63,            // ldi 99
8510 	PATCH_END
8511 };
8512 
8513 static const uint16 phant2NumSavesSignature2[] = {
8514 	SIG_MAGICDWORD,
8515 	0x8d, 0x00, // lst temp[0]
8516 	0x35, 0x14, // ldi 20
8517 	0x22,       // lt?
8518 	SIG_END
8519 };
8520 
8521 static const uint16 phant2NumSavesPatch2[] = {
8522 	PATCH_ADDTOOFFSET(+2), // lst temp[0]
8523 	0x35, 0x63,            // ldi 99
8524 	PATCH_END
8525 };
8526 
8527 // The game script responsible for handling document scrolling in the computer
8528 // interface uses a spin loop to wait for 10 ticks every time the document
8529 // scrolls. This makes scrolling janky and makes the mouse appear
8530 // non-responsive. Eliminating the delay entirely makes scrolling with the arrow
8531 // buttons a little too quick; a delay of 3 ticks is an OK middle-ground between
8532 // allowing mostly fluid motion with mouse dragging and reasonably paced
8533 // scrolling holding down the arrows. Preferably, ScrollbarArrow::handleEvent or
8534 // ScrollbarArrow::action would only send cues once every N ticks whilst being
8535 // held down, but unfortunately the game was not programmed to do this.
8536 // Applies to at least: US English
8537 static const uint16 phant2SlowScrollSignature[] = {
8538 	SIG_MAGICDWORD,
8539 	0x35, 0x0a,                         // ldi 10
8540 	0x22,                               // lt?
8541 	0x31, 0x17,                         // bnt [end of loop]
8542 	0x76,                               // push0
8543 	0x43, 0x79, SIG_UINT16(0x0000),     // callk GetTime, 0
8544 	SIG_END
8545 };
8546 
8547 static const uint16 phant2SlowScrollPatch[] = {
8548 	0x78,                                     // push1
8549 	0x39, 0x03,                               // pushi 3
8550 	0x43, kScummVMWaitId, PATCH_UINT16(0x02), // callk Wait, 2
8551 	0x33, 0x13,                               // jmp [end of loop]
8552 	PATCH_END
8553 };
8554 
8555 // WynNetDoco::open calls setSize before it calls posn, but the values set by
8556 // posn are used by setSize, so the left/top coordinates of the text and note
8557 // fields is wrong for the first render of a document or email. (Incidentally,
8558 // these fields are the now-seen rect fields, and the game is doing a very bad
8559 // thing by touching these manually and then relying on the values instead of
8560 // asking the kernel.) This is most noticeable during chapters 1 and 3 when the
8561 // computer is displaying scary messages, since every time the scary message is
8562 // rendered the text fields re-render at the top-left corner of the screen.
8563 // Applies to at least: US English
8564 // Fixes bug: #10036
8565 static const uint16 phant2BadPositionSignature[] = {
8566 	SIG_MAGICDWORD,
8567 	0x39, SIG_SELECTOR8(setSize), // pushi setSize
8568 	0x76,                         // push0
8569 	0x39, SIG_SELECTOR8(init),    // pushi init
8570 	0x78,                         // pushi 1
8571 	0x89, 0x03,                   // lsg global[3]
8572 	0x39, SIG_SELECTOR8(posn),    // pushi posn
8573 	0x7a,                         // push2
8574 	0x66, SIG_ADDTOOFFSET(+2),    // pTos (x position)
8575 	0x66, SIG_ADDTOOFFSET(+2),    // pTos (y position)
8576 	SIG_END
8577 };
8578 
8579 static const uint16 phant2BadPositionPatch[] = {
8580 	0x39, PATCH_SELECTOR8(posn),        // pushi posn
8581 	0x7a,                               // push2
8582 	0x66, PATCH_GETORIGINALUINT16(12),  // pTos (x position)
8583 	0x66, PATCH_GETORIGINALUINT16(15),  // pTos (y position)
8584 	0x39, PATCH_SELECTOR8(setSize),     // pushi setSize
8585 	0x76,                               // push0
8586 	0x39, PATCH_SELECTOR8(init),        // pushi init
8587 	0x78,                               // pushi 1
8588 	0x89, 0x03,                         // lsg global[3]
8589 	PATCH_END
8590 };
8591 
8592 // WynDocuStore::refresh resets the cel of the open folder and document icons,
8593 // so they don't end up being rendered as closed folder/document icons, but it
8594 // forgets to actually update the icon's View with the kernel, so they render
8595 // as closed for the first render after a refresh anyway. This is most
8596 // noticeable during chapters 1 and 3 when the computer is displaying scary
8597 // messages, since every time the scary message is rendered the icons re-render
8598 // as closed.
8599 // Applies to at least: US English
8600 static const uint16 phant2BadIconSignature[] = {
8601 	SIG_MAGICDWORD,
8602 	0x38, SIG_SELECTOR16(setCel), // pushi setCel
8603 	0x78,                         // push1
8604 	0x78,                         // push1
8605 	0x38, SIG_SELECTOR16(iconV),  // pushi iconV
8606 	0x76,                         // push0
8607 	0x62, SIG_ADDTOOFFSET(+2),    // pToa curFolder/curDoco
8608 	0x4a, SIG_UINT16(0x0004),     // send 4
8609 	0x4a, SIG_UINT16(0x0006),     // send 6
8610 	SIG_END
8611 };
8612 
8613 static const uint16 phant2BadIconPatch[] = {
8614 	PATCH_ADDTOOFFSET(+5),          // pushi setCel, push1, push1
8615 	0x38, PATCH_SELECTOR16(update), // pushi update
8616 	0x76,                           // push0
8617 	0x4a, PATCH_UINT16(0x000a),     // send 10
8618 	0x33, 0x04,                     // jmp [past unused bytes]
8619 	PATCH_END
8620 };
8621 
8622 // The left and right arrows move inventory items a pixel more than each
8623 // inventory item is wide, which causes the inventory to creep to the left by
8624 // one pixel per scrolled item.
8625 // Applies to at least: US English
8626 // Fixes bug: #10037
8627 static const uint16 phant2InvLeftDeltaSignature[] = {
8628 	SIG_MAGICDWORD,
8629 	SIG_UINT16(0x0042),                 // delta
8630 	SIG_UINT16(0x0019),                 // moveDelay
8631 	SIG_END
8632 };
8633 
8634 static const uint16 phant2InvLeftDeltaPatch[] = {
8635 	PATCH_UINT16(0x0041),               // delta
8636 	PATCH_END
8637 };
8638 
8639 static const uint16 phant2InvRightDeltaSignature[] = {
8640 	SIG_MAGICDWORD,
8641 	SIG_UINT16(0xffbe),                 // delta
8642 	SIG_UINT16(0x0019),                 // moveDelay
8643 	SIG_END
8644 };
8645 
8646 static const uint16 phant2InvRightDeltaPatch[] = {
8647 	PATCH_UINT16(0xffbf),               // delta
8648 	PATCH_END
8649 };
8650 
8651 // The first inventory item is put too far to the right, which causes wide items
8652 // to get cut off on the right side of the inventory.
8653 // Applies to at least: US English
8654 static const uint16 phant2InvOffsetSignature[] = {
8655 	SIG_MAGICDWORD,
8656 	0x35, 0x26,                         // ldi 38
8657 	0x64, SIG_SELECTOR16(xOff),         // aTop xOff
8658 	SIG_END
8659 };
8660 
8661 static const uint16 phant2InvOffsetPatch[] = {
8662 	0x35, 0x1d,                         // ldi 29
8663 	PATCH_END
8664 };
8665 
8666 // The text placement of "File" and "Note" content inside DocuStore File
8667 // Retrieval System makes some letters especially "g" overlap the
8668 // corresponding box. Set by 'WynNetDoco::open'.
8669 // We fix this by changing the position of those 2 inside the heap of
8670 // subclass 'WynNetDoco' slightly.
8671 // Applies to at least: English CD, Japanese CD, German CD
8672 // Fixes bug: #10034
8673 static const uint16 phant2DocuStoreFileNotePlacementSignature[] = {
8674 	SIG_MAGICDWORD,
8675 	SIG_UINT16(0x0046),   // nameX
8676 	SIG_UINT16(0x000a),   // nameY
8677 	SIG_ADDTOOFFSET(+10), // skip over nameMsg*
8678 	SIG_UINT16(0x0046),   // noteX
8679 	SIG_UINT16(0x001e),   // noteY
8680 	SIG_END
8681 };
8682 
8683 static const uint16 phant2DocuStoreFileNotePlacementPatch[] = {
8684 	PATCH_ADDTOOFFSET(+2),
8685 	PATCH_UINT16(0x0006),   // new nameY
8686 	PATCH_ADDTOOFFSET(+12),
8687 	PATCH_UINT16(0x001b),   // new noteY
8688 	PATCH_END
8689 };
8690 
8691 // The text placement of "From" and "Subject" content inside DocuStore.
8692 // We fix this by changing the position inside the heap of subclass
8693 // 'WynNetEmail' slightly.
8694 // For this one, we also fix the horizontal placement.
8695 static const uint16 phant2DocuStoreEmailPlacementSignature[] = {
8696 	SIG_MAGICDWORD,
8697 	SIG_UINT16(0x0049),   // nameX
8698 	SIG_UINT16(0x0008),   // nameY
8699 	SIG_ADDTOOFFSET(+10), // skip over nameMsg*
8700 	SIG_UINT16(0x0049),   // noteX
8701 	SIG_UINT16(0x001c),   // noteY
8702 	SIG_END
8703 };
8704 
8705 static const uint16 phant2DocuStoreEmailPlacementPatch[] = {
8706 	PATCH_UINT16(0x0050),   // new nameX
8707 	PATCH_UINT16(0x0006),   // new nameY
8708 	PATCH_ADDTOOFFSET(+10),
8709 	PATCH_UINT16(0x0050),   // new noteX
8710 	PATCH_UINT16(0x001b),   // new noteY
8711 	PATCH_END
8712 };
8713 
8714 //          script, description,                                      signature                                  patch
8715 static const SciScriptPatcherEntry phantasmagoria2Signatures[] = {
8716 	{  true,     0, "speed up interface fades",                    3, phant2SlowIFadeSignature,                  phant2SlowIFadePatch },
8717 	{  true,     0, "fix bad arguments to get game version",       1, phant2GetVersionSignature,                 phant2GetVersionPatch },
8718 	{  true,  3000, "replace spin loop in alien password window",  1, phant2WaitParam1Signature,                 phant2WaitParam1Patch },
8719 	{  true,  4081, "replace spin loop after ratboy puzzle",       1, phant2RatboySignature,                     phant2RatboyPatch },
8720 	{  true, 63001, "fix inventory left scroll delta",             1, phant2InvLeftDeltaSignature,               phant2InvLeftDeltaPatch },
8721 	{  true, 63001, "fix inventory right scroll delta",            1, phant2InvRightDeltaSignature,              phant2InvRightDeltaPatch },
8722 	{  true, 63001, "fix inventory wrong initial offset",          1, phant2InvOffsetSignature,                  phant2InvOffsetPatch },
8723 	{  true, 63004, "limit in-game audio volume",                  1, phant2AudioVolumeSignature,                phant2AudioVolumePatch },
8724 	{  true, 63016, "replace spin loop during music fades",        1, phant2Wait4FadeSignature,                  phant2Wait4FadePatch },
8725 	{  true, 63019, "replace spin loop during computer load",      1, phant2WaitParam1Signature,                 phant2WaitParam1Patch },
8726 	{  true, 63019, "replace spin loop during computer scrolling", 1, phant2SlowScrollSignature,                 phant2SlowScrollPatch },
8727 	{  true, 63019, "fix bad doc/email name & memo positioning",   2, phant2BadPositionSignature,                phant2BadPositionPatch },
8728 	{  true, 63019, "fix bad folder/doc icon refresh",             2, phant2BadIconSignature,                    phant2BadIconPatch },
8729 	{  true, 63019, "fix file and note content placement",         1, phant2DocuStoreFileNotePlacementSignature, phant2DocuStoreFileNotePlacementPatch },
8730 	{  true, 63019, "fix email content placement",                 1, phant2DocuStoreEmailPlacementSignature,    phant2DocuStoreEmailPlacementPatch },
8731 	{  true, 64990, "remove save game name mangling (1/2)",        1, phant2SaveNameSignature1,                  phant2SaveNamePatch1 },
8732 	{  true, 64990, "increase number of save games (1/2)",         1, phant2NumSavesSignature1,                  phant2NumSavesPatch1 },
8733 	{  true, 64990, "increase number of save games (2/2)",         2, phant2NumSavesSignature2,                  phant2NumSavesPatch2 },
8734 	{  true, 64994, "remove save game name mangling (2/2)",        1, phant2SaveNameSignature2,                  phant2SaveNamePatch2 },
8735 	SCI_SIGNATUREENTRY_TERMINATOR
8736 };
8737 
8738 #endif
8739 
8740 // ===========================================================================
8741 // Police Quest 1 VGA
8742 
8743 // When briefing is about to start in room 15, other officers will get into the room too.
8744 // When one of those officers gets into the way of ego, they will tell the player to sit down.
8745 // But control will be disabled right at that point. Ego may then go to his seat by himself,
8746 // or more often than not will just stand there. The player is unable to do anything.
8747 //
8748 // Sergeant Dooley will then enter the room. Tell the player to sit down 3 times and after
8749 // that it's game over.
8750 //
8751 // Because the Sergeant is telling the player to sit down, one has to assume that the player
8752 // is meant to still be in control. Which is why this script patch removes disabling of player control.
8753 //
8754 // The script also tries to make ego walk to the chair, but it fails because it gets stuck with other
8755 // actors. So I guess the safest way is to remove all of that and let the player do it manually.
8756 //
8757 // The responsible method seems to use a few hardcoded texts, which is why I have to assume that it's
8758 // not used anywhere else. I also checked all scripts and couldn't find any other calls to it.
8759 //
8760 // This of course also happens when using the original interpreter.
8761 //
8762 // Scripts work like this: manX::doit (script 134) triggers gab::changeState, which then triggers rm015::notify
8763 //
8764 // Applies to at least: English floppy
8765 // Responsible method: gab::changeState (script 152)
8766 // Fixes bug: #5865
8767 static const uint16 pq1vgaSignatureBriefingGettingStuck[] = {
8768 	0x76,                                // push0
8769 	0x45, 0x02, 0x00,                    // callb [export 2 of script 0], 00 (disable control)
8770 	0x38, SIG_ADDTOOFFSET(+2),           // pushi notify
8771 	0x76,                                // push0
8772 	0x81, 0x02,                          // lag global[2] (get current room)
8773 	0x4a, 0x04,                          // send 04
8774 	SIG_MAGICDWORD,
8775 	0x8b, 0x02,                          // lsl local[2]
8776 	0x35, 0x01,                          // ldi 01
8777 	0x02,                                // add
8778 	SIG_END
8779 };
8780 
8781 static const uint16 pq1vgaPatchBriefingGettingStuck[] = {
8782 	0x33, 0x0a,                      // jmp [to lsl local[2], skip over export 2 and ::notify]
8783 	PATCH_END                        // rm015::notify would try to make ego walk to the chair
8784 };
8785 
8786 // When at the police station, you can put or get your gun from your locker.
8787 // The script, that handles this, is buggy. It disposes the gun as soon as
8788 //  you click, but then waits 2 seconds before it also closes the locker.
8789 // Problem is that it's possible to click again, which then results in a
8790 //  disposed object getting accessed. This happened to work by pure luck in
8791 //  SSCI.
8792 // This patch changes the code, so that the gun is actually given away
8793 //  when the 2 seconds have passed and the locker got closed.
8794 // Applies to at least: English floppy
8795 // Responsible method: putGun::changeState (script 341)
8796 // Fixes bug: #5705, #6400
8797 static const uint16 pq1vgaSignaturePutGunInLockerBug[] = {
8798 	0x35, 0x00,                      // ldi 00
8799 	0x1a,                            // eq?
8800 	0x31, 0x25,                      // bnt [next state check]
8801 	SIG_ADDTOOFFSET(+22),            // [skip 22 bytes]
8802 	SIG_MAGICDWORD,
8803 	0x38, SIG_SELECTOR16(put),       // pushi put
8804 	0x78,                            // push1
8805 	0x76,                            // push0
8806 	0x81, 0x00,                      // lag global[0]
8807 	0x4a, 0x06,                      // send 06 - ego::put(0)
8808 	0x35, 0x02,                      // ldi 02
8809 	0x65, 0x1c,                      // aTop 1c (set timer to 2 seconds)
8810 	0x33, 0x0e,                      // jmp [end of method]
8811 	0x3c,                            // dup (state check)
8812 	0x35, 0x01,                      // ldi 01
8813 	0x1a,                            // eq?
8814 	0x31, 0x08,                      // bnt [end of method]
8815 	0x39, SIG_SELECTOR8(dispose),    // pushi dispose
8816 	0x76,                            // push0
8817 	0x72, SIG_UINT16(0x0088),        // lofsa 0088
8818 	0x4a, 0x04,                      // send 04 - locker::dispose
8819 	SIG_END
8820 };
8821 
8822 static const uint16 pq1vgaPatchPutGunInLockerBug[] = {
8823 	PATCH_ADDTOOFFSET(+3),
8824 	0x31, 0x1c,                      // bnt [next state check]
8825 	PATCH_ADDTOOFFSET(+22),
8826 	0x35, 0x02,                      // ldi 02
8827 	0x65, 0x1c,                      // aTop 1c (set timer to 2 seconds)
8828 	0x33, 0x17,                      // jmp [end of method]
8829 	0x3c,                            // dup (state check)
8830 	0x35, 0x01,                      // ldi 01
8831 	0x1a,                            // eq?
8832 	0x31, 0x11,                      // bnt [end of method]
8833 	0x38, PATCH_SELECTOR16(put),     // pushi put
8834 	0x78,                            // push1
8835 	0x76,                            // push0
8836 	0x81, 0x00,                      // lag global[0]
8837 	0x4a, 0x06,                      // send 06 - ego::put(0)
8838 	PATCH_END
8839 };
8840 
8841 // When restoring a saved game, which was made while driving around,
8842 //  the game didn't redraw the map. This also happened in Sierra SCI.
8843 //
8844 // The map is a picture resource and drawn over the main picture.
8845 //  This is called an "overlay" in SCI. This wasn't implemented properly.
8846 //  We fix it by actually implementing it properly.
8847 //
8848 // Applies to at least: English floppy
8849 // Responsible method: rm500::init, changeOverlay::changeState (script 500)
8850 // Fixes bug: #5016
8851 static const uint16 pq1vgaSignatureMapSaveRestoreBug[] = {
8852 	0x39, 0x04,                          // pushi 04
8853 	SIG_ADDTOOFFSET(+2),                 // skip either lsg global[f9] or pTos register
8854 	SIG_MAGICDWORD,
8855 	0x38, 0x64, 0x80,                    // pushi 8064
8856 	0x76,                                // push0
8857 	0x89, 0x28,                          // lsg global[28]
8858 	0x43, 0x08, 0x08,                    // callk DrawPic, 8
8859 	SIG_END
8860 };
8861 
8862 static const uint16 pq1vgaPatchMapSaveRestoreBug[] = {
8863 	0x38, PATCH_SELECTOR16(overlay), // pushi overlay
8864 	0x7a,                            // push2
8865 	0x89, 0xf9,                      // lsg global[f9]
8866 	0x39, 0x64,                      // pushi 64 (no transition)
8867 	0x81, 0x02,                      // lag global[2] (current room object)
8868 	0x4a, 0x08,                      // send 08
8869 	0x18,                            // not (waste byte)
8870 	PATCH_END
8871 };
8872 
8873 // In the first release of PQ1VGA, looking at objects while sitting in the car
8874 //  outside of Carol's breaks the game. The objects set Look as an approachVerb,
8875 //  causing ego to float towards them without leaving the car and initializing.
8876 //
8877 // We fix this as Sierra did by removing Look from all approachVerbs in room 30.
8878 //
8879 // Applies to: English Floppy without 30.HEP and 30.SCR
8880 // Responsible methods: door:init, harleys:init, willySign:init, carolSign:init,
8881 //                      carolWindow:init, weeds:init, alley:init, mat:init
8882 // Fixes bug: #5826
8883 static const uint16 pq1vgaSignatureFloatOutsideCarols1[] = {
8884 	0x38, SIG_SELECTOR16(approachVerbs), // pushi approachVerbs
8885 	SIG_MAGICDWORD,
8886 	0x78,                                // push1
8887 	0x78,                                // push1
8888 	0x54, 0x06,                          // self 06 [ self approachVerbs: 1 ]
8889 	SIG_END
8890 };
8891 
8892 static const uint16 pq1vgaPatchFloatOutsideCarols1[] = {
8893 	0x32, PATCH_UINT16(0x0004),          // jmp 0004 [ don't set approachVerbs ]
8894 	PATCH_END
8895 };
8896 
8897 static const uint16 pq1vgaSignatureFloatOutsideCarols2[] = {
8898 	0x38, SIG_SELECTOR16(approachVerbs), // pushi approachVerbs
8899 	SIG_MAGICDWORD,
8900 	0x7a,                                // push2
8901 	0x78,                                // push1
8902 	0x39, 0x04,                          // pushi 04
8903 	0x54, 0x08,                          // self 08 [ self approachVerbs: 1 4 ]
8904 	SIG_END
8905 };
8906 
8907 static const uint16 pq1vgaPatchFloatOutsideCarols2[] = {
8908 	PATCH_ADDTOOFFSET(+3),
8909 	0x39, 0x01,                          // pushi 01
8910 	PATCH_ADDTOOFFSET(+2),
8911 	0x54, 0x06,                          // self 06 [ self approachVerbs: 4 ]
8912 	PATCH_END
8913 };
8914 
8915 //          script, description,                                         signature                            patch
8916 static const SciScriptPatcherEntry pq1vgaSignatures[] = {
8917 	{  true,    30, "float outside carol's (1/2)",                    7, pq1vgaSignatureFloatOutsideCarols1,  pq1vgaPatchFloatOutsideCarols1 },
8918 	{  true,    30, "float outside carol's (2/2)",                    1, pq1vgaSignatureFloatOutsideCarols2,  pq1vgaPatchFloatOutsideCarols2 },
8919 	{  true,   152, "getting stuck while briefing is about to start", 1, pq1vgaSignatureBriefingGettingStuck, pq1vgaPatchBriefingGettingStuck },
8920 	{  true,   341, "put gun in locker bug",                          1, pq1vgaSignaturePutGunInLockerBug,    pq1vgaPatchPutGunInLockerBug },
8921 	{  true,   500, "map save/restore bug",                           2, pq1vgaSignatureMapSaveRestoreBug,    pq1vgaPatchMapSaveRestoreBug },
8922 	SCI_SIGNATUREENTRY_TERMINATOR
8923 };
8924 
8925 // ===========================================================================
8926 // Police Quest 3
8927 
8928 // The player can give the locket to Marie on day 6, which was supposed to grant
8929 // 10 points. Sadly no version did so, so it was not possible to complete the game
8930 // with a perfect score (460 points).
8931 
8932 // Those 10 points are mentioned in the official Sierra hint book for day 6,
8933 // which is why we consider this to be accurate.
8934 
8935 // This bug occurs of course also, when using the original interpreter.
8936 
8937 // We fix this issue by granting those 10 points.
8938 
8939 // Applies to at least: English PC floppy, English Amiga, German PC floppy
8940 // Responsible method: giveLocket::changeState(1), script 36
8941 // Fixes bug: #9862
8942 static const uint16 pq3SignatureGiveLocketPoints[] = {
8943 	// selectors hardcoded in here, it seems all game versions use the same selector ids
8944 	0x39, 0x20,                          // pushi 20h (state)
8945 	0x78,                                // push1
8946 	0x78,                                // push1
8947 	0x39, 0x43,                          // pushi 43h (at)
8948 	0x78,                                // push1
8949 	SIG_MAGICDWORD,
8950 	0x39, 0x25,                          // pushi 25h
8951 	0x81, 0x09,                          // lag global[9]
8952 	0x4a, 0x06,                          // send 06 - Inv::at(25h)
8953 	0x4a, 0x06,                          // send 06 - locket::state(1)
8954 	0x38, SIG_UINT16(0x009b),            // pushi 009bh (owner)
8955 	0x76,                                // push0
8956 	0x39, 0x43,                          // pushi 43h (at)
8957 	0x78,                                // push1
8958 	0x39, 0x25,                          // pushi 25h
8959 	0x81, 0x09,                          // lag global[9]
8960 	0x4a, 0x06,                          // send 06 - Inv:at(25h)
8961 	0x4a, 0x04,                          // send 04 - locket::owner
8962 	SIG_END
8963 };
8964 
8965 static const uint16 pq3PatchGiveLocketPoints[] = {
8966 	// new code for points, 9 bytes
8967 	0x7a,                                // push2
8968 	0x38, PATCH_UINT16(0x00ff),          // pushi 0x00ff - using last flag slot, seems to be unused
8969 	0x39, 0x0a,                          // pushi 10d - 10 points
8970 	0x45, 0x06, 0x04,                    // callb [export 6 of script 0], 4
8971 	// original code
8972 	0x39, 0x20,                          // pushi 20h (state)
8973 	0x78,                                // push1
8974 	0x78,                                // push1
8975 	0x39, 0x43,                          // pushi 43h (at)
8976 	0x78,                                // push1
8977 	0x39, 0x25,                          // pushi 25h
8978 	0x81, 0x09,                          // lag global[9]
8979 	0x4a, 0x06,                          // send 06 - Inv::at(25h)
8980 	0x4a, 0x06,                          // send 06 - locket::state(1)
8981 	// optimized code, saving 9 bytes
8982 	0x38, PATCH_UINT16(0x009b),          // pushi 009bh (owner)
8983 	0x76,                                // push0
8984 	0x4a, 0x04,                          // send 04 - locket::owner
8985 	PATCH_END
8986 };
8987 
8988 // The doctor's mouth moves too fast in room 36. doctorMouth:cyleSpeed is set to
8989 //  one, the maximum speed, unlike any other inset in the game. Most insets use
8990 //  the default speed of six and almost all the rest use an even slower speed.
8991 //  We set the doctor's mouth to the default speed.
8992 //
8993 // Applies to: All versions
8994 // Responsible method: insetDoctor:init
8995 // Fixes bug: #10255
8996 static const uint16 pq3SignatureDoctorMouthSpeed[] = {
8997 	0x38, SIG_MAGICDWORD,                // pushi cyleSpeed
8998 	      SIG_SELECTOR16(cycleSpeed),
8999 	0x78,                                // push1
9000 	0x78,                                // push1
9001 	SIG_ADDTOOFFSET(+13),
9002 	0x4a, 0x1c,                          // send 1c [ doctorMouth ... cycleSpeed: 1 ... ]
9003 	SIG_END
9004 };
9005 
9006 static const uint16 pq3PatchDoctorMouthSpeed[] = {
9007 	0x32, PATCH_UINT16(0x0002),          // jmp 0002
9008 	PATCH_ADDTOOFFSET(+15),
9009 	0x4a, 0x16,                          // send 16 [ don't set cycleSpeed, use default (6) ]
9010 	PATCH_END
9011 };
9012 
9013 // The house fire on day six reoccurs if you return to the hospital. Flag 66
9014 //  triggers the fire sequence and is always set when leaving the hospital on
9015 //  day 6. It's then cleared when arriving at the fire.
9016 //
9017 // We add a test for flag 57, which is set at the fire, so that flag 66 isn't
9018 //  set a second time. This is also what Sierra did in later versions.
9019 //
9020 // Applies to: English PC VGA Floppy
9021 // Responsible method: outHospital:changeState(6)
9022 // Fixes bug: #11089
9023 static const uint16 pq3SignatureHouseFireRepeats[] = {
9024 	0x30, SIG_UINT16(0x0068),            // bnt 0068 [ state 7 ]
9025 	SIG_ADDTOOFFSET(+82),
9026 	SIG_MAGICDWORD,
9027 	0x30, SIG_UINT16(0x0006),            // bnt 0006 [ don't set fire-started flag ]
9028 	0x78,                                // push1
9029 	0x39, 0x42,                          // pushi 42 [ flag 66 ]
9030 	0x45, 0x09, 0x02,                    // callb proc0_9 [ set fire-started flag ]
9031 	0x38, SIG_SELECTOR16(newRoom),       // pushi newRoom
9032 	0x78,                                // push1
9033 	0x39, 0x19,                          // pushi 19
9034 	0x81, 0x02,                          // lag 02
9035 	0x4a, 0x06,                          // send 06 [ rm033 newRoom: 25 ]
9036 	0x32, SIG_UINT16(0x004c),            // jmp 004c [ end of method ]
9037 	0x3c,                                // dup
9038 	0x35, 0x07,                          // ldi 07
9039 	0x1a,                                // eq?
9040 	0x30, SIG_UINT16(0x0007),            // bnt 0007 [ state 8 ]
9041 	0x35, 0x01,                          // ldi 01
9042 	0x65, 0x10,                          // aTop cycles
9043 	0x32, SIG_UINT16(0x003e),            // jmp 003e [ end of method ]
9044 	SIG_END
9045 };
9046 
9047 static const uint16 pq3PatchHouseFireRepeats[] = {
9048 	0x30, PATCH_UINT16(0x006c),          // bnt 006c [ state 7 ]
9049 	PATCH_ADDTOOFFSET(+82),
9050 	0x31, 0x0e,                          // bnt 0e [ don't set fire-started flag ]
9051 	0x78,                                // push1
9052 	0x39, 0x39,                          // pushi 39 [ flag 57 ]
9053 	0x45, 0x0a, 0x02,                    // callb proc0_10 [ have you been to the fire? ]
9054 	0x2f, 0x06,                          // bt 06 [ don't set fire-started flag ]
9055 	0x78,                                // push1
9056 	0x39, 0x42,                          // pushi 42 [ flag 66 ]
9057 	0x45, 0x09, 0x02,                    // callb proc0_9 [ set fire-started flag ]
9058 	0x38, PATCH_SELECTOR16(newRoom),     // pushi newRoom
9059 	0x78,                                // push1
9060 	0x39, 0x19,                          // pushi 19
9061 	0x81, 0x02,                          // lag 02
9062 	0x4a, 0x06,                          // send 06 [ rm033 newRoom: 25 ]
9063 	0x3c,                                // dup
9064 	0x35, 0x07,                          // ldi 07
9065 	0x1a,                                // eq?
9066 	0x31, 0x04,                          // bnt 04 [ state 8 ]
9067 	0x35, 0x01,                          // ldi 01
9068 	0x65, 0x10,                          // aTop cycles
9069 	PATCH_END
9070 };
9071 
9072 //          script, description,                                 signature                     patch
9073 static const SciScriptPatcherEntry pq3Signatures[] = {
9074 	{  true, 33, "prevent house fire repeating",              1, pq3SignatureHouseFireRepeats, pq3PatchHouseFireRepeats },
9075 	{  true, 36, "give locket missing points",                1, pq3SignatureGiveLocketPoints, pq3PatchGiveLocketPoints },
9076 	{  true, 36, "doctor mouth speed",                        1, pq3SignatureDoctorMouthSpeed, pq3PatchDoctorMouthSpeed },
9077 	SCI_SIGNATUREENTRY_TERMINATOR
9078 };
9079 
9080 
9081 #ifdef ENABLE_SCI32
9082 #pragma mark -
9083 #pragma mark Police Quest 4
9084 
9085 // Add support for simultaneous speech & subtitles to the in-game UI.
9086 // The original game code has code paths for lo-res mode but only creates the
9087 // buttons in hi-res mode, so the lo-res code paths are removed to gain more
9088 // space for the patch.
9089 // Applies to: English CD
9090 // Responsible method: iconText::init, iconText::select
9091 static const uint16 pq4CdSpeechAndSubtitlesSignature[] = {
9092 	// iconText::init
9093 	0x76,                         // push0
9094 	0x43, 0x22, SIG_UINT16(0x00), // callk IsHiRes
9095 	0x18,                         // not
9096 	0x31, 0x05,                   // bnt [skip next 2 opcodes, when hires]
9097 	SIG_MAGICDWORD,
9098 	0x34, SIG_UINT16(0x2661),     // ldi 9825
9099 	0x65, 0x78,                   // aTop mainView
9100 	0x89, 0x5a,                   // lsg global[$5a]
9101 	0x35, 0x01,                   // ldi 1
9102 	0x12,                         // and
9103 	0x30, SIG_UINT16(0x1b),       // bnt [when in speech mode]
9104 	0x76,                         // push0
9105 	0x43, 0x22, SIG_UINT16(0x00), // callk IsHiRes
9106 	SIG_ADDTOOFFSET(+45),         // skip over the remaining code
9107 	0x38, SIG_SELECTOR16(init),   // pushi init ($93)
9108 	0x76,                         // push0
9109 	0x59, 0x01,                   // &rest 01
9110 	0x57, 0x8f, SIG_UINT16(0x04), // super GCItem, 4
9111 	0x48,                         // ret
9112 
9113 	// iconText::select
9114 	0x38, SIG_SELECTOR16(select), // pushi select ($1c4)
9115 	0x76,                         // push0
9116 	0x59, 0x01,                   // &rest 01
9117 	0x57, 0x8f, SIG_UINT16(0x04), // super GCItem, 4
9118 	0x89, 0x5a,                   // lsg global[$5a]
9119 	0x35, 0x02,                   // ldi 2
9120 	0x12,                         // and
9121 	0x30, SIG_UINT16(0x001f),     // bnt [to currently-in-text-mode code]
9122 	SIG_ADDTOOFFSET(+67),         // skip over the rest
9123 	0x48,                         // ret
9124 	SIG_END
9125 };
9126 
9127 static const uint16 pq4CdSpeechAndSubtitlesPatch[] = {
9128 	// iconText::init
9129 	0x76,                           // push0
9130 	0x41, 0x02, PATCH_UINT16(0x00), // call [our new subroutine which sets view+loop+cel], 0
9131 	0x33, 0x40,                     // jmp [to original init, super GCItem call]
9132 	// new code for setting view+loop+cel
9133 	0x34, PATCH_UINT16(0x2aeb),     // ldi 10987
9134 	0x65, 0x78,                     // aTop mainView - always set this view, because it's used by 2 states
9135 	0x89, 0x5a,                     // lsg global[$5a]
9136 	0x35, 0x03,                     // ldi 3
9137 	0x1a,                           // eq?
9138 	0x31, 0x04,                     // bnt [skip over follow up code]
9139 	// speech+subtitles mode
9140 	0x78,                           // push1
9141 	0x69, 0x7a,                     // sTop mainLoop
9142 	0x48,                           // ret
9143 	0x89, 0x5a,                     // lsg global[$5a]
9144 	0x35, 0x01,                     // ldi 1
9145 	0x12,                           // and
9146 	0x31, 0x04,                     // bnt [skip over follow up code]
9147 	// subtitles mode
9148 	0x76,                           // push0
9149 	0x69, 0x7a,                     // sTop mainLoop
9150 	0x48,                           // ret
9151 	// speech mode
9152 	0x34, PATCH_UINT16(0x2ae6),     // ldi 10982
9153 	0x65, 0x78,                     // aTop mainView
9154 	0x35, 0x0f,                     // ldi 15
9155 	0x65, 0x7a,                     // aTop mainLoop
9156 	0x48,                           // ret
9157 	PATCH_ADDTOOFFSET(+38),         // skip to iconText::select
9158 
9159 	// iconText::select
9160 	PATCH_ADDTOOFFSET(+10),         // skip over the super code
9161 	0xc1, 0x5a,                     // +ag global[$5a]
9162 	0xa1, 0x5a,                     // sag global[$5a]
9163 	0x36,                           // push
9164 	0x35, 0x04,                     // ldi 4
9165 	0x28,                           // uge?
9166 	0x31, 0x03,                     // bnt [skip over follow up code]
9167 	0x78,                           // push1
9168 	0xa9, 0x5a,                     // ssg global[$5a]
9169 	0x76,                           // push0
9170 	0x41, 0x99, PATCH_UINT16(0x00), // call [our new subroutine which sets view+loop+cel, effectively -103], 0
9171 	0x33, 0x2f,                     // jmp [to end of original select, show call]
9172 	PATCH_END
9173 };
9174 
9175 // When showing the red shoe to Barbie, after showing the police badge but
9176 // before exhausting the normal dialogue tree, the game plays the expected
9177 // dialogue but fails to award points or set an internal flag indicating this
9178 // interaction has occurred (which is needed to progress in the game).
9179 //
9180 // This is because the game checks global[$9a] (dialogue progress flag) instead
9181 // of local[3] (badge shown flag) when interacting with Barbie. The game uses
9182 // the same `shoeShoe::changeState(0)` method for showing the shoe to the young
9183 // woman at the bar earlier in the game, and checks local[3] then, so just
9184 // check local[3] in both cases to prevent the game from appearing to be in an
9185 // unwinnable state just because the player interacted in the "wrong" order.
9186 //
9187 // Applies to at least: English floppy, German floppy, English CD, German CD
9188 // Fixes bug: #9849
9189 static const uint16 pq4BittyKittyShowBarieRedShoeSignature[] = {
9190 	// stripper::noun check is for checking, if police badge was shown
9191 	SIG_MAGICDWORD,
9192 	0x89, 0x9a,                         // lsg global[$9a]
9193 	0x35, 0x02,                         // ldi 2
9194 	0x1e,                               // gt?
9195 	0x30, SIG_UINT16(0x0028),           // bnt [skip 2 points code]
9196 	0x39, SIG_SELECTOR8(points),        // pushi points ($61)
9197 	SIG_END
9198 };
9199 
9200 static const uint16 pq4BittyKittyShowBarbieRedShoePatch[] = {
9201 	0x83, 0x03,                         // lal local[3]
9202 	0x30, PATCH_UINT16(0x002b),         // bnt [skip 2 points code]
9203 	0x33, 0x01,                         // jmp 1 (waste some bytes)
9204 	PATCH_END
9205 };
9206 
9207 // In PQ4, scripts for the city hall action sequences use `ticks`. These
9208 // continue to count down even during inventory interaction, so if the user is
9209 // unable to find the correct inventory item quickly enough for the sequence,
9210 // the game will immediately end with a "game over" once they close the
9211 // inventory and the main game loop resumes. This can seem like a game bug, so
9212 // we change these sequences to use `seconds`, which only tick down by 1 when
9213 // the game returns to the main loop and the wall time has changed, even if many
9214 // seconds have actually elapsed. However, since `seconds` uses absolute
9215 // hardware clock time with a granularity of 1 second, "one" second can actually
9216 // be less than one second if the timer is set in between hardware clock
9217 // seconds, so the values are increased slightly from their equivalent tick
9218 // values to compensate for this.
9219 //
9220 // TODO: The object structure changed in PQ4CD so ticks moved from 0x20 to 0x22.
9221 // Additional signatures/patches will need to be added for CD version.
9222 //
9223 // Applies to at least: English Floppy, German floppy
9224 // Responsible method: metzAttack::changeState(2) - 120 ticks (player needs to draw gun)
9225 //                     stickScr::changeState(0) - 180 ticks (player needs to tell enemy to drop gun)
9226 //                     dropStick::changeState(5) - 120 ticks (player needs to tell enemy to turn around)
9227 //                     turnMetz::changeState(5) - 600/420 ticks (player needs to cuff Metz)
9228 //                     all in script 390
9229 static const uint16 pq4FloppyCityHallDrawGunTimerSignature[] = {
9230 	SIG_MAGICDWORD,
9231 	0x4a, SIG_UINT16(0x08), // send 8
9232 	0x32,                   // jmp [ret]
9233 	SIG_ADDTOOFFSET(+8),    // skip over some code
9234 	0x35, 0x78,             // ldi $78 (120)
9235 	0x65, 0x20,             // aTop ticks
9236 	SIG_END
9237 };
9238 
9239 static const uint16 pq4FloppyCityHallDrawGunTimerPatch[] = {
9240 	PATCH_ADDTOOFFSET(+12), // send 8, jmp, skip over some code
9241 	0x35, 0x05,             // ldi 5 (120t/2s -> 5s)
9242 	0x65, 0x1c,             // aTop seconds
9243 	PATCH_END
9244 };
9245 
9246 static const uint16 pq4FloppyCityHallTellEnemyDropWeaponTimerSignature[] = {
9247 	SIG_MAGICDWORD,
9248 	0x34, SIG_UINT16(0xb4), // ldi $b4 (180)
9249 	0x65, 0x20,             // aTop ticks
9250 	0x32, SIG_UINT16(0x5e), // jmp [to ret]
9251 	SIG_END
9252 };
9253 
9254 static const uint16 pq4FloppyCityHallTellEnemyDropWeaponTimerPatch[] = {
9255 	0x34, PATCH_UINT16(0x05), // ldi 5 (180t/3s -> 5s)
9256 	0x65, 0x1c,               // aTop seconds
9257 	PATCH_END
9258 };
9259 
9260 static const uint16 pq4FloppyCityHallTellEnemyTurnAroundTimerSignature[] = {
9261 	SIG_MAGICDWORD,
9262 	0x4a, SIG_UINT16(0x04), // send 4
9263 	0x35, 0x78,             // ldi $78 (120)
9264 	0x65, 0x20,             // aTop ticks
9265 	SIG_END
9266 };
9267 
9268 static const uint16 pq4FloppyCityHallTellEnemyTurnAroundTimerPatch[] = {
9269 	PATCH_ADDTOOFFSET(+3), // send 4
9270 	0x35, 0x03,            // ldi 3 (120t/2s -> 3s)
9271 	0x65, 0x1c,            // aTop seconds
9272 	PATCH_END
9273 };
9274 
9275 static const uint16 pq4FloppyCityHallCuffEnemyTimerSignature[] = {
9276 	SIG_MAGICDWORD,
9277 	0x34, SIG_UINT16(0x0258), // ldi $258 (600)
9278 	0x65, 0x20,               // aTop ticks
9279 	SIG_ADDTOOFFSET(+3),
9280 	0x34, SIG_UINT16(0x01a4), // ldi $1a4 (420)
9281 	0x65, 0x20,               // aTop ticks
9282 	SIG_END
9283 };
9284 
9285 static const uint16 pq4FloppyCityHallCuffEnemyTimerPatch[] = {
9286 	0x34, PATCH_UINT16(0x0a), // ldi 10 (600t/10s)
9287 	0x65, 0x1c,               // aTop seconds
9288 	PATCH_ADDTOOFFSET(+3),
9289 	0x34, PATCH_UINT16(0x07), // ldi 7 (420t/7s)
9290 	0x65, 0x1c,               // aTop seconds
9291 	PATCH_END
9292 };
9293 
9294 // The end game action sequence also uses ticks instead of seconds. See the
9295 // description of city hall action sequence issues for more information.
9296 //
9297 // Applies to at least: English Floppy, German floppy, English CD
9298 // Responsible method: comeInLast::changeState(11)
9299 static const uint16 pq4LastActionHeroTimerSignature[] = {
9300 	SIG_MAGICDWORD,
9301 	0x34, SIG_UINT16(0x012c),  // ldi $12c (300)
9302 	0x65, SIG_ADDTOOFFSET(+1), // aTop ticks ($20 for floppy, $22 for CD)
9303 	SIG_END
9304 };
9305 
9306 static const uint16 pq4LastActionHeroTimerPatch[] = {
9307 	0x34, PATCH_UINT16(0x0005),               // ldi 5 (300t/5s)
9308 	0x65, PATCH_GETORIGINALBYTEADJUST(4, -4), // aTop seconds
9309 	PATCH_END
9310 };
9311 
9312 //          script, description,                                          signature                                           patch
9313 static const SciScriptPatcherEntry pq4Signatures[] = {
9314 	{  true,     9, "add speech+subtitles to in-game UI",              1, pq4CdSpeechAndSubtitlesSignature,                   pq4CdSpeechAndSubtitlesPatch },
9315 	{  true,   315, "fix missing points showing barbie the red shoe",  1, pq4BittyKittyShowBarieRedShoeSignature,             pq4BittyKittyShowBarbieRedShoePatch },
9316 	{  true,   390, "change floppy city hall use gun timer",           1, pq4FloppyCityHallDrawGunTimerSignature,             pq4FloppyCityHallDrawGunTimerPatch },
9317 	{  true,   390, "change floppy city hall say 'drop weapon' timer", 1, pq4FloppyCityHallTellEnemyDropWeaponTimerSignature, pq4FloppyCityHallTellEnemyDropWeaponTimerPatch },
9318 	{  true,   390, "change floppy city hall say 'turn around' timer", 1, pq4FloppyCityHallTellEnemyTurnAroundTimerSignature, pq4FloppyCityHallTellEnemyTurnAroundTimerPatch },
9319 	{  true,   390, "change floppy city hall use handcuffs timer",     1, pq4FloppyCityHallCuffEnemyTimerSignature,           pq4FloppyCityHallCuffEnemyTimerPatch },
9320 	{  true,   755, "change last action sequence timer",               1, pq4LastActionHeroTimerSignature,                    pq4LastActionHeroTimerPatch },
9321 	{  true, 64908, "disable video benchmarking",                      1, sci2BenchmarkSignature,                             sci2BenchmarkPatch },
9322 	{  true, 64918, "fix Str::strip in floppy version",                1, sci2BrokenStrStripSignature,                        sci2BrokenStrStripPatch },
9323 	{  true, 64990, "increase number of save games (1/2)",             1, sci2NumSavesSignature1,                             sci2NumSavesPatch1 },
9324 	{  true, 64990, "increase number of save games (2/2)",             1, sci2NumSavesSignature2,                             sci2NumSavesPatch2 },
9325 	{  true, 64990, "disable change directory button",                 1, sci2ChangeDirSignature,                             sci2ChangeDirPatch },
9326 	SCI_SIGNATUREENTRY_TERMINATOR
9327 };
9328 
9329 #pragma mark -
9330 #pragma mark Police Quest: SWAT
9331 
9332 // The init code that runs when PQ:SWAT starts up unconditionally resets the
9333 // master sound volume to 127, but the game should always use the volume stored
9334 // in ScummVM.
9335 // Applies to at least: English CD
9336 // Fixes bug: #9700
9337 static const uint16 pqSwatVolumeResetSignature[] = {
9338 	SIG_MAGICDWORD,
9339 	0x38, SIG_SELECTOR16(masterVolume), // pushi masterVolume
9340 	0x78,                               // push1
9341 	0x39, 0x7f,                         // pushi $7f
9342 	0x54, SIG_UINT16(0x0006),           // self 6
9343 	SIG_END
9344 };
9345 
9346 static const uint16 pqSwatVolumeResetPatch[] = {
9347 	0x32, PATCH_UINT16(0x0006),         // jmp 6 [past volume reset]
9348 	PATCH_END
9349 };
9350 
9351 //          script, description,                                      signature                         patch
9352 static const SciScriptPatcherEntry pqSwatSignatures[] = {
9353 	{  true,     0, "disable volume reset on startup (1/2)",       1, pqSwatVolumeResetSignature,        pqSwatVolumeResetPatch },
9354 	{  true,     1, "disable volume reset on startup (2/2)",       1, sci2VolumeResetSignature,          sci2VolumeResetPatch },
9355 	SCI_SIGNATUREENTRY_TERMINATOR
9356 };
9357 
9358 #endif
9359 
9360 // ===========================================================================
9361 //  At the healer's house there is a bird's nest up on the tree.
9362 //   The player can throw rocks at it until it falls to the ground.
9363 //   The hero will then grab the item, that is in the nest.
9364 //
9365 //  When running is active, the hero will not reach the actual destination
9366 //   and because of that, the game will get stuck.
9367 //
9368 //  We just change the coordinate of the destination slightly, so that walking,
9369 //   sneaking and running work.
9370 //
9371 //  This bug was fixed by Sierra at least in the Japanese PC-9801 version.
9372 // Applies to at least: English floppy (1.000, 1.012)
9373 // Responsible method: pickItUp::changeState (script 54)
9374 // Fixes bug: #6407
9375 static const uint16 qfg1egaSignatureThrowRockAtNest[] = {
9376 	0x4a, 0x04,                         // send 04 (nest::x)
9377 	0x36,                               // push
9378 	SIG_MAGICDWORD,
9379 	0x35, 0x0f,                         // ldi 0f (15d)
9380 	0x02,                               // add
9381 	0x36,                               // push
9382 	SIG_END
9383 };
9384 
9385 static const uint16 qfg1egaPatchThrowRockAtNest[] = {
9386 	PATCH_ADDTOOFFSET(+3),
9387 	0x35, 0x12,                         // ldi 12 (18d)
9388 	PATCH_END
9389 };
9390 
9391 //          script, description,                                      signature                            patch
9392 static const SciScriptPatcherEntry qfg1egaSignatures[] = {
9393 	{  true,    54, "throw rock at nest while running",            1, qfg1egaSignatureThrowRockAtNest,     qfg1egaPatchThrowRockAtNest },
9394 	SCI_SIGNATUREENTRY_TERMINATOR
9395 };
9396 
9397 // ===========================================================================
9398 //  script 215 of qfg1vga pointBox::doit actually processes button-presses
9399 //   during fighting with monsters. It strangely also calls kGetEvent. Because
9400 //   the main User::doit also calls kGetEvent it's pure luck, where the event
9401 //   will hit. It's the same issue as in freddy pharkas and if you turn DOSBox
9402 //   to max cycles, sometimes clicks also won't get registered. Strangely it's
9403 //   not nearly as bad as in our sci, but these differences may be caused by
9404 //   timing.
9405 //   We just reuse the active event, thus removing the duplicate kGetEvent call.
9406 // Applies to at least: English floppy
9407 // Responsible method: pointBox::doit
9408 // Fixes bug: #5038
9409 static const uint16 qfg1vgaSignatureFightEvents[] = {
9410 	0x39, SIG_MAGICDWORD,
9411 	SIG_SELECTOR8(new),                 // pushi new
9412 	0x76,                               // push0
9413 	0x51, 0x07,                         // class Event
9414 	0x4a, 0x04,                         // send 04 - call Event::new
9415 	0xa5, 0x00,                         // sat temp[0]
9416 	0x78,                               // push1
9417 	0x76,                               // push0
9418 	0x4a, 0x04,                         // send 04 - read Event::x
9419 	0xa5, 0x03,                         // sat temp[3]
9420 	0x76,                               // push0 (selector y)
9421 	0x76,                               // push0
9422 	0x85, 0x00,                         // lat temp[0]
9423 	0x4a, 0x04,                         // send 04 - read Event::y
9424 	0x36,                               // push
9425 	0x35, 0x0a,                         // ldi 0a
9426 	0x04,                               // sub (poor mans localization) ;-)
9427 	SIG_END
9428 };
9429 
9430 static const uint16 qfg1vgaPatchFightEvents[] = {
9431 	0x38, PATCH_SELECTOR16(curEvent), // pushi curEvent (15a)
9432 	0x76,                            // push0
9433 	0x81, 0x50,                      // lag global[50]
9434 	0x4a, 0x04,                      // send 04 - read User::curEvent -> needs one byte more than previous code
9435 	0xa5, 0x00,                      // sat temp[0]
9436 	0x78,                            // push1
9437 	0x76,                            // push0
9438 	0x4a, 0x04,                      // send 04 - read Event::x
9439 	0xa5, 0x03,                      // sat temp[3]
9440 	0x76,                            // push0 (selector y)
9441 	0x76,                            // push0
9442 	0x85, 0x00,                      // lat temp[0]
9443 	0x4a, 0x04,                      // send 04 - read Event::y
9444 	0x39, 0x00,                      // pushi 00
9445 	0x02,                            // add (waste 3 bytes) - we don't need localization, User::doit has already done it
9446 	PATCH_END
9447 };
9448 
9449 // Script 814 of QFG1VGA is responsible for showing dialogs. However, the death
9450 // screen message shown when the hero dies in room 64 (ghost room) is too large
9451 // (254 chars long). Since the window header and main text are both stored in
9452 // temp space, this is an issue, as the scripts read the window header, then the
9453 // window text, which erases the window header text because of its length. To
9454 // fix that, we allocate more temp space and move the pointer used for the
9455 // window header a little bit, wherever it's used in script 814.
9456 // Fixes bug: #6139.
9457 
9458 // Patch 1: Increase temp space
9459 static const uint16 qfg1vgaSignatureTempSpace[] = {
9460 	SIG_MAGICDWORD,
9461 	0x3f, 0xba,                         // link 0xba
9462 	0x87, 0x00,                         // lap param[0]
9463 	SIG_END
9464 };
9465 
9466 static const uint16 qfg1vgaPatchTempSpace[] = {
9467 	0x3f, 0xca,                         // link 0xca
9468 	PATCH_END
9469 };
9470 
9471 // Patch 2: Move the pointer used for the window header a little bit
9472 static const uint16 qfg1vgaSignatureDialogHeader[] = {
9473 	SIG_MAGICDWORD,
9474 	0x5b, 0x04, 0x80,                   // lea temp[0x80]
9475 	0x36,                               // push
9476 	SIG_END
9477 };
9478 
9479 static const uint16 qfg1vgaPatchDialogHeader[] = {
9480 	0x5b, 0x04, 0x90,                   // lea temp[0x90]
9481 	PATCH_END
9482 };
9483 
9484 // When clicking on the crusher in room 331, Ego approaches him to talk to him,
9485 // an action that is handled by moveToCrusher::changeState in script 331. The
9486 // scripts set Ego to move close to the crusher, but when Ego is sneaking instead
9487 // of walking, the target coordinates specified by script 331 are never reached,
9488 // as Ego is making larger steps, and never reaches the required spot. This is an
9489 // edge case that can occur when Ego is set to sneak. Normally, when clicking on
9490 // the crusher, ego is supposed to move close to position 79, 165. We change it
9491 // to 85, 165, which is not an edge case thus the freeze is avoided.
9492 // Fixes bug: #6180
9493 static const uint16 qfg1vgaSignatureMoveToCrusher[] = {
9494 	SIG_MAGICDWORD,
9495 	0x51, 0x1f,                         // class Motion
9496 	0x36,                               // push
9497 	0x39, 0x4f,                         // pushi 4f (79 - x)
9498 	0x38, SIG_UINT16(0x00a5),           // pushi 00a5 (165 - y)
9499 	0x7c,                               // pushSelf
9500 	SIG_END
9501 };
9502 
9503 static const uint16 qfg1vgaPatchMoveToCrusher[] = {
9504 	PATCH_ADDTOOFFSET(+3),
9505 	0x39, 0x55,                         // pushi 55 (85 - x)
9506 	PATCH_END
9507 };
9508 
9509 // Clicking "Do" on Crusher in room 331 while standing near the card table locks
9510 //  up the game. This is due to a script bug which also occurs in the original.
9511 //  This is unrelated to bug #6180 in which clicking "Do" on Crusher while
9512 //  sneaking also locks up.
9513 //
9514 // rm331:doit sets ego's script to cardScript when ego enters a rectangle around
9515 //  the card table and sets ego's script to none when exiting. This assumes that
9516 //  ego can't have a different script set. Clicking "Do" on Crusher sets ego's
9517 //  script to moveToCrusher. If moveToCrusher causes ego to enter or exit the
9518 //  table's rectangle then the script will be stopped. When ego reaches Crusher
9519 //  he will have no script to continue the sequence and the game will be stuck
9520 //  in handsOff mode.
9521 //
9522 // We fix this by skipping the card table code in rm331:doit if ego already has
9523 //  a script other than cardScript. This prevents the card game from interfering
9524 //  with running scripts such as moveToCrusher.
9525 //
9526 // This bug was fixed in the Macintosh version by changing the card game to no
9527 //  longer involve setting ego's script and removing the code from rm331:doit.
9528 //
9529 // Applies to: PC Floppy
9530 // Responsible method: rm331:doit
9531 // Fixes bug: #10826
9532 static const uint16 qfg1vgaSignatureCrusherCardGame[] = {
9533 	SIG_MAGICDWORD,
9534 	0x63, 0x12,                         // pToa script
9535 	0x31, 0x02,                         // bnt 02
9536 	0x33, 0x28,                         // jmp 28 [ card table location tests ]
9537 	0x38, SIG_SELECTOR16(script),       // pushi script
9538 	0x76,                               // push0
9539 	0x81, 0x00,                         // lag global[0]
9540 	0x4a, 0x04,                         // send 4 [ ego:script? ]
9541 	0x31, 0x04,                         // bnt 04
9542 	0x35, 0x00,                         // ldi 00 [ does nothing ]
9543 	0x33, 0x1a,                         // jmp 1a [ card table location tests ]
9544 	SIG_ADDTOOFFSET(+113),
9545 	0x39, SIG_SELECTOR8(doit),          // pushi doit [ pc version only ]
9546 	SIG_END
9547 };
9548 
9549 static const uint16 qfg1vgaPatchCrusherCardGame[] = {
9550 	0x38, PATCH_SELECTOR16(script),     // pushi script
9551 	0x76,                               // push0
9552 	0x81, 0x00,                         // lag global[0]
9553 	0x4a, 0x04,                         // send 4 [ ego:script? ]
9554 	0x31, 0x06,                         // bnt 06
9555 	0x74, PATCH_UINT16(0x0ee4),         // lofss cardScript
9556 	0x1a,                               // eq?
9557 	0x31, 0x75,                         // bnt 75 [ skip card table location tests ]
9558 	0x63, 0x12,                         // pToa script
9559 	0x2f, 0x1a,                         // bt 1a [ card table location tests ]
9560 	PATCH_END
9561 };
9562 
9563 // Same pathfinding bug as above, where Ego is set to move to an impossible
9564 // spot when sneaking. In GuardsTrumpet::changeState, we change the final
9565 // location where Ego is moved from 111, 111 to 116, 116.
9566 // target coordinate is really problematic here.
9567 //
9568 // 114, 114 works when the speed slider is all the way up, but doesn't work
9569 // when the speed slider is not.
9570 //
9571 // It seems that this bug was fixed by Sierra for the Macintosh version.
9572 //
9573 // Applies to at least: English PC floppy
9574 // Responsible method: GuardsTrumpet::changeState(8)
9575 // Fixes bug: #6248
9576 static const uint16 qfg1vgaSignatureMoveToCastleGate[] = {
9577 	0x51, SIG_ADDTOOFFSET(+1),          // class MoveTo
9578 	SIG_MAGICDWORD,
9579 	0x36,                               // push
9580 	0x39, 0x6f,                         // pushi 6f (111d)
9581 	0x3c,                               // dup (111d) - coordinates 111, 111
9582 	0x7c,                               // pushSelf
9583 	SIG_END
9584 };
9585 
9586 static const uint16 qfg1vgaPatchMoveToCastleGate[] = {
9587 	PATCH_ADDTOOFFSET(+3),
9588 	0x39, 0x74,                         // pushi 74 (116d), changes coordinates to 116, 116
9589 	PATCH_END
9590 };
9591 
9592 // Typo in the original Sierra scripts
9593 // Looking at a cheetaur resulted in a text about a Saurus Rex
9594 // The code treats both monster types the same.
9595 // Applies to at least: English floppy
9596 // Responsible method: smallMonster::doVerb
9597 // Fixes bug: #6249
9598 static const uint16 qfg1vgaSignatureCheetaurDescription[] = {
9599 	SIG_MAGICDWORD,
9600 	0x34, SIG_UINT16(0x01b8),           // ldi 01b8
9601 	0x1a,                               // eq?
9602 	0x31, 0x16,                         // bnt 16
9603 	0x38, SIG_SELECTOR16(say),          // pushi say (0127h)
9604 	0x39, 0x06,                         // pushi 06
9605 	0x39, 0x03,                         // pushi 03
9606 	0x78,                               // push1
9607 	0x39, 0x12,                         // pushi 12 -> monster type Saurus Rex
9608 	SIG_END
9609 };
9610 
9611 static const uint16 qfg1vgaPatchCheetaurDescription[] = {
9612 	PATCH_ADDTOOFFSET(+14),
9613 	0x39, 0x11,                         // pushi 11 -> monster type cheetaur
9614 	PATCH_END
9615 };
9616 
9617 // In the "funny" room (Yorick's room) in QfG1 VGA, pulling the chain and
9618 //  then pressing the button on the right side of the room results in
9619 //  a broken game. This also happens in SSCI.
9620 // Problem is that the Sierra programmers forgot to disable the door that
9621 //  gets opened by pulling the chain. So when ego falls down and then
9622 //  rolls through the door, one method thinks that the player walks through
9623 //  it and acts that way and the other method is still doing the roll animation.
9624 // The timer that closes the door (door11) is local[5]. Setting it to 1 during
9625 //  happyFace::changeState(0) stops door11::doit from calling goTo6::init, so
9626 //  the whole issue is stopped from happening.
9627 //
9628 // Applies to at least: English floppy
9629 // Responsible method: happyFace::changeState, door11::doit
9630 // Fixes bug: #6181
9631 static const uint16 qfg1vgaSignatureFunnyRoomFix[] = {
9632 	0x65, 0x14,                         // aTop 14 (state)
9633 	0x36,                               // push
9634 	0x3c,                               // dup
9635 	0x35, 0x00,                         // ldi 00
9636 	0x1a,                               // eq?
9637 	0x30, SIG_UINT16(0x0025),           // bnt 0025 [next state]
9638 	SIG_MAGICDWORD,
9639 	0x35, 0x01,                         // ldi 01
9640 	0xa3, 0x4e,                         // sal local[4e]
9641 	SIG_END
9642 };
9643 
9644 static const uint16 qfg1vgaPatchFunnyRoomFix[] = {
9645 	PATCH_ADDTOOFFSET(+3),
9646 	0x2e, PATCH_UINT16(0x0029),         // bt 0029 [next state] - saves 4 bytes
9647 	0x35, 0x01,                         // ldi 01
9648 	0xa3, 0x4e,                         // sal local[4e]
9649 	0xa3, 0x05,                         // sal local[5] (set to 1)
9650 	0xa3, 0x05,                         // and again to make absolutely sure (actually to waste 2 bytes)
9651 	PATCH_END
9652 };
9653 
9654 // In Yorick's room, room 96, walking in certain spots in front of the rightmost
9655 //  door locks up the game. This also occurs in Sierra's interpreter.
9656 //
9657 // rm96:doit runs the script goTo2 when ego enters a rect in front of the door.
9658 //  This rect is low enough that ego can collide with the door's boundary
9659 //  obstacle on the right and prevent goTo2 from restoring control to the user.
9660 //
9661 // We fix this by raising the bottom of the door rect. Sierra fixed this bug in
9662 //  the Mac version by rewriting the door code, switching to control areas, and
9663 //  tweaking the sizes and locations of all the relevant objects.
9664 //
9665 // Applies to: PC Floppy
9666 // Responsible method: rm96:doit
9667 // Fixes bug #6410
9668 static const uint16 qfg1vgaSignatureYorickDoorTwoRect[] = {
9669 	SIG_MAGICDWORD,
9670 	0x38, SIG_UINT16(0x0135),               // pushi 0135 [ x = 309 ]
9671 	0x39, 0x64,                             // pushi 64   [ y = 100 ]
9672 	0x38, SIG_UINT16(0x013f),               // pushi 013f [ x = 319 ]
9673 	0x39, 0x70,                             // pushi 70   [ y = 112 ]
9674 	SIG_END
9675 };
9676 
9677 static const uint16 qfg1vgaPatchYorickDoorTwoRect[] = {
9678 	PATCH_ADDTOOFFSET(+8),
9679 	0x39, 0x6d,                             // pushi 6d [ y = 109 ]
9680 	PATCH_END
9681 };
9682 
9683 // The player is able to buy (and also steal) potions in the healer's hut
9684 //  Strangely Sierra delays the actual buy/get potion code for 60 ticks
9685 //  Why they did that is unknown. The code is triggered anyway only after
9686 //  the relevant dialog boxes are closed.
9687 //
9688 // This delay causes problems in case the user quickly enters the inventory.
9689 // That's why we change the amount of ticks to 1, so that the remaining states
9690 //  are executed right after the dialog boxes are closed.
9691 //
9692 // Applies to at least: English floppy
9693 // Responsible method: cueItScript::changeState
9694 // Fixes bug: #6706
9695 static const uint16 qfg1vgaSignatureHealerHutNoDelay[] = {
9696 	0x65, 0x14,                         // aTop 14 (state)
9697 	0x36,                               // push
9698 	0x3c,                               // dup
9699 	0x35, 0x00,                         // ldi 00
9700 	0x1a,                               // eq?
9701 	0x31, 0x07,                         // bnt 07 [next state]
9702 	SIG_MAGICDWORD,
9703 	0x35, 0x3c,                         // ldi 3c (60 ticks)
9704 	0x65, 0x20,                         // aTop ticks
9705 	0x32,                               // jmp [end of method]
9706 	SIG_END
9707 };
9708 
9709 static const uint16 qfg1vgaPatchHealerHutNoDelay[] = {
9710 	PATCH_ADDTOOFFSET(+9),
9711 	0x35, 0x01,                         // ldi 01 (1 tick only, so that execution will resume as soon as dialog box is closed)
9712 	PATCH_END
9713 };
9714 
9715 // When following the white stag, you can actually enter the 2nd room from the
9716 //  mushroom/fairy location, which results in ego entering from the top. When
9717 //  you then throw a dagger at the stag, one animation frame will stay on
9718 //  screen, because of a script bug.
9719 //
9720 // Applies to at least: English floppy, Mac floppy
9721 // Responsible method: stagHurt::changeState
9722 // Fixes bug: #6135
9723 static const uint16 qfg1vgaSignatureWhiteStagDagger[] = {
9724 	0x87, 0x01,                         // lap param[1]
9725 	0x65, 0x14,                         // aTop state
9726 	0x36,                               // push
9727 	0x3c,                               // dup
9728 	0x35, 0x00,                         // ldi 0
9729 	0x1a,                               // eq?
9730 	0x31, 0x16,                         // bnt [next parameter check]
9731 	0x76,                               // push0
9732 	0x45, 0x02, 0x00,                   // callb [export 2 of script 0], 0
9733 	SIG_MAGICDWORD,
9734 	0x38, SIG_SELECTOR16(say),          // pushi say (0127h)
9735 	0x39, 0x05,                         // pushi 05
9736 	0x39, 0x03,                         // pushi 03
9737 	0x39, 0x51,                         // pushi 51h
9738 	0x76,                               // push0
9739 	0x76,                               // push0
9740 	0x7c,                               // pushSelf
9741 	0x81, 0x5b,                         // lag global[5Bh] -> qg1Messager
9742 	0x4a, 0x0e,                         // send 0Eh -> qg1Messager::say(3, 51h, 0, 0, stagHurt)
9743 	0x33, 0x12,                         // jmp [end of method]
9744 	0x3c,                               // dup
9745 	0x35, 0x01,                         // ldi 1
9746 	0x1a,                               // eq?
9747 	0x31, 0x0c,                         // bnt [end of method]
9748 	0x38,                               // pushi...
9749 	SIG_ADDTOOFFSET(+11),
9750 	0x3a,                               // toss
9751 	0x48,                               // ret
9752 	SIG_END
9753 };
9754 
9755 static const uint16 qfg1vgaPatchWhiteStagDagger[] = {
9756 	PATCH_ADDTOOFFSET(+4),
9757 	0x2f, 0x05,                         // bt [next check] (state != 0)
9758 	// state = 0 code
9759 	0x35, 0x01,                         // ldi 1
9760 	0x65, 0x1a,                         // aTop cycles
9761 	0x48,                               // ret
9762 	0x36,                               // push
9763 	0x35, 0x01,                         // ldi 1
9764 	0x1a,                               // eq?
9765 	0x31, 0x16,                         // bnt [state = 2 code]
9766 	// state = 1 code
9767 	0x76,                               // push0
9768 	0x45, 0x02, 0x00,                   // callb [export 2 of script 0], 0
9769 	0x38, PATCH_SELECTOR16(say),        // pushi say (0127h)
9770 	0x39, 0x05,                         // pushi 05
9771 	0x39, 0x03,                         // pushi 03
9772 	0x39, 0x51,                         // pushi 51h
9773 	0x76,                               // push0
9774 	0x76,                               // push0
9775 	0x7c,                               // pushSelf
9776 	0x81, 0x5b,                         // lag global[5Bh] -> qg1Messager
9777 	0x4a, 0x0e,                         // send 0Eh -> qg1Messager::say(3, 51h, 0, 0, stagHurt)
9778 	0x48,                               // ret
9779 	// state = 2 code
9780 	PATCH_ADDTOOFFSET(+13),
9781 	0x48,                               // ret (remove toss)
9782 	PATCH_END
9783 };
9784 
9785 // The dagger range has a script bug that can freeze the game or cause Brutus
9786 // to kill hero even after Brutus dies.
9787 //
9788 // When Bruno leaves, a 300 tick countdown starts. If hero kills Brutus or
9789 // leaves room 73 within those 300 ticks, then the game is left in a broken
9790 // state. For the rest of the game, if hero ever returns to the dagger range
9791 // from the east or west during the first half of the day, then the game will
9792 // freeze or Brutus, dead or not, will kill hero.
9793 //
9794 // Special thanks, credits and kudos to sluicebox, who did a ton of research on
9795 // this and even found this game bug originally.
9796 //
9797 // Applies to at least: English floppy, Mac floppy
9798 // Responsible method: brutusWaits::changeState
9799 // Fixes bug: #9558
9800 static const uint16 qfg1vgaSignatureBrutusScriptFreeze[] = {
9801 	0x78,                               // push1
9802 	0x38, SIG_UINT16(0x0144),           // pushi 144h (324d)
9803 	0x45, 0x05, 0x02,                   // callb [export 5 of script 0], 2
9804 	SIG_MAGICDWORD,
9805 	0x34, SIG_UINT16(0x012c),           // ldi 12Ch (300d)
9806 	0x65, 0x20,                         // aTop ticks
9807 	SIG_END
9808 };
9809 
9810 static const uint16 qfg1vgaPatchBrutusScriptFreeze[] = {
9811 	0x34, PATCH_UINT16(0x0000),         // ldi 0 (waste 7 bytes)
9812 	0x35, 0x00,                         // ldi 0
9813 	0x35, 0x00,                         // ldi 0
9814 	PATCH_END
9815 };
9816 
9817 // Patch the speed test so that it always ends up at the highest level. This
9818 //  improves the detail in Yorick's room (96), and slightly changes the timing
9819 //  in other rooms. This is compatible with PC and Mac versions which use
9820 //  significantly different tests and calculations.
9821 //
9822 // Applies to: PC Floppy, Mac Floppy
9823 // Responsible method: speedTest:changeState(2)
9824 static const uint16 qfg1vgaSignatureSpeedTest[] = {
9825 	0x76,                               // push0
9826 	0x43, 0x42, 0x00,                   // callk GetTime, 0
9827 	SIG_MAGICDWORD,
9828 	0x36,                               // push
9829 	0x83, 0x01,                         // lal 01
9830 	0x04,                               // sub
9831 	0xa3, 0x00,                         // sal 00
9832 	SIG_END
9833 };
9834 
9835 static const uint16 qfg1vgaPatchSpeedTest[] = {
9836 	0x35, 0x00,                         // ldi 00 [ local0 = 0, the best result ]
9837 	0x33, 0x04,                         // jmp 04
9838 	PATCH_END
9839 };
9840 
9841 // QFG1VGA has a bug where exceeding the weight limit during certain scenes
9842 //  breaks the character screen for the rest of the game. Picking mushrooms,
9843 //  searching cheetaurs, and fetching the seed are among the vulnerable actions.
9844 //
9845 // When adding inventory, ego:get displays a warning if the new items exceed the
9846 //  weight limit. If this happens while qfgMessager is displaying a message
9847 //  then both will display at the same time but only one will be disposed. This
9848 //  leaves an extra entry in the kernel's window list for the rest of the game.
9849 //  kDisplay then sends text to the wrong window, breaking the character screen
9850 //  and others, and prevents the player from ever viewing their stats.
9851 //
9852 // We fix this by adding a check to ego:get that skips displaying messages if a
9853 //  dialog already exists. This is what Sierra did in the Mac version after
9854 //  reverting the scene-specific patches they issued for the PC version.
9855 //
9856 // Applies to: PC Floppy
9857 // Responsible method: ego:get
9858 // Fixes bug: #10942
9859 static const uint16 qfg1vgaSignatureInventoryWeightWarn[] = {
9860 	0x8f, 0x00,                         // lsp 00
9861 	0x35, 0x01,                         // ldi 01
9862 	0x1a,                               // eq?
9863 	0x31, 0x04,                         // bnt 04
9864 	0x35, 0x01,                         // ldi 01
9865 	0x33, 0x02,                         // jmp 02
9866 	0x87, 0x02,                         // lap 02
9867 	0xa5, SIG_MAGICDWORD, 0x01,         // sat 01
9868 	0x38, SIG_UINT16(0x024d),           // pushi amount [ hard-coded for PC ]
9869 	0x76,                               // push0
9870 	0x85, 0x00,                         // lat 00
9871 	0x4a, 0x04,                         // send 04 [ temp0 amount? ]
9872 	0xa5, 0x02,                         // sat 02
9873 	SIG_ADDTOOFFSET(+0x0092),
9874 	0x8d, 0x01,                         // lst 01
9875 	SIG_END
9876 };
9877 
9878 static const uint16 qfg1vgaPatchInventoryWeightWarn[] = {
9879 	0x87, 0x00,                         // lap 00
9880 	0x78,                               // push1 [ save 1 byte ]
9881 	0x1a,                               // eq?
9882 	0x2f, 0x02,                         // bt 02 [ save 4 bytes ]
9883 	0x87, 0x02,                         // lap 02
9884 	0xa5, 0x01,                         // sat 01
9885 	0x38, PATCH_UINT16(0x024d),         // pushi amount [ hard-coded for PC ]
9886 	0x76,                               // push0
9887 	0x85, 0x00,                         // lat 00
9888 	0x4a, 0x04,                         // send 04 [ temp0 amount? ]
9889 	0xa5, 0x02,                         // sat 02
9890 	0x81, 0x19,                         // lag 19  [ dialog ]
9891 	0x2e, PATCH_UINT16(0x0092),         // bt 0092 [ skip messages if dialog ]
9892 	PATCH_END
9893 };
9894 
9895 // The baby antwerps in room 78 lockup the game if they get in ego's way when
9896 //  exiting south. They also crash the interpreter if they wander too far off
9897 //  the screen. These problems also occur in Sierra's interpreter.
9898 //
9899 // The antwerps are controlled by the Wander motion which randomly moves them
9900 //  around. There's nothing stopping them from moving past the southern edge of
9901 //  the screen and they tend to do so. When ego moves south of 180, sExitAll
9902 //  disables control and moves ego off screen, but if an antwerp is in the way
9903 //  then ego stops and the user never regains control. Once an antwerp has
9904 //  escaped the screen it continues to wander until its position overflows
9905 //  several minutes later. This freezes Sierra's interpreter and currently
9906 //  causes ours to fail an assertion due to constructing an invalid Rect.
9907 //
9908 // We fix both problems by not allowing antwerps south of 180 so that they
9909 //  remain on screen and can't block ego's exit. This is consistent with
9910 //  room 85 where they also appear but without a southern exit. The Wander
9911 //  motion is only used by antwerps and the sparkles above Yorick in room 96,
9912 //  so it can be safely patched to enforce a southern limit. We make room for
9913 //  this patch by replacing Wander's calculations with their known results,
9914 //  since the default Wander:distance of 30 is always used, and by overwriting
9915 //  Wander:onTarget which no script calls and just returns zero.
9916 //
9917 // Applies to: PC Floppy, Mac Floppy
9918 // Responsible method: Wander:setTarget
9919 // Fixes bug: #9564
9920 static const uint16 qfg1vgaSignatureAntwerpWander[] = {
9921 	SIG_MAGICDWORD,
9922 	0x3f, 0x01,                             // link 01
9923 	0x78,                                   // push1
9924 	0x76,                                   // push0
9925 	0x63, 0x12,                             // pToa client
9926 	0x4a, 0x04,                             // send 4
9927 	0x36,                                   // push
9928 	0x67, 0x30,                             // pTos distance
9929 	0x7a,                                   // push2
9930 	0x76,                                   // push0
9931 	0x67, 0x30,                             // pTos distance
9932 	0x35, 0x02,                             // ldi 02
9933 	0x06,                                   // mul
9934 	0xa5, 0x00,                             // sat temp[0] [ distance * 2 ]
9935 	0x36,                                   // push
9936 	0x43, 0x3c, 0x04,                       // callk Random, 4
9937 	0x04,                                   // sub
9938 	0x02,                                   // add
9939 	0x65, 0x16,                             // aTop x [ x = client:x + (distance - Random(0, temp[0])) ]
9940 	0x76,                                   // push0
9941 	0x76,                                   // push0
9942 	0x63, 0x12,                             // pToa client
9943 	0x4a, 0x04,                             // send 4
9944 	0x36,                                   // push
9945 	0x67, 0x30,                             // pTos distance
9946 	0x7a,                                   // push2
9947 	0x76,                                   // push0
9948 	0x8d, 0x00,                             // lst temp[0]
9949 	0x43, 0x3c, 0x04,                       // callk Random, 4
9950 	0x04,                                   // sub
9951 	0x02,                                   // add
9952 	0x65, 0x18,                             // aTop y [ y = client:y + (distance - Random(0, temp[0])) ]
9953 	0x48,                                   // ret
9954 	0x35, 0x00,                             // ldi 00 [ start of Wander:onTarget, returns 0 and isn't called ]
9955 	SIG_END
9956 };
9957 
9958 static const uint16 qfg1vgaPatchAntwerpWander[] = {
9959 	0x78,                                   // push1
9960 	0x76,                                   // push0
9961 	0x63, 0x12,                             // pToa client
9962 	0x4a, 0x04,                             // send 4
9963 	0x36,                                   // push
9964 	0x39, 0x1e,                             // pushi 1e
9965 	0x7a,                                   // push2
9966 	0x76,                                   // push0
9967 	0x39, 0x3c,                             // pushi 3c
9968 	0x43, 0x3c, 0x04,                       // callk Random, 4
9969 	0x04,                                   // sub
9970 	0x02,                                   // add
9971 	0x65, 0x16,                             // aTop x [ x = client:x + (30d - Random(0, 60d)) ]
9972 	0x76,                                   // push0
9973 	0x76,                                   // push0
9974 	0x63, 0x12,                             // pToa client
9975 	0x4a, 0x04,                             // send 4
9976 	0x36,                                   // push
9977 	0x39, 0x1e,                             // pushi 1e
9978 	0x7a,                                   // push2
9979 	0x76,                                   // push0
9980 	0x39, 0x3c,                             // pushi 3c
9981 	0x43, 0x3c, 0x04,                       // callk Random, 4
9982 	0x04,                                   // sub
9983 	0x02,                                   // add
9984 	0x7a,                                   // push2
9985 	0x36,                                   // push
9986 	0x38, PATCH_UINT16(0x00b4),             // pushi 00b4
9987 	0x46, PATCH_UINT16(0x03e7),             // calle [export 2 of script 999], 4 [ Min ]
9988 	      PATCH_UINT16(0x0002), 0x04,
9989 	0x65, 0x18,                             // aTop y [ y = Min(client:y + (30d - Random(0, 60d))), 180d) ]
9990 	PATCH_END
9991 };
9992 
9993 // QFG1VGA Mac disables all controls when the antwerp falls in room 78, killing
9994 //  the player by not allowing them to defend themselves.
9995 //
9996 // The antwerp falls in rooms 78 and 85, and the only way to survive is to hold
9997 //  up a weapon. These two antwerp scripts were identical in the PC version and
9998 //  enabled all menu icons even though most of them couldn't really be used.
9999 //  Sierra attempted to improve this in Mac by only enabling the inventory
10000 //  icons but instead disabled everything in room 78 by not calling the enable
10001 //  procedure.
10002 //
10003 // We fix this by calling the enable procedure like the script in room 85 does.
10004 //
10005 // Applies to: Mac Floppy
10006 // Responsible method: antwerped:changeState(1)
10007 // Fixes bug: #10856
10008 static const uint16 qfg1vgaSignatureMacAntwerpControls[] = {
10009 	0x30, SIG_UINT16(0x0033),               // bnt 0033 [ state 1 ]
10010 	SIG_ADDTOOFFSET(+48),
10011 	SIG_MAGICDWORD,
10012 	0x32, SIG_UINT16(0x014e),               // jmp 014e [ end of method ]
10013 	0x3c,                                   // dup
10014 	0x35, 0x01,                             // ldi 01
10015 	0x1a,                                   // eq?
10016 	0x30, SIG_UINT16(0x0033),               // bnt 0033 [ state 2 ]
10017 	0x38, SIG_UINT16(0x00f9),               // pushi 00f9 [ canControl, hard coded for Mac ]
10018 	SIG_END
10019 };
10020 
10021 static const uint16 qfg1vgaPatchMacAntwerpControls[] = {
10022 	0x30, PATCH_UINT16(0x0030),             // bnt 0030 [ state 1 ]
10023 	PATCH_ADDTOOFFSET(+48),
10024 	0x3c,                                   // dup
10025 	0x35, 0x01,                             // ldi 01
10026 	0x1a,                                   // eq?
10027 	0x31, 0x37,                             // bnt 37 [ state 2 ]
10028 	0x76,                                   // push0
10029 	0x45, 0x03, 0x00,                       // callb [export 3 of script 0], 00 [ enable all input ]
10030 	PATCH_END
10031 };
10032 
10033 // The Mac version's Sierra logo and introduction are often skipped when using a
10034 //  mouse. This is a bug in the original that accidentally relies on slower
10035 //  machines. In DOS these scenes could be skipped by pressing Enter. Sierra
10036 //  updated this to include the mouse, but they did this by accepting any event
10037 //  type, including mouse-up. These rooms load in response to mouse-down and if
10038 //  they finish loading before the button is released then they are skipped.
10039 //
10040 // We fix this by excluding mouse-up events from these room event handlers.
10041 //
10042 // Applies to: Mac Floppy
10043 // Responsible methods: LogoRoom:handleEvent, intro:handleEvent
10044 // Fixes bug: #10937
10045 static const uint16 qfg1vgaSignatureMacLogoIntroSkip[] = {
10046 	0x4a, 0x04,                             // send 04 [ event type? ]
10047 	0x31, SIG_ADDTOOFFSET(+1),              // bnt [ skip if event:type == none (0) ]
10048 	0x39, SIG_ADDTOOFFSET(+1),              // pushi claimed
10049 	SIG_MAGICDWORD,
10050 	0x78,                                   // push1
10051 	0x78,                                   // push1
10052 	0x87, 0x01,                             // lap 01
10053 	0x4a, 0x06,                             // send 06 [ event claimed: 1 ]
10054 	SIG_END
10055 };
10056 
10057 static const uint16 qfg1vgaPatchMacLogoIntroSkip[] = {
10058 	0x39, PATCH_GETORIGINALBYTE(+5),        // pushi claimed
10059 	0x78,                                   // push1
10060 	0x78,                                   // push1
10061 	0x4a, 0x0a,                             // send 0a [ event type? claimed: 1 ]
10062 	0x38, PATCH_UINT16(0x00fd),             // pushi 00fd
10063 	0x12,                                   // and
10064 	0x31, PATCH_GETORIGINALBYTEADJUST(+3, -8), // bnt [ skip if event:type == none (0) or mouse-up (2)]
10065 	PATCH_END
10066 };
10067 
10068 // The Thieves' Guild cashier in room 332 stops responding to verbs when he
10069 //  reappears at his window. This is due to heGoes:changeState(1) disposing and
10070 //  deleting borisThief once he's out of sight, indirectly deleting his actions
10071 //  object borisTeller which handles verbs. borisTeller is only initialized in
10072 //  rm332:init and this leaves the player unable to purchase or fence items.
10073 //
10074 // We fix this by toggling borisThief's visibility with the hide and show
10075 //  methods instead of disposing and re-initializing.
10076 //
10077 // Applies to: PC Floppy, Mac Floppy
10078 // Responsible methods: heComes:changeState, heGoes:changeState
10079 // Fixes bug #10939
10080 static const uint16 qfg1vgaSignatureThievesGuildCashier[] = {
10081 	0x30, SIG_UINT16(0x0024),               // bnt 0024 [ state 1 ]
10082 	SIG_ADDTOOFFSET(+31),
10083 	SIG_MAGICDWORD,
10084 	0x4a, 0x20,                             // send 20  [ borisThief ... init: ... ]
10085 	0x32, SIG_UINT16(0x002b),               // jmp 002b [ end of method ]
10086 	0x3c,                                   // dup
10087 	0x35, 0x01,                             // ldi 01
10088 	0x1a,                                   // eq?
10089 	0x30, SIG_UINT16(0x0019),               // bnt 0019 [ state 2 ]
10090 	SIG_ADDTOOFFSET(+82),
10091 	0x39, SIG_SELECTOR8(dispose),           // pushi dispose
10092 	0x76,                                   // push0
10093 	0x39, SIG_SELECTOR8(delete),            // pushi delete
10094 	SIG_ADDTOOFFSET(+4),
10095 	0x4a, 0x08,                             // send 08 [ borisThief dispose: delete: ]
10096 	SIG_END
10097 };
10098 
10099 static const uint16 qfg1vgaPatchThievesGuildCashier[] = {
10100 	0x30, PATCH_UINT16(0x0025),             // bnt 0025 [ state 1 ]
10101 	PATCH_ADDTOOFFSET(+31),
10102 	0x38, PATCH_SELECTOR16(show),           // pushi show
10103 	0x76,                                   // push0
10104 	0x4a, 0x24,                             // send 24 [ borisThief ... init: ... show: ]
10105 	0x3c,                                   // dup
10106 	0x35, 0x01,                             // ldi 01
10107 	0x1a,                                   // eq?
10108 	0x31, 0x19,                             // bnt 19 [ state 2 ]
10109 	PATCH_ADDTOOFFSET(+82),
10110 	0x39, PATCH_SELECTOR8(hide),            // pushi hide
10111 	0x32, PATCH_UINT16(0x0000),             // jmp 0000
10112 	PATCH_ADDTOOFFSET(+4),
10113 	0x4a, 0x04,                             // send 04 [ borisThief hide: ]
10114 	PATCH_END
10115 };
10116 
10117 // When entering the great hall (room 141), the Mac version stores ego's speed
10118 //  in a temp variable in egoEnters:changeState(0) and expects that value to be
10119 //  there in state 6 when restoring ego's speed. We patch the script to use its
10120 //  register instead so that it works and doesn't do an uninitialized read.
10121 //
10122 // Applies to: Mac Floppy
10123 // Responsible method: egoEnters:changeState
10124 // Fixes bug: #10945
10125 static const uint16 qfg1vgaSignatureMacEnterGreatHall[] = {
10126 	SIG_MAGICDWORD,
10127 	0x4a, 0x04,                             // send 04 [ ego cycleSpeed? ]
10128 	0xa5, 0x00,                             // sat 00
10129 	SIG_ADDTOOFFSET(+140),
10130 	0x8d, 0x00,                             // lst 00
10131 	SIG_END
10132 };
10133 
10134 static const uint16 qfg1vgaPatchMacEnterGreatHall[] = {
10135 	PATCH_ADDTOOFFSET(+2),
10136 	0x65, 0x24,                             // aTop register
10137 	PATCH_ADDTOOFFSET(+140),
10138 	0x67, 0x24,                             // pTos register
10139 	PATCH_END
10140 };
10141 
10142 // When fighting the giant in room 58, the Mac version stores the weapon in a
10143 //  temp variable in giantFights:changeState(2) and expects that value to be
10144 //  there in later states to determine whether or not to run sword animation.
10145 //  We patch the script to use the local variable that holds the weapon instead
10146 //  so that this works and doesn't do an uninitialized read.
10147 //
10148 // Applies to: Mac Floppy
10149 // Responsible method: giantFights:changeState
10150 // Fixes bug: #10948
10151 static const uint16 qfg1vgaSignatureMacGiantFight[] = {
10152 	SIG_MAGICDWORD,
10153 	0x8d, 0x00,                             // lst 00 [ temp0 set to 0 in state 2 if local5 == 1 ]
10154 	0x35, 0x00,                             // ldi 00
10155 	0x1a,                                   // eq?
10156 	SIG_END
10157 };
10158 
10159 static const uint16 qfg1vgaPatchMacGiantFight[] = {
10160 	0x8b, 0x05,                             // lsl 05
10161 	0x35, 0x01,                             // ldi 01
10162 	PATCH_END
10163 };
10164 
10165 // Dag-Nab-It, the dagger game in room 340, mistakenly leaves inventory enabled.
10166 //  Using a throwable item, such as a dagger, locks up the game.
10167 //
10168 // We fix this by disabling inventory controls on this screen. Sierra attempted
10169 //  to do this in the Mac version but introduced a new bug which we also fix.
10170 //
10171 // Applies to: PC Floppy
10172 // Responsible method: dagnabitScript:changeState
10173 // Fixes bug: #10958
10174 static const uint16 qfg1vgaSignatureDagnabitInventory[] = {
10175 	0x38, SIG_SELECTOR16(disable),          // pushi disable
10176 	0x39, 0x03,                             // pushi 03
10177 	0x78,                                   // push1
10178 	0x39, SIG_MAGICDWORD, 0x05,             // pushi 05
10179 	0x39, 0x09,                             // pushi 09
10180 	0x81, 0x45,                             // lag 45
10181 	0x4a, 0x0a,                             // send 0a [ mainIconBar disable: 1 5 9 ]
10182 	0x35, 0x01,                             // ldi 01
10183 	0xa3, 0x13,                             // sal 13
10184 	0x32, SIG_UINT16(0x0278),               // jmp 0278 [ end of method ]
10185 	SIG_ADDTOOFFSET(+625),
10186 	0x38, SIG_SELECTOR16(changeState),      // pushi changeState
10187 	0x78,                                   // push1
10188 	0x78,                                   // push1
10189 	SIG_END
10190 };
10191 
10192 static const uint16 qfg1vgaPatchDagnabitInventory[] = {
10193 	PATCH_ADDTOOFFSET(+3),
10194 	0x39, 0x05,                             // pushi 05
10195 	PATCH_ADDTOOFFSET(+5),
10196 	0x39, 0x07,                             // pushi 07 [ "use" inventory icon ]
10197 	0x39, 0x08,                             // pushi 08 [ inventory ]
10198 	0x81, 0x45,                             // lag 45
10199 	0x4a, 0x0e,                             // send 0e [ mainIconBar disable: 1 5 9 7 8 ]
10200 	0x78,                                   // push1
10201 	0xab, 0x13,                             // ssl 13
10202 	PATCH_ADDTOOFFSET(+629),
10203 	0x76,                                   // push0 [ state 0 re-disables inventory ]
10204 	PATCH_END
10205 };
10206 
10207 // The Mac version of Dag-Nab-It, the dagger game in room 340, introduced a bug
10208 //  that sends a message to a non-object when clicking during the start.
10209 //
10210 // The PC version left inventory enabled and Sierra fixed this in Mac. Sierra
10211 //  also attempted to clear the inventory cursor, but this was done in a way
10212 //  that leaves the icon bar in an illegal state. mainIconBar:curInvIcon is
10213 //  set to zero but mainIconBar:curIcon remains set to the "use" icon item.
10214 //  Clicking anywhere during the initial two seconds causes IconBar:handleEvent
10215 //  to query curInvIcon:message but since curInvIcon is zero this is an error.
10216 //
10217 // We fix this with a deceptively simple patch that prevents the "use" icon from
10218 //  ending up as mainIconBar:curIcon. rm340:init runs a complex sequence of icon
10219 //  disabling and enabling. Patching a redundant mainIconBar:disable to include
10220 //  "use" prevents the subsequent call to handsOff from cycling through enabled
10221 //  icons and landing on "use" as the new curIcon, preventing the illegal state.
10222 //
10223 // Applies to: Mac Floppy
10224 // Responsible method: rm340:init
10225 // Fixes bug: #10958
10226 static const uint16 qfg1vgaSignatureMacDagnabitIconBar[] = {
10227 	0x38, SIG_SELECTOR16(disable),          // pushi disable
10228 	0x39, 0x03,                             // pushi 03
10229 	0x78,                                   // push1
10230 	SIG_MAGICDWORD,
10231 	0x39, 0x05,                             // pushi 05
10232 	0x39, 0x08,                             // pushi 08
10233 	0x81, 0x45,                             // lag 45
10234 	0x4a, 0x0a,                             // send 0a [ mainIconBar disable: 1 5 8 ]
10235 	SIG_END
10236 };
10237 
10238 static const uint16 qfg1vgaPatchMacDagnabitIconBar[] = {
10239 	PATCH_ADDTOOFFSET(+6),
10240 	0x39, 0x07,                             // pushi 07 [ "use" inventory icon ]
10241 	PATCH_END
10242 };
10243 
10244 // Drinking water in room 87 after talking to the healer about falling water
10245 //  shows two messages at once. The second message overwrites the first before
10246 //  it is shown. We add a state for the second message as in the Mac version.
10247 //
10248 // Applies to: PC Floppy
10249 // Responsible method: drinkWater:changeState
10250 // Fixes bug: #11086
10251 static const uint16 qfg1vgaSignatureDrinkWaterMessage[] = {
10252 	0x31, SIG_MAGICDWORD, 0x29,             // bnt 29 [ state 3 handler ]
10253 	0x38, SIG_SELECTOR16(say),              // pushi say
10254 	0x39, 0x05,                             // pushi 05
10255 	0x39, 0x07,                             // pushi 07
10256 	0x76,                                   // push0
10257 	0x76,                                   // push0
10258 	0x78,                                   // push1
10259 	0x7c,                                   // pushSelf
10260 	0x81, 0x5b,                             // lag 5b
10261 	0x4a, 0x0e,                             // send 0e [ qg1Messager say: 7 0 0 1 self ]
10262 	0x78,                                   // push1
10263 	0x38, SIG_UINT16(0x00c9),               // pushi 00c9 [ flag 201 ]
10264 	0x45, 0x07, 0x02,                       // callb proc0_7 [ talked to healer about water? ]
10265 	0x31, 0x42,                             // bnt 42 [ skip second message ]
10266 	SIG_ADDTOOFFSET(+9),
10267 	0x7a,                                   // push2 [ message seq: 2 ]
10268 	SIG_ADDTOOFFSET(+8),
10269 	0x35, 0x03,                             // ldi 03 [ state 3 ]
10270 	SIG_ADDTOOFFSET(+18),
10271 	0x35, 0x04,                             // ldi 04 [ state 4 ]
10272 	SIG_END
10273 };
10274 
10275 static const uint16 qfg1vgaPatchDrinkWaterMessage[] = {
10276 	0x2f, 0x14,                             // bt 14  [ show first message in state 2 ]
10277 	0x3c,                                   // dup
10278 	0x35, 0x03,                             // ldi 03 [ state 3 ]
10279 	0x1a,                                   // eq?
10280 	0x31, 0x23,                             // bnt 23 [ state 4 handler ]
10281 	0x78,                                   // push1
10282 	0x38, PATCH_UINT16(0x00c9),             // pushi 00c9 [ flag 201 ]
10283 	0x45, 0x07, 0x02,                       // callb proc0_7 [ talked to healer about water? ]
10284 	0x2f, 0x05,                             // bt 05 [ show second message in state 3 ]
10285 	0x78,                                   // push1
10286 	0x69, 0x1a,                             // sTop cycles [ cycles = 1 ]
10287 	0x3a,                                   // toss
10288 	0x48,                                   // ret
10289 	0x3c,                                   // dup
10290 	0x35, 0x01,                             // ldi 01
10291 	0x04,                                   // sub
10292 	PATCH_ADDTOOFFSET(+9),
10293 	0x36,                                   // push [ message seq: state - 1 ]
10294 	PATCH_ADDTOOFFSET(+8),
10295 	0x35, 0x04,                             // ldi 04 [ state 4 ]
10296 	PATCH_ADDTOOFFSET(+18),
10297 	0x35, 0x05,                             // ldi 05 [ state 5 ]
10298 	PATCH_END
10299 };
10300 
10301 //          script, description,                                      signature                            patch
10302 static const SciScriptPatcherEntry qfg1vgaSignatures[] = {
10303 	{  true,     0, "inventory weight warning",                    1, qfg1vgaSignatureInventoryWeightWarn, qfg1vgaPatchInventoryWeightWarn },
10304 	{  true,    41, "moving to castle gate",                       1, qfg1vgaSignatureMoveToCastleGate,    qfg1vgaPatchMoveToCastleGate },
10305 	{  true,    55, "healer's hut, no delay for buy/steal",        1, qfg1vgaSignatureHealerHutNoDelay,    qfg1vgaPatchHealerHutNoDelay },
10306 	{  true,    58, "mac: giant fight",                            6, qfg1vgaSignatureMacGiantFight,       qfg1vgaPatchMacGiantFight },
10307 	{  true,    73, "brutus script freeze glitch",                 1, qfg1vgaSignatureBrutusScriptFreeze,  qfg1vgaPatchBrutusScriptFreeze },
10308 	{  true,    77, "white stag dagger throw animation glitch",    1, qfg1vgaSignatureWhiteStagDagger,     qfg1vgaPatchWhiteStagDagger },
10309 	{  true,    78, "mac: enable antwerp controls",                1, qfg1vgaSignatureMacAntwerpControls,  qfg1vgaPatchMacAntwerpControls },
10310 	{  true,    87, "drink water message",                         1, qfg1vgaSignatureDrinkWaterMessage,   qfg1vgaPatchDrinkWaterMessage },
10311 	{  true,    96, "funny room script bug fixed",                 1, qfg1vgaSignatureFunnyRoomFix,        qfg1vgaPatchFunnyRoomFix },
10312 	{  true,    96, "yorick door #2 lockup fixed",                 1, qfg1vgaSignatureYorickDoorTwoRect,   qfg1vgaPatchYorickDoorTwoRect },
10313 	{  true,   141, "mac: enter great hall",                       1, qfg1vgaSignatureMacEnterGreatHall,   qfg1vgaPatchMacEnterGreatHall },
10314 	{  true,   200, "mac: intro mouse-up fix",                     1, qfg1vgaSignatureMacLogoIntroSkip,    qfg1vgaPatchMacLogoIntroSkip },
10315 	{  true,   210, "cheetaur description fixed",                  1, qfg1vgaSignatureCheetaurDescription, qfg1vgaPatchCheetaurDescription },
10316 	{  true,   215, "fight event issue",                           1, qfg1vgaSignatureFightEvents,         qfg1vgaPatchFightEvents },
10317 	{  true,   216, "weapon master event issue",                   1, qfg1vgaSignatureFightEvents,         qfg1vgaPatchFightEvents },
10318 	{  true,   299, "speedtest",                                   1, qfg1vgaSignatureSpeedTest,           qfg1vgaPatchSpeedTest },
10319 	{  true,   331, "moving to crusher",                           1, qfg1vgaSignatureMoveToCrusher,       qfg1vgaPatchMoveToCrusher },
10320 	{  true,   331, "moving to crusher from card game",            1, qfg1vgaSignatureCrusherCardGame,     qfg1vgaPatchCrusherCardGame },
10321 	{  true,   332, "thieves' guild cashier fix",                  1, qfg1vgaSignatureThievesGuildCashier, qfg1vgaPatchThievesGuildCashier },
10322 	{  true,   340, "dagnabit inventory fix",                      1, qfg1vgaSignatureDagnabitInventory,   qfg1vgaPatchDagnabitInventory },
10323 	{  true,   340, "mac: dagnabit icon bar fix",                  1, qfg1vgaSignatureMacDagnabitIconBar,  qfg1vgaPatchMacDagnabitIconBar },
10324 	{  true,   603, "mac: logo mouse-up fix",                      1, qfg1vgaSignatureMacLogoIntroSkip,    qfg1vgaPatchMacLogoIntroSkip },
10325 	{  true,   814, "window text temp space",                      1, qfg1vgaSignatureTempSpace,           qfg1vgaPatchTempSpace },
10326 	{  true,   814, "dialog header offset",                        3, qfg1vgaSignatureDialogHeader,        qfg1vgaPatchDialogHeader },
10327 	{  true,   970, "antwerps wandering off-screen",               1, qfg1vgaSignatureAntwerpWander,       qfg1vgaPatchAntwerpWander },
10328 	SCI_SIGNATUREENTRY_TERMINATOR
10329 };
10330 
10331 // ===========================================================================
10332 
10333 // This is a very complicated bug.
10334 // When the player encounters an enemy in the desert while riding a saurus and
10335 //  later tries to get back on it by entering "ride", the game will not give
10336 //  control back to the player.
10337 //
10338 // This is caused by script mountSaurus getting triggered twice. Once by
10339 //  entering the command "ride" and then a second time by a proximity check.
10340 //
10341 // Both are calling mountSaurus::init() in script 20. This one disables
10342 //  controls. Then mountSaurus::changeState() from script 660 is triggered.
10343 //  Finally, mountSaurus::changeState(5) calls mountSaurus::dispose(), also in
10344 //  script 20, which re-enables controls.
10345 //
10346 // A fix is difficult to implement. The code in script 20 is generic and used
10347 //  by multiple objects
10348 //
10349 // An early attempt changed the responsible vars (global[102], global[161])
10350 //  during mountSaurus::changeState(5). This worked for controls, but
10351 //  mountSaurus::init changes a few selectors of ego as well, which won't get
10352 //  restored in that situation, which then messes up room changes and other
10353 //  things.
10354 //
10355 // Instead we change sheepScript::changeState(2) in script 665.
10356 //
10357 // Note: This could cause issues in case there is a cutscene, where ego is
10358 //  supposed to get onto the saurus using sheepScript.
10359 //
10360 // Applies to at least: English PC Floppy, English Amiga Floppy
10361 // Responsible method: mountSaurus::changeState(), mountSaurus::init(), mountSaurus::dispose()
10362 // Fixes bug: #5156
10363 static const uint16 qfg2SignatureSaurusFreeze[] = {
10364 	0x3c,                               // dup
10365 	0x35, 0x02,                         // ldi 2
10366 	SIG_MAGICDWORD,
10367 	0x1a,                               // eq?
10368 	0x30, SIG_UINT16(0x0043),           // bnt [ret]
10369 	0x76,                               // push0
10370 	SIG_ADDTOOFFSET(+61),               // skip to dispose code
10371 	0x39, SIG_SELECTOR8(dispose),       // pushi dispose
10372 	0x76,                               // push0
10373 	0x54, 0x04,                         // self 04
10374 	SIG_END
10375 };
10376 
10377 static const uint16 qfg2PatchSaurusFreeze[] = {
10378 	0x81, 0x66,                         // lag global[66h]
10379 	0x2e, PATCH_UINT16(0x0040),         // bt [to dispose code]
10380 	0x35, 0x00,                         // ldi 0 (waste 2 bytes)
10381 	PATCH_END
10382 };
10383 
10384 // The Jackalmen combat code has at least one serious issue.
10385 //
10386 // Jackalmen may attack in groups. This is handled by 2 globals.
10387 //  global[136h]: amount of Jackalmen still alive.
10388 //  global[137h]: amount of Jackalmen killed so far during combat.
10389 //
10390 // After combat has ended, global[137h] is subtracted from global[136h]. BUT
10391 // when the player manages to hit the last enemy AFTER defeating it during its
10392 // death animation (yes, that is possible - don't ask), the code is called a
10393 // second time. Subtracting global[137h] twice, which will make global[136h]
10394 // negative and will then create an inconsistent state. Some variables will
10395 // show that there is still an enemy, while others don't. The game will crash
10396 // when leaving the room. The original interpreter would show the infamous
10397 // "Oops, you did something we weren't expecting..."
10398 //
10399 // TODO: Check, if patch works for 1.000. That version surely has the same bug.
10400 // Applies to at least: English Floppy (1.102+1.105)
10401 // Responsible method: jackalMan::die (script 695)
10402 // Fixes bug: #10218
10403 static const uint16 qfg2SignatureOopsJackalMen[] = {
10404 	SIG_MAGICDWORD,
10405 	0x8b, 0x00,                         // lsl local[0]
10406 	0x35, 0x00,                         // ldi 0
10407 	0x22,                               // lt?
10408 	0x30, SIG_UINT16(0x000b),           // bnt [Jackalman death animation code]
10409 	0x38, SIG_ADDTOOFFSET(+2),          // pushi (die)
10410 	0x76,                               // push0
10411 	0x57, 0x66, 0x04,                   // super Monster, 4
10412 	0x48,                               // ret
10413 	0x32, SIG_UINT16(0x001a),           // jmp (seems to be a compiler bug)
10414 	// Jackalman death animation code
10415 	0x83, 0x00,                         // lal local[0]
10416 	0x18,                               // not
10417 	0x30, SIG_UINT16(0x0003),           // bnt [make next enemy walk in]
10418    SIG_END
10419 };
10420 
10421 static const uint16 qfg2PatchOopsJackalMen[] = {
10422 	0x80, PATCH_UINT16(0x0136),         // lag global[136h]
10423 	0x31, 0x0e,                         // bnt [skip everything] - requires 5 extra bytes
10424 	0x8b, 0x00,                         // lsl local[0]
10425 	0x35, 0x00,                         // ldi 0
10426 	0x22,                               // lt?
10427 	0x31, 0x08,                         // bnt [Jackalman death animation code] (save 1 byte)
10428 	0x38, PATCH_GETORIGINALUINT16(+9),  // pushi (die)
10429 	0x76,                               // push0
10430 	0x57, 0x66, 0x04,                   // super Monster, 4
10431 	0x48,                               // ret
10432 	// Jackalman death animation code
10433 	0x83, 0x00,                         // lal local[0]
10434 	0x18,                               // not
10435 	0x31, 0x03,                         // bnt [make next enemy walk in] (save 1 byte)
10436 	PATCH_END
10437 };
10438 
10439 // Script 944 in QFG2 contains the FileSelector system class, used in the
10440 // character import screen. This gets incorrectly called constantly, whenever
10441 // the user clicks on a button in order to refresh the file list. This was
10442 // probably done because it would be easier to refresh the list whenever the
10443 // user inserted a new floppy disk, or changed directory. The problem is that
10444 // the script has a bug, and it invalidates the text of the entries in the
10445 // list. This has a high probability of breaking, as the user could change the
10446 // list very quickly, or the garbage collector could kick in and remove the
10447 // deleted entries. We don't allow the user to change the directory, thus the
10448 // contents of the file list are constant, so we can avoid the constant file
10449 // and text entry refreshes whenever a button is pressed, and prevent possible
10450 // crashes because of these constant quick object reallocations.
10451 // Fixes bug: #5096
10452 static const uint16 qfg2SignatureImportDialog[] = {
10453 	0x63, SIG_MAGICDWORD, 0x20,         // pToa text
10454 	0x30, SIG_UINT16(0x000b),           // bnt [next state]
10455 	0x7a,                               // push2
10456 	0x39, 0x03,                         // pushi 03
10457 	0x36,                               // push
10458 	0x43, 0x72, 0x04,                   // callk Memory, 4
10459 	0x35, 0x00,                         // ldi 00
10460 	0x65, 0x20,                         // aTop text
10461 	SIG_END
10462 };
10463 
10464 static const uint16 qfg2PatchImportDialog[] = {
10465 	PATCH_ADDTOOFFSET(+5),
10466 	0x48,                               // ret
10467 	PATCH_END
10468 };
10469 
10470 // Quest For Glory 2 character import doesn't properly set the character type
10471 //  in versions 1.102 and below, which makes all imported characters a fighter.
10472 //
10473 // Sierra released an official patch. However the fix is really easy to
10474 //  implement on our side, so we also patch the flaw in here in case we find it.
10475 //
10476 // The version released on GOG is 1.102 without this patch applied, so us
10477 //  patching it is quite useful.
10478 //
10479 // Applies to at least: English Floppy
10480 // Responsible method: importHero::changeState
10481 // Fixes bug: inside versions 1.102 and below
10482 static const uint16 qfg2SignatureImportCharType[] = {
10483 	0x35, 0x04,                         // ldi 04
10484 	0x90, SIG_UINT16(0x023b),           // lagi global[23Bh]
10485 	0x02,                               // add
10486 	0x36,                               // push
10487 	0x35, 0x04,                         // ldi 04
10488 	0x08,                               // div
10489 	0x36,                               // push
10490 	0x35, 0x0d,                         // ldi 0D
10491 	0xb0, SIG_UINT16(0x023b),           // sagi global[023Bh]
10492 	0x8b, 0x1f,                         // lsl local[1Fh]
10493 	0x35, 0x05,                         // ldi 05
10494 	SIG_MAGICDWORD,
10495 	0xb0, SIG_UINT16(0x0150),           // sagi global[0150h]
10496 	0x8b, 0x02,                         // lsl local[02h]
10497 	SIG_END
10498 };
10499 
10500 static const uint16 qfg2PatchImportCharType[] = {
10501 	0x80, PATCH_UINT16(0x023f),         // lag global[23Fh] <-- patched to save 2 bytes
10502 	0x02,                               // add
10503 	0x36,                               // push
10504 	0x35, 0x04,                         // ldi 04
10505 	0x08,                               // div
10506 	0x36,                               // push
10507 	0xa8, PATCH_UINT16(0x0248),         // ssg global[0248h] <-- patched to save 2 bytes
10508 	0x8b, 0x1f,                         // lsl local[1Fh]
10509 	0xa8, PATCH_UINT16(0x0155),         // ssg global[0155h] <-- patched to save 2 bytes
10510 	// new code, directly from the official sierra patch file
10511 	0x83, 0x01,                         // lal local[01h]
10512 	0xa1, 0xbb,                         // sag global[BBh]
10513 	0xa1, 0x73,                         // sag global[73h]
10514 	PATCH_END
10515 };
10516 
10517 //          script, description,                                      signature                    patch
10518 static const SciScriptPatcherEntry qfg2Signatures[] = {
10519 	{  true,   665, "getting back on saurus freeze fix",           1, qfg2SignatureSaurusFreeze,   qfg2PatchSaurusFreeze },
10520 	{  true,   695, "Oops Jackalmen fix",                          1, qfg2SignatureOopsJackalMen,  qfg2PatchOopsJackalMen },
10521 	{  true,   805, "import character type fix",                   1, qfg2SignatureImportCharType, qfg2PatchImportCharType },
10522 	{  true,   944, "import dialog continuous calls",              1, qfg2SignatureImportDialog,   qfg2PatchImportDialog },
10523 	SCI_SIGNATUREENTRY_TERMINATOR
10524 };
10525 
10526 // ===========================================================================
10527 // Patch for the import screen in QFG3, same as the one for QFG2 above
10528 static const uint16 qfg3SignatureImportDialog[] = {
10529 	0x63, SIG_MAGICDWORD, 0x2a,         // pToa text
10530 	0x31, 0x0b,                         // bnt [next state]
10531 	0x7a,                               // push2
10532 	0x39, 0x03,                         // pushi 03
10533 	0x36,                               // push
10534 	0x43, 0x72, 0x04,                   // callk Memory, 4
10535 	0x35, 0x00,                         // ldi 00
10536 	0x65, 0x2a,                         // aTop text
10537 	SIG_END
10538 };
10539 
10540 static const uint16 qfg3PatchImportDialog[] = {
10541 	PATCH_ADDTOOFFSET(+4),
10542 	0x48,                               // ret
10543 	PATCH_END
10544 };
10545 
10546 // Patch for the Woo dialog option in Uhura's conversation.
10547 //
10548 // Problem: The Woo dialog option (0xffb5) is negative, and therefore
10549 //  treated as an option opening a submenu. This leads to uhuraTell::doChild
10550 //  being called, which calls hero::solvePuzzle and then proceeds with
10551 //  Teller::doChild to open the submenu. However, there is no actual submenu
10552 //  defined for option -75 since -75 does not show up in uhuraTell::keys.
10553 //  This will cause Teller::doChild to run out of bounds while scanning through
10554 //  uhuraTell::keys.
10555 //
10556 // Strategy: there is another conversation option in uhuraTell::doChild calling
10557 //  hero::solvePuzzle (0xfffc) which does a ret afterwards without going to
10558 //  Teller::doChild. We jump to this call of hero::solvePuzzle to get that same
10559 //  behaviour.
10560 //
10561 // Applies to at least: English, German, Italian, French, Spanish Floppy
10562 // Responsible method: uhuraTell::doChild
10563 // Fixes bug: #5172
10564 static const uint16 qfg3SignatureWooDialog[] = {
10565 	SIG_MAGICDWORD,
10566 	0x67, 0x12,                         // pTos 12 (query)
10567 	0x35, 0xb6,                         // ldi b6
10568 	0x1a,                               // eq?
10569 	0x2f, 0x05,                         // bt 05
10570 	0x67, 0x12,                         // pTos 12 (query)
10571 	0x35, 0x9b,                         // ldi 9b
10572 	0x1a,                               // eq?
10573 	0x31, 0x0c,                         // bnt 0c
10574 	0x38, SIG_SELECTOR16(solvePuzzle),  // pushi solvePuzzle (0297)
10575 	0x7a,                               // push2
10576 	0x38, SIG_UINT16(0x010c),           // pushi 010c
10577 	0x7a,                               // push2
10578 	0x81, 0x00,                         // lag global[0]
10579 	0x4a, 0x08,                         // send 08
10580 	0x67, 0x12,                         // pTos 12 (query)
10581 	0x35, 0xb5,                         // ldi b5
10582 	SIG_END
10583 };
10584 
10585 static const uint16 qfg3PatchWooDialog[] = {
10586 	PATCH_ADDTOOFFSET(+0x29),
10587 	0x33, 0x11,                         // jmp [to 0x6a2, the call to hero::solvePuzzle for 0xFFFC]
10588 	PATCH_END
10589 };
10590 
10591 // Alternative version, with uint16 offsets, for GOG release of QfG3.
10592 static const uint16 qfg3SignatureWooDialogAlt[] = {
10593 	SIG_MAGICDWORD,
10594 	0x67, 0x12,                         // pTos 12 (query)
10595 	0x35, 0xb6,                         // ldi b6
10596 	0x1a,                               // eq?
10597 	0x2e, SIG_UINT16(0x0005),           // bt 05
10598 	0x67, 0x12,                         // pTos 12 (query)
10599 	0x35, 0x9b,                         // ldi 9b
10600 	0x1a,                               // eq?
10601 	0x30, SIG_UINT16(0x000c),           // bnt 0c
10602 	0x38, SIG_SELECTOR16(solvePuzzle),  // pushi solvePuzzle (0297)
10603 	0x7a,                               // push2
10604 	0x38, SIG_UINT16(0x010c),           // pushi 010c
10605 	0x7a,                               // push2
10606 	0x81, 0x00,                         // lag global[0]
10607 	0x4a, 0x08,                         // send 08
10608 	0x67, 0x12,                         // pTos 12 (query)
10609 	0x35, 0xb5,                         // ldi b5
10610 	SIG_END
10611 };
10612 
10613 static const uint16 qfg3PatchWooDialogAlt[] = {
10614 	PATCH_ADDTOOFFSET(+44),
10615 	0x33, 0x12,                         // jmp [to 0x708, the call to hero::solvePuzzle for 0xFFFC]
10616 	PATCH_END
10617 };
10618 
10619 // When exporting characters at the end of Quest for Glory 3, the underlying
10620 //  code has issues with values above 9999.
10621 //  For further study: https://github.com/Blazingstix/QFGImporter/blob/master/QFGImporter/QFGImporter/QFG3.txt
10622 //
10623 // If a value is above 9999, parts or even the whole character file will get
10624 //  corrupted. We calculate the checksum and add extra code to lower such
10625 //  values to 9999.
10626 //
10627 // Applies to at least: English, French, German, Italian, Spanish floppy
10628 // Responsible method: saveHero::changeState
10629 // Fixes bug: #6807
10630 static const uint16 qfg3SignatureExportChar[] = {
10631 	0x35, SIG_ADDTOOFFSET(+1),          // ldi 00 / ldi 01 (2 loops, we patch both)
10632 	0xa5, 0x00,                         // sat temp[0] [contains index to data]
10633 	0x8d, 0x00,                         // lst temp[0]
10634 	SIG_MAGICDWORD,
10635 	0x35, 0x2c,                         // ldi 2c
10636 	0x22,                               // lt? (index above or equal to 2Ch (44d)?)
10637 	0x31, 0x23,                         // bnt [exit loop]
10638 	// from this point it's actually useless code, maybe a sci compiler bug
10639 	0x8d, 0x00,                         // lst temp[0]
10640 	0x35, 0x01,                         // ldi 01
10641 	0x02,                               // add
10642 	0x9b, 0x00,                         // lsli local[0] ---------- load local[0 + ACC] onto stack
10643 	0x8d, 0x00,                         // lst temp[0]
10644 	0x35, 0x01,                         // ldi 01
10645 	0x02,                               // add
10646 	0xb3, 0x00,                         // sali local[0] ---------- save stack to local[0 + ACC]
10647 	// end of useless code
10648 	0x8b, SIG_ADDTOOFFSET(+1),          // lsl local[36h/37h] ----- load local[36h/37h] onto stack
10649 	0x8d, 0x00,                         // lst temp[0]
10650 	0x35, 0x01,                         // ldi 01
10651 	0x02,                               // add
10652 	0x93, 0x00,                         // lali local[0] ---------- load local[0 + ACC] into ACC
10653 	0x02,                               // add -------------------- add ACC + stack and put into ACC
10654 	0xa3, SIG_ADDTOOFFSET(+1),          // sal local[36h/37h] ----- save ACC to local[36h/37h]
10655 	0x8d, 0x00,                         // lst temp[0] ------------ temp[0] to stack
10656 	0x35, 0x02,                         // ldi 02
10657 	0x02,                               // add -------------------- add 2 to stack
10658 	0xa5, 0x00,                         // sat temp[0] ------------ save ACC to temp[0]
10659 	0x33, 0xd6,                         // jmp [loop]
10660 	SIG_END
10661 };
10662 
10663 static const uint16 qfg3PatchExportChar[] = {
10664 	PATCH_ADDTOOFFSET(+11),
10665 	0x85, 0x00,                         // lat temp[0]
10666 	0x9b, 0x01,                         // lsli local[0] + 1 ------ load local[ ACC + 1] onto stack
10667 	0x3c,                               // dup
10668 	0x34, PATCH_UINT16(0x2710),         // ldi 2710h (10000d)
10669 	0x2c,                               // ult? ------------------- is value smaller than 10000?
10670 	0x2f, 0x0a,                         // bt [jump over]
10671 	0x3a,                               // toss
10672 	0x38, PATCH_UINT16(0x270f),         // pushi 270fh (9999d)
10673 	0x3c,                               // dup
10674 	0x85, 0x00,                         // lat temp[0]
10675 	0xba, PATCH_UINT16(0x0001),         // ssli local[0] + 1 ------ save stack to local[ACC + 1] (UINT16 to waste 1 byte)
10676 	// jump offset
10677 	0x83, PATCH_GETORIGINALBYTE(+26),   // lal local[37h/36h] ----- load local[37h/36h] into ACC
10678 	0x02,                               // add -------------------- add local[37h/36h] + data value
10679 	PATCH_END
10680 };
10681 
10682 // Quest for Glory 3 doesn't properly import the character type of QFG1
10683 //  character files. This issue was never addressed. It's caused by Sierra
10684 //  reading data directly from the local area, which is only set by QFG2
10685 //  import data, instead of reading the properly set global variable.
10686 //
10687 // We fix it, by also directly setting the local variable.
10688 //
10689 // Applies to at least: English, French, German, Italian, Spanish floppy
10690 // Responsible method: importHero::changeState(4)
10691 static const uint16 qfg3SignatureImportQfG1Char[] = {
10692 	SIG_MAGICDWORD,
10693 	0x82, SIG_UINT16(0x0238),           // lal local[0x0238]
10694 	0xa0, SIG_UINT16(0x016a),           // sag global[0x016a]
10695 	0xa1, 0x7d,                         // sag global[0x7d]
10696 	0x35, 0x01,                         // ldi 01
10697 	0x99, 0xfb,                         // lsgi global[0xfb]
10698 	SIG_END
10699 };
10700 
10701 static const uint16 qfg3PatchImportQfG1Char[] = {
10702 	PATCH_ADDTOOFFSET(+8),
10703 	0xa3, 0x01,                         // sal local[1]
10704 	0x89, 0xfc,                         // lsg global[0xfc] (save 2 bytes vs global[0xfb + 1])
10705 	PATCH_END
10706 };
10707 
10708 // The chief in his hut (room 640) is not drawn using the correct priority,
10709 //  which results in a graphical glitch. This is a game bug and also happens
10710 //  in Sierra's SCI. We adjust priority accordingly to fix it.
10711 //
10712 // Applies to at least: English, French, German, Italian, Spanish floppy
10713 // Responsible method: heap in script 640
10714 // Fixes bug: #5173
10715 static const uint16 qfg3SignatureChiefPriority[] = {
10716 	SIG_MAGICDWORD,
10717 	SIG_UINT16(0x0002),                 // yStep     0x0002
10718 	SIG_UINT16(0x0281),                 // view      0x0281
10719 	SIG_UINT16(0x0000),                 // loop      0x0000
10720 	SIG_UINT16(0x0000),                 // cel       0x0000
10721 	SIG_UINT16(0x0000),                 // priority  0x0000
10722 	SIG_UINT16(0x0000),                 // underbits 0x0000
10723 	SIG_UINT16(0x1000),                 // signal    0x1000
10724 	SIG_END
10725 };
10726 
10727 static const uint16 qfg3PatchChiefPriority[] = {
10728 	PATCH_ADDTOOFFSET(+8),
10729 	PATCH_UINT16(0x000a),               // priority  0x000A (10d)
10730 	PATCH_ADDTOOFFSET(+2),
10731 	PATCH_UINT16(0x1010),               // signal    0x1010 (set fixed priority flag)
10732 	PATCH_END
10733 };
10734 
10735 // There are 3 points that can't be achieved in the game. They should've been
10736 // awarded for telling Rakeesh and Kreesha (room 285) about the Simabni
10737 // initiation.
10738 // However the array of posibble messages the hero can tell in that room
10739 // (local[156]) is missing the "Tell about Initiation" message (#31) which
10740 // awards these points.
10741 // This patch adds the message to that array, thus allowing the hero to tell
10742 // that message (after completing the initiation) and gain the 3 points.
10743 // A side effect of increasing the local[156] array is that the next local
10744 // array is shifted and shrinks in size from 4 words to 3. The patch changes
10745 // the 2 locations in the script that reference that array, to point to the new
10746 // location (local[$aa] --> local[$ab]). It is safe to shrink the 2nd array to
10747 // 3 words because only the first element in it is ever used.
10748 //
10749 // Note: You have to re-enter the room in case a saved game was loaded from a
10750 // previous version of ScummVM and that saved game was made inside that room.
10751 //
10752 // Applies to: English, French, German, Italian, Spanish and the GOG release.
10753 // Responsible method: heap in script 285
10754 // Fixes bug: #7086
10755 static const uint16 qfg3SignatureMissingPoints1[] = {
10756 	// local[$9c] = [0 -41 -76 1 -30 -77 -33 -34 -35 -36 -37 -42 -80 999]
10757 	// local[$aa] = [0 0 0 0]
10758 	SIG_UINT16(0x0000),                 //   0 START MARKER
10759 	SIG_MAGICDWORD,
10760 	SIG_UINT16(0xffd7),                 // -41 "Greet"
10761 	SIG_UINT16(0xffb4),                 // -76 "Say Good-bye"
10762 	SIG_UINT16(0x0001),                 //   1 "Tell about Tarna"
10763 	SIG_UINT16(0xffe2),                 // -30 "Tell about Simani"
10764 	SIG_UINT16(0xffb3),                 // -77 "Tell about Prisoner"
10765 	SIG_UINT16(0xffdf),                 // -33 "Dispelled Leopard Lady"
10766 	SIG_UINT16(0xffde),                 // -34 "Tell about Leopard Lady"
10767 	SIG_UINT16(0xffdd),                 // -35 "Tell about Leopard Lady"
10768 	SIG_UINT16(0xffdc),                 // -36 "Tell about Leopard Lady"
10769 	SIG_UINT16(0xffdb),                 // -37 "Tell about Village"
10770 	SIG_UINT16(0xffd6),                 // -42 "Greet"
10771 	SIG_UINT16(0xffb0),                 // -80 "Say Good-bye"
10772 	SIG_UINT16(0x03e7),                 // 999 END MARKER
10773 	SIG_ADDTOOFFSET(+2),                // local[$aa][0]
10774 	SIG_END
10775 };
10776 
10777 static const uint16 qfg3PatchMissingPoints1[] = {
10778 	PATCH_ADDTOOFFSET(+14),
10779 	PATCH_UINT16(0xffe1),               // -31 "Tell about Initiation"
10780 	PATCH_UINT16(0xffde),               // -34 "Tell about Leopard Lady"
10781 	PATCH_UINT16(0xffdd),               // -35 "Tell about Leopard Lady"
10782 	PATCH_UINT16(0xffdc),               // -36 "Tell about Leopard Lady"
10783 	PATCH_UINT16(0xffdb),               // -37 "Tell about Village"
10784 	PATCH_UINT16(0xffd6),               // -42 "Greet"
10785 	PATCH_UINT16(0xffb0),               // -80 "Say Good-bye"
10786 	PATCH_UINT16(0x03e7),               // 999 END MARKER
10787 	PATCH_GETORIGINALUINT16(+28),       // local[$aa][0]
10788 	PATCH_END
10789 };
10790 
10791 static const uint16 qfg3SignatureMissingPoints2a[] = {
10792 	SIG_MAGICDWORD,
10793 	0x35, 0x00,                         // ldi 0
10794 	0xb3, 0xaa,                         // sali local[$aa]
10795 	SIG_END
10796 };
10797 
10798 static const uint16 qfg3SignatureMissingPoints2b[] = {
10799 	SIG_MAGICDWORD,
10800 	0x36,                               // push
10801 	0x5b, 0x02, 0xaa,                   // lea local[$aa]
10802 	SIG_END
10803 };
10804 
10805 static const uint16 qfg3PatchMissingPoints2[] = {
10806 	PATCH_ADDTOOFFSET(+3),
10807 	0xab,                               // local[$ab] (replace local[$aa])
10808 	PATCH_END
10809 };
10810 
10811 // Partly WORKAROUND:
10812 // During combat, the game is not properly throttled. That's because the game uses
10813 // an inner loop for combat and does not iterate through the main loop.
10814 // It also doesn't call kGameIsRestarting. This may get fixed properly at some point
10815 // by rewriting the speed throttler.
10816 //
10817 // Additionally Sierra set the cycle speed of the hero to 0. Which explains
10818 // why the actions of the hero are so incredibly fast. This issue also happened
10819 // in the original interpreter, when the computer was too powerful.
10820 //
10821 // Applies to at least: English, French, German, Italian, Spanish PC floppy
10822 // Responsible method: combatControls::dispatchEvent (script 550) + WarriorObj in heap
10823 // Fixes bug: #6247
10824 static const uint16 qfg3SignatureCombatSpeedThrottling1[] = {
10825 	0x3f, 0x03,                         // link 3d (these temp vars are never used)
10826 	0x76,                               // push0
10827 	0x43, 0x42, 0x00,                   // callk GetTime, 0d
10828 	0xa1, 0x58,                         // sag global[88]
10829 	0x36,                               // push
10830 	0x83, 0x01,                         // lal local[1]
10831 	SIG_ADDTOOFFSET(+3),                // ...
10832 	SIG_MAGICDWORD,
10833 	0x89, 0xd2,                         // lsg global[210]
10834 	0x35, 0x00,                         // ldi 0
10835 	0x1e,                               // gt?
10836 	SIG_ADDTOOFFSET(+6),                // ...
10837 	0xa3, 0x01,                         // sal local[1]
10838 	SIG_END
10839 };
10840 
10841 static const uint16 qfg3PatchCombatSpeedThrottling1[] = {
10842 	0x76,                               // push0  (no link, freed +2 bytes)
10843 	0x43, 0x42, 0x00,                   // callk GetTime, 0d
10844 	0xa1, 0x58,                         // sag global[88] (no push, leave time in acc)
10845 	0x8b, 0x01,                         // lsl local[1] (stack up the local instead, freed +1 byte)
10846 	0x1c,                               // ne?
10847 	0x31, 0x0c,                         // bnt 12d [after sal]
10848                                         //
10849 	0x81, 0xd2,                         // lag global[210] (load into acc instead of stack)
10850 	0x76,                               // push0 (push0 instead of ldi 0, freed +1 byte)
10851 	0x22,                               // lt? (flip the comparison)
10852 	0x31, 0x06,                         // bnt 6d [after sal]
10853                                         //
10854 	0xe1, 0xd2,                         // -ag global[210]
10855 	0x81, 0x58,                         // lag global[88]
10856 	0xa3, 0x01,                         // sal local[1]
10857 
10858 	0x76,                               // push0 (0 call args)
10859 	0x43, 0x2c, 0x00,                   // callk GameIsRestarting, 0d (add this to trigger our speed throttler)
10860 	PATCH_END
10861 };
10862 
10863 static const uint16 qfg3SignatureCombatSpeedThrottling2[] = {
10864 	SIG_MAGICDWORD,
10865 	SIG_UINT16(12),                     // priority 12
10866 	SIG_UINT16(0x0000),                 // underbits 0
10867 	SIG_UINT16(0x4010),                 // signal 4010h
10868 	SIG_ADDTOOFFSET(+18),
10869 	SIG_UINT16(0x0000),                 // scaleSignal 0
10870 	SIG_UINT16(128),                    // scaleX 128
10871 	SIG_UINT16(128),                    // scaleY 128
10872 	SIG_UINT16(128),                    // maxScale 128
10873 	SIG_UINT16(0x0000),                 // cycleSpeed 0
10874 	SIG_END
10875 };
10876 
10877 static const uint16 qfg3PatchCombatSpeedThrottling2[] = {
10878 	PATCH_ADDTOOFFSET(+32),
10879 	PATCH_UINT16(0x0005),               // set cycleSpeed to 5
10880 	PATCH_END
10881 };
10882 
10883 // In room #750, when the hero enters from the top east path (room #755), it
10884 // could go out of the contained-access polygon bounds, and be able to travel
10885 // freely in the room.
10886 // The reason is that the cutoff y value (42) that determines whether the hero
10887 // enters from the top or bottom path is inaccurate: it's possible to enter the
10888 // top path from as low as y=45.
10889 // This patch changes the cutoff to be 50 which should be low enough.
10890 // It also changes the position in which the hero enters from the top east path
10891 // as the current location is hidden behind the tree.
10892 //
10893 // Applies to: English, French, German, Italian, Spanish and the GOG release.
10894 // Responsible method: enterEast::changeState (script 750)
10895 // Fixes bug: #6693
10896 static const uint16 qfg3SignatureRoom750Bounds1[] = {
10897 	// (if (< (ego y?) 42)
10898 	0x76,                               // push0 (y)
10899 	0x76,                               // push0
10900 	0x81, 0x00,                         // lag global[0] (ego)
10901 	0x4a, 0x04,                         // send 4
10902 	SIG_MAGICDWORD,
10903 	0x36,                               // push
10904 	0x35,   42,                         // ldi 42 (if ego.y < 42)
10905 	0x22,                               // lt?
10906 	SIG_END
10907 };
10908 
10909 static const uint16 qfg3PatchRoom750Bounds1[] = {
10910 	// (if (< (ego y?) 50)
10911 	PATCH_ADDTOOFFSET(+8),
10912 	  50,                               // 50 (replace 42)
10913 	PATCH_END
10914 };
10915 
10916 static const uint16 qfg3SignatureRoom750Bounds2[] = {
10917 	// (ego x: 294 y: 39)
10918 	0x78,                               // push1 (x)
10919 	0x78,                               // push1
10920 	0x38, SIG_UINT16(294),              // pushi 294
10921 	0x76,                               // push0 (y)
10922 	0x78,                               // push1
10923 	SIG_MAGICDWORD,
10924 	0x39, 0x1d,                         // pushi 29
10925 	0x81, 0x00,                         // lag global[0] (ego)
10926 	0x4a, 0x0c,                         // send 12
10927 	SIG_END
10928 };
10929 
10930 static const uint16 qfg3PatchRoom750Bounds2[] = {
10931 	// (ego x: 320 y: 39)
10932 	PATCH_ADDTOOFFSET(+3),
10933 	PATCH_UINT16(320),                  // 320 (replace 294)
10934 	PATCH_ADDTOOFFSET(+3),
10935 	  39,                               // 39 (replace 29)
10936 	PATCH_END
10937 };
10938 
10939 static const uint16 qfg3SignatureRoom750Bounds3[] = {
10940 	// (ego setMotion: MoveTo 282 29 self)
10941 	0x38, SIG_SELECTOR16(setMotion),    // pushi setMotion (0x133)
10942 	0x39, 0x04,                         // pushi 4
10943 	0x51, SIG_ADDTOOFFSET(+1),          // class MoveTo
10944 	0x36,                               // push
10945 	0x38, SIG_UINT16(282),              // pushi 282
10946 	SIG_MAGICDWORD,
10947 	0x39,   29,                         // pushi 29
10948 	0x7c,                               // pushSelf
10949 	0x81, 0x00,                         // lag global[0] (ego)
10950 	0x4a, 0x0c,                         // send 12
10951 	SIG_END
10952 };
10953 
10954 static const uint16 qfg3PatchRoom750Bounds3[] = {
10955 	// (ego setMotion: MoveTo 309 35 self)
10956 	PATCH_ADDTOOFFSET(+9),
10957 	PATCH_UINT16(309),                  // 309 (replace 282)
10958 	PATCH_ADDTOOFFSET(+1),
10959 	  35,                               // 35 (replace 29)
10960 	PATCH_END
10961 };
10962 
10963 // When putting the last instance of an item in the chest in room 310 or 430,
10964 //  holding the enter key causes a message to be sent to a non-object.
10965 //
10966 // This bug is similar to the Dag-Nab-It bug in QFG1VGA Mac. A script tries to
10967 //  clear the inventory cursor by setting mainIconBar:curInvIcon to zero without
10968 //  updating curIcon. This briefly places the icon bar in an illegal state which
10969 //  causes mainIconBar:handleEvent to error when enter is pressed.
10970 //
10971 // We fix this by setting mainIconBar:curIcon to theWalkIcon to prevent the
10972 //  illegal state where curInvIcon is zero while curIcon equals useIconItem.
10973 //  useCode:init has two similar code paths with this bug.
10974 //
10975 // Applies to: All versions
10976 // Responsible method: useCode:init
10977 // Fixes bug: #11196
10978 static const uint16 qfg3SignatureChestIconBar[] = {
10979 	0x38, SIG_SELECTOR16(say),          // pushi say [ "You put it in the chest." ]
10980 	0x39, 0x06,                         // pushi 06
10981 	0x78,                               // push1
10982 	0x39, 0x06,                         // pushi 06
10983 	0x39, 0x04,                         // pushi 04
10984 	0x78,                               // push1
10985 	0x76,                               // push0
10986 	0x39, 0x1d,                         // pushi 1d
10987 	0x81, 0x5b,                         // lag 5b
10988 	0x4a, 0x10,                         // send 10 [ qg3Messager say: 1 6 4 1 0 29 ]
10989 	0x38, SIG_SELECTOR16(curInvIcon),   // pushi curInvIcon
10990 	0x78,                               // push1
10991 	0x76,                               // push0
10992 	0x81, 0x45,                         // lag 45
10993 	SIG_ADDTOOFFSET(+0x8b),
10994 	0x38, SIG_SELECTOR16(curInvIcon),   // pushi curInvIcon
10995 	0x78,                               // push1
10996 	0x76,                               // push0
10997 	0x81, 0x45,                         // lag 45
10998 	0x4a, SIG_MAGICDWORD, 0x06,         // send 06 [ mainIconBar curInvIcon: 0 ]
10999 	0x38, SIG_SELECTOR16(say),          // pushi say [ "You put it in the chest." ]
11000 	SIG_END
11001 };
11002 
11003 static const uint16 qfg3PatchChestIconBar[] = {
11004 	0x39, PATCH_SELECTOR8(at),          // pushi at
11005 	0x78,                               // push1
11006 	0x78,                               // push1
11007 	0x81, 0x45,                         // lag 45
11008 	0x4a, 0x06,                         // send 06 [ mainIconBar at: 1 ]
11009 	0x38, PATCH_SELECTOR16(curInvIcon), // pushi curInvIcon
11010 	0x78,                               // push1
11011 	0x76,                               // push0
11012 	0x38, PATCH_SELECTOR16(curIcon),    // pushi curIcon
11013 	0x78,                               // push1
11014 	0x36,                               // push
11015 	0x81, 0x45,                         // lag 45
11016 	0x4a, 0x0c,                         // send 0c [ mainIconBar curInvIcon: 0 curIcon: theWalkIcon ]
11017 	0x32, PATCH_UINT16(0x0094),         // jmp 0094 [ "You put it in the chest." ]
11018 	PATCH_ADDTOOFFSET(+0x8b),
11019 	0x32, PATCH_UINT16(0xff59),         // jmp ff59 [ mainIconBar curInvIcon: 0 curIcon: theWalkIcon ]
11020 	PATCH_END
11021 };
11022 
11023 // Entering the jungle close to the Leopardman village and then exiting with
11024 //  Johari causes the game to display corrupt message boxes and divide by zero.
11025 //  Manu's script has the same bugs which could potentially be triggered.
11026 //
11027 // The script fromJungle in room 170 sets local0 to the result of calculations
11028 //  based on ego's initial distance to his destination. The scripts walkJohari
11029 //  and walkManu divide by local0. fromJungle checks to see if it's set local0
11030 //  to zero, but instead of addressing this, it displays a series of messages
11031 //  all at once and corrupts the screen before continuing on to the error.
11032 //
11033 // We fix this by not setting local0 to zero and disabling the broken messages.
11034 //  The message code is unnecessary since later in the same game cycle a local
11035 //  procedure displays the messages correctly. This is similar to the fix in the
11036 //  QFG3 Unofficial Update: https://github.com/AshLancer/QFG3-Fan-Patch
11037 //
11038 // Applies to: All versions
11039 // Responsible method: fromJungle:changeState(0)
11040 // Fixes bug: #11216
11041 static const uint16 qfg3SignatureJohariManuMapBugs[] = {
11042 	SIG_MAGICDWORD,
11043 	0xa3, 0x00,                         // sal 00 [ local0 = result ]
11044 	0x36,                               // push
11045 	0x35, 0x01,                         // ldi 01
11046 	0x22,                               // lt? [ local0 < 1 ]
11047 	0x31,                               // bnt [ skip broken messages ]
11048 	SIG_END
11049 };
11050 
11051 static const uint16 qfg3PatchJohariManuMapBugs[] = {
11052 	0x2f, 0x02,                         // bt 02
11053 	0x35, 0x01,                         // ldi 01
11054 	0xa3, 0x00,                         // sal 00 [ local0 = result ? result : 1 ]
11055 	0x33,                               // jmp    [ always skip broken messages  ]
11056 	PATCH_END
11057 };
11058 
11059 //          script, description,                                      signature                    patch
11060 static const SciScriptPatcherEntry qfg3Signatures[] = {
11061 	{  true,   944, "import dialog continuous calls",                     1, qfg3SignatureImportDialog,           qfg3PatchImportDialog },
11062 	{  true,   440, "dialog crash when asking about Woo",                 1, qfg3SignatureWooDialog,              qfg3PatchWooDialog },
11063 	{  true,   440, "dialog crash when asking about Woo",                 1, qfg3SignatureWooDialogAlt,           qfg3PatchWooDialogAlt },
11064 	{  true,    52, "export character save bug",                          2, qfg3SignatureExportChar,             qfg3PatchExportChar },
11065 	{  true,    54, "import character from QfG1 bug",                     1, qfg3SignatureImportQfG1Char,         qfg3PatchImportQfG1Char },
11066 	{  true,   640, "chief in hut priority fix",                          1, qfg3SignatureChiefPriority,          qfg3PatchChiefPriority },
11067 	{  true,   285, "missing points for telling about initiation heap",   1, qfg3SignatureMissingPoints1,         qfg3PatchMissingPoints1 },
11068 	{  true,   285, "missing points for telling about initiation script", 1, qfg3SignatureMissingPoints2a,	      qfg3PatchMissingPoints2 },
11069 	{  true,   285, "missing points for telling about initiation script", 1, qfg3SignatureMissingPoints2b,        qfg3PatchMissingPoints2 },
11070 	{  true,   550, "combat speed throttling script",                     1, qfg3SignatureCombatSpeedThrottling1, qfg3PatchCombatSpeedThrottling1 },
11071 	{  true,   550, "combat speed throttling heap",                       1, qfg3SignatureCombatSpeedThrottling2, qfg3PatchCombatSpeedThrottling2 },
11072 	{  true,   750, "hero goes out of bounds in room 750",                2, qfg3SignatureRoom750Bounds1,         qfg3PatchRoom750Bounds1 },
11073 	{  true,   750, "hero goes out of bounds in room 750",                2, qfg3SignatureRoom750Bounds2,         qfg3PatchRoom750Bounds2 },
11074 	{  true,   750, "hero goes out of bounds in room 750",                2, qfg3SignatureRoom750Bounds3,         qfg3PatchRoom750Bounds3 },
11075 	{  true,    29, "icon bar crash when using chest",                    1, qfg3SignatureChestIconBar,           qfg3PatchChestIconBar },
11076 	{  true,   170, "johari/manu map crash and message bugs",             2, qfg3SignatureJohariManuMapBugs,      qfg3PatchJohariManuMapBugs },
11077 	SCI_SIGNATUREENTRY_TERMINATOR
11078 };
11079 
11080 #ifdef ENABLE_SCI32
11081 #pragma mark -
11082 #pragma mark Quest for Glory 4
11083 
11084 // ===========================================================================
11085 // Cranium's TRAP screen in room 380 incorrectly creates an int array for
11086 // string data.
11087 //
11088 // Applies to at least: English CD, English floppy, German floppy
11089 // Responsible method: trap::init() in script 83
11090 // Fixes bug: #10766
11091 static const uint16 qfg4TrapArrayTypeSignature[] = {
11092 	0x38, SIG_SELECTOR16(new),          // pushi new
11093 	0x78,                               // push1
11094 	SIG_MAGICDWORD,
11095 	0x38, SIG_UINT16(0x0080),           // pushi 128d (array size)
11096 	0x51, SIG_ADDTOOFFSET(+1),          // class IntArray (CD=0x0b. floppy=0x0a)
11097 	0x4a, SIG_UINT16(0x0006),           // send 6
11098 	SIG_END
11099 };
11100 
11101 static const uint16 qfg4TrapArrayTypePatch[] = {
11102 	PATCH_ADDTOOFFSET(+4),
11103 	0x38, PATCH_UINT16(0x0100),               // pushi 256d (array size)
11104 	0x51, PATCH_GETORIGINALBYTEADJUST(8, +2), // class ByteArray (CD=0x0d, floppy=0x0c)
11105 	PATCH_END
11106 };
11107 
11108 // QFG4 has custom video benchmarking code inside a subroutine, which is called
11109 // by 'glryInit::init', that needs to be disabled. See: sci2BenchmarkSignature.
11110 //
11111 // Applies to at least: English CD, English floppy, German Floppy
11112 // Responsible method: localproc_0010() in script 1
11113 static const uint16 qfg4BenchmarkSignature[] = {
11114 	0x38, SIG_SELECTOR16(new),          // pushi new
11115 	0x76,                               // push0
11116 	0x51, SIG_ADDTOOFFSET(+1),          // class View
11117 	0x4a, SIG_UINT16(0x0004),           // send 4
11118 	0xa5, 0x00,                         // sat temp[0]
11119 	0x39, SIG_SELECTOR8(view),          // pushi view
11120 	SIG_MAGICDWORD,
11121 	0x78,                               // push1
11122 	0x38, SIG_UINT16(0x270f),           // pushi $270f (9999)
11123 	SIG_END
11124 };
11125 
11126 static const uint16 qfg4BenchmarkPatch[] = {
11127 	0x35, 0x01,                         // ldi 1
11128 	0xa1, 0xbf,                         // sag global[191]
11129 	0x48,                               // ret
11130 	PATCH_END
11131 };
11132 
11133 // WORKAROUND: Script needed, because of differences in our pathfinding
11134 // algorithm.
11135 // At the inn, there is a path that goes off screen. In our pathfinding
11136 // algorithm, we move all the pathfinding points so that they are within
11137 // the visible area. However, two points of the path are outside the
11138 // screen, so moving them will place them both on top of each other,
11139 // thus creating an impossible pathfinding area. This makes the
11140 // pathfinding algorithm ignore the walkable area when the hero moves
11141 // up the ladder to his room. We therefore move one of the points
11142 // slightly, so that it is already within the visible screen, so that
11143 // the walkable polygon is valid, and the pathfinding algorithm can
11144 // work properly.
11145 //
11146 // Applies to at least: English CD, English floppy, German floppy
11147 // Responsible method: rm320::init() in script 320
11148 // Fixes bug: #10693
11149 static const uint16 qfg4InnPathfindingSignature[] = {
11150 	SIG_MAGICDWORD,
11151 	0x38, SIG_UINT16(0x0154),  // pushi x = 340
11152 	0x39, 0x77,                // pushi y = 119
11153 	0x38, SIG_UINT16(0x0114),  // pushi x = 276
11154 	0x39, 0x31,                // pushi y = 49
11155 	0x38, SIG_UINT16(0x00fc),  // pushi x = 252
11156 	0x39, 0x30,                // pushi y = 48
11157 	0x38, SIG_UINT16(0x00a5),  // pushi x = 165
11158 	0x39, 0x55,                // pushi y = 85
11159 	0x38, SIG_UINT16(0x00c0),  // pushi x = 192
11160 	0x39, 0x55,                // pushi y = 85
11161 	0x38, SIG_UINT16(0x010b),  // pushi x = 267
11162 	0x39, 0x34,                // pushi y = 52
11163 	0x38, SIG_UINT16(0x0144),  // pushi x = 324
11164 	0x39, 0x77,                // pushi y = 119
11165 	SIG_END
11166 };
11167 
11168 static const uint16 qfg4InnPathfindingPatch[] = {
11169 	PATCH_ADDTOOFFSET(+30),
11170 	0x38, PATCH_UINT16(0x013f), // pushi x = 319 (was 324)
11171 	0x39, 0x77,                 // pushi y = 119
11172 	PATCH_END
11173 };
11174 
11175 // When autosave is enabled, Glory::save() (script 0) deletes savegame files in
11176 // a loop, while disk space is insufficient for a new save, or while there are
11177 // 20+ saves. Since ScummVM handles slots differently and allows far more
11178 // slots, this deletes all but the most recent 19 manual saves, merely by
11179 // walking from room to room!
11180 //
11181 // Ironically, kGetSaveFiles() (kfile.cpp) and the debugger's 'list_files'
11182 // command rely on listSavegames() (file.cpp), which specifically omits the
11183 // autosave slot, so the script will only ever delete manual saves. And the
11184 // space check doesn't take into account the reduced demand when overwriting an
11185 // existing autosave.
11186 //
11187 // No good can come of this loop. So we skip it entirely. If the disk truly is
11188 // out of space, a message box will complain, and the player can delete saves
11189 // voluntarily.
11190 //
11191 // Note: Glory::save() contains another space freeing loop, but it might be
11192 // unreachable.
11193 //
11194 // Applies to at least: English CD, English floppy, German floppy
11195 // Responsible method: Glory::save() in script 0
11196 // Fixes bug: #10758
11197 static const uint16 qfg4AutosaveSignature[] = {
11198 	0x30, SIG_ADDTOOFFSET(+2),          // bnt ?? [end the loop]
11199 	0x78,                               // push1 (1 call arg)
11200                                         //
11201 	0x39, SIG_SELECTOR8(data),          // pushi data
11202 	0x76,                               // push0
11203 	SIG_ADDTOOFFSET(+2),                // (CD="lag global[29]", floppy="lat temp[6]")
11204 	0x4a, SIG_UINT16(0x0004),           // send 4d
11205 	0x36,                               // push
11206 	SIG_MAGICDWORD,
11207 	0x43, 0x3f, SIG_UINT16(0x0002),     // callk CheckFreeSpace, 2d
11208 	0x18,                               // not
11209 	0x2f, 0x05,                         // bt 05 [skip other OR condition]
11210 	0x8d, 0x09,                         // lst temp[9] (savegame file count)
11211 	0x35, 0x14,                         // ldi 20d
11212 	0x20,                               // ge?
11213 	SIG_END
11214 };
11215 
11216 static const uint16 qfg4AutosavePatch[] = {
11217 	0x32, // ...                        // jmp [end the loop]
11218 	PATCH_END
11219 };
11220 
11221 // The swamp areas have typos where a Grooper object is passed to
11222 // View::setLoop(), a method which expects an integer to store in the "loop"
11223 // property. This leads to arithmetic crashes later. We change it to
11224 // Actor::setLooper().
11225 //
11226 // Applies to at least: English CD, English floppy, German floppy
11227 // Responsible method:
11228 //                     Script 440
11229 //                         sToWater::changeState(3)
11230 //                     Script 530, 535
11231 //                         sGlideFromTuff::changeState(1)
11232 //                         sGoGlide::changeState(2)
11233 //                         sFromWest::changeState(0)
11234 //                         sFromSouth::changeState(0)
11235 //                     Script 541, 542, 543
11236 //                         sGlideFromTuff::changeState(1)
11237 //                         sFromEast::changeState(0)
11238 //                         sFromNorth::changeState(0)
11239 //                         sFromWest::changeState(0)
11240 //                         sFromSouth::changeState(0)
11241 //                     Script 545
11242 //                         sCombatEnter::changeState(0)
11243 //                         sGlideFromTuff::changeState(2)
11244 //                         sFromNorth::changeState(0)
11245 //                         sFromEast::changeState(0)
11246 //                         sFromWest::changeState(0)
11247 // Fixes bug: #10777
11248 static const uint16 qfg4SetLooperSignature1[] = {
11249 	SIG_MAGICDWORD,
11250 	0x38, SIG_SELECTOR16(setLoop),      // pushi setLoop
11251 	0x78,                               // push1
11252 	0x51, 0x5a,                         // class Grooper
11253 	SIG_END
11254 };
11255 
11256 static const uint16 qfg4SetLooperPatch1[] = {
11257 	0x38, PATCH_SELECTOR16(setLooper),  // pushi setLooper
11258 	PATCH_END
11259 };
11260 
11261 // As above, except it's an exported subclass of Grooper: stopGroop.
11262 //
11263 // Applies to at least: English CD, English floppy, German floppy
11264 // Responsible method: sJumpWater::changeState(3), sToJump::changeState(2) in script 10
11265 // Fixes bug: #10777
11266 static const uint16 qfg4SetLooperSignature2[] = {
11267 	SIG_MAGICDWORD,
11268 	0x38, SIG_SELECTOR16(setLoop),      // pushi setLoop
11269 	0x78,                               // push1
11270 	0x7a,                               // push2 (2 call args)
11271 	0x39, 0x1c,                         // pushi 28d
11272 	0x78,                               // push1
11273 	0x43, 0x02, SIG_UINT16(0x0004),     // callk ScriptID, 4d (ScriptID 28 1)
11274 	SIG_END
11275 };
11276 
11277 static const uint16 qfg4SetLooperPatch2[] = {
11278 	0x38, PATCH_SELECTOR16(setLooper),  // pushi setLooper
11279 	PATCH_END
11280 };
11281 
11282 // The panel showing time of day gets stuck displaying consecutive moonrises.
11283 // Despite time actually advancing, the sun will never rise again because
11284 // local[5] is set to 1 for night UI and never resets until hero moves to
11285 // another room.
11286 //
11287 // temp[0] is not used in this method. Thus we can safely ignore and reuse the
11288 // code block that manipulates it in order to fix this bug.
11289 //
11290 // Applies to at least: English CD, English floppy, German floppy
11291 // Responsible method: showTime::init() in script 7
11292 // Fixes bug: #10775
11293 static const uint16 qfg4MoonriseSignature[] = {
11294 	SIG_MAGICDWORD,
11295 	0x81, 0x7a,                         // lag global[122]
11296 	0xa5, 0x00,                         // sat temp[0]
11297 	0x89, 0x7b,                         // lsg global[123]
11298 	0x35, 0x06,                         // ldi 6d
11299 	0x1c,                               // ne?
11300 	0x2f, 0x06,                         // bt 6d [skip remaining OR conditions]
11301 	0x89, 0x78,                         // lsg global[120]
11302 	0x34, SIG_UINT16(0x01f4),           // ldi 500d
11303 	0x1e,                               // gt?
11304 	0x31, 0x02,                         // bnt 2d [skip the if block]
11305 	0xc5, 0x00,                         // +at temp[0]
11306 	SIG_END
11307 };
11308 
11309 static const uint16 qfg4MoonrisePatch[] = {
11310 	0x35, 0x00,                         // ldi 0 (reset the is-night var)
11311 	0xa3, 0x05,                         // sal local[5]
11312 	0x33, 0x0f,                         // jmp 15d [skip waste bytes]
11313 	PATCH_END
11314 };
11315 
11316 // Visiting the inn after rescuing Igor sets a plot flag. Such flags are tested
11317 // on subsequent visits to decide the dialogue options when clicking MOUTH on
11318 // hero. That particular check neglects time of day, allowing hero to talk to
11319 // an empty room after midnight... and get responses from the absent innkeeper.
11320 //
11321 // The inn's init() has a series of cond blocks to boil down all the checks
11322 // into values for local[2], representing discrete situations. Then there's a
11323 // switch block in sInitShit() that acts on those values, making arrival
11324 // announcements and setting new flags.
11325 //
11326 // "So Dmitri says the gypsy didn't really kill Igor after all." sets flag 132.
11327 // "I must thank you for saving our Tanya." sets flag 134.
11328 //
11329 // There are two bugged situations. When you have flag 132 and haven't gotten
11330 // 134 yet, local[2] = 11. When you get flag 134, local[2] = 12. Neither
11331 // of them consider the time of day, talking as if the innkeeper were always
11332 // present.
11333 //
11334 // A day in QFG4 is broken up into 3-hour spans: 6,7,0,1,2,3,4,5. Where 6 is
11335 // midnight. The sun rises in 0 and sets in 4. Current span is global[123]. The
11336 // innkeeper sprite is not around from midnight to morning.
11337 //
11338 // To make room, we optimize block 10's time check:
11339 // "t <= 3 || t is in [4, 5]" becomes "t <= 5". No need for a lengthy call
11340 // to do a simple comparison. Conceptually it meant, "daytime and evening".
11341 //
11342 // That gap is used to insert similar pre-midnight checks before block 11 and
11343 // block 12. This will sync them with the sprite's schedule. Ideally, the
11344 // sprite never would've been scheduled separately in the first place.
11345 //
11346 // Applies to at least: English CD, English floppy, German floppy
11347 // Responsible method: rm320::init() in script 320
11348 // Fixes bug: #10753
11349 static const uint16 qfg4AbsentInnkeeperSignature[] = {
11350 	SIG_MAGICDWORD,                     // (block 10, partway through)
11351 	0x31, 0x1c,                         // bnt 28d [block 11]
11352 	0x89, 0x7b,                         // lsg global[123]
11353 	0x35, 0x03,                         // ldi 3d
11354 	0x24,                               // le?
11355                                         // (~~ junk begins ~~)
11356 	0x2f, 0x0f,                         // bt 15d [after the calle]
11357 	0x39, 0x03,                         // pushi 3d (3 call args)
11358 	0x89, 0x7b,                         // lsg global[123] (needle value)
11359 	0x39, 0x04,                         // pushi 4d (haystack values...)
11360 	0x39, 0x05,                         // pushi 5d
11361 	0x46, SIG_UINT16(0xfde7), SIG_UINT16(0x0005), SIG_UINT16(0x0006), // calle [export 5 of script 64999], 6d (is needle in haystack?))
11362                                         // (~~ junk ends ~~)
11363 	0x31, 0x04,                         // bnt 4d [block 11]
11364 	0x35, 0x0a,                         // ldi 10d
11365 	0x33, 0x29,                         // jmp 41d [done, local[2]=acc]
11366                                         //
11367 	SIG_ADDTOOFFSET(+25),               // (...block 11...) (patch ends after this)
11368 	SIG_ADDTOOFFSET(+14),               // (...block 12...)
11369 	SIG_ADDTOOFFSET(+2),                // (...else 0...)
11370 	0xa3, 0x02,                         // sal local[2] (all blocks set acc and jmp here)
11371 	SIG_END
11372 };
11373 
11374 static const uint16 qfg4AbsentInnkeeperPatch[] = {
11375 	0x31, 0x0e,                         // bnt 14d [block 11]
11376 	0x89, 0x7b,                         // lsg global[123]
11377 	0x35, 0x05,                         // ldi 5d (make it t <= 5)
11378 	0x24,                               // le?
11379                                         // (*snip*)
11380 	0x31, 0x07,                         // bnt 7d [block 11]
11381 	0x35, 0x0a,                         // ldi 10d
11382 	0x33, 0x3a,                         // jmp 58d [done, local[2]=acc]
11383                                         //
11384 	0x34, PATCH_UINT16(0x0000),         // ldi 0 (waste 3 bytes)
11385                                         // (14 freed bytes remain.)
11386                                         // (use 7 freed bytes to prepend block 11)
11387                                         // (and shift all of block 11 up by 7 bytes)
11388                                         // (use that new gap below to prepend block 12)
11389                                         //
11390                                         // (block 11, prepend a time check)
11391 	0x89, 0x7b,                         // lsg global[123]
11392 	0x35, 0x05,                         // ldi 5d
11393 	0x24,                               // le?
11394 	0x31, 0x19,                         // bnt 25d [block 12]
11395                                         // (block 11, original ops shift up)
11396 	0x78,                               // push1 (1 call arg)
11397 	0x38, PATCH_UINT16(0x0084),         // pushi 132d
11398 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb [export 4 of script 0], 2d (test flag 132)
11399 	0x31, 0x0f,                         // bnt 15d [next block]
11400 	0x78,                               // push1 (1 call arg)
11401 	0x38, PATCH_UINT16(0x0086),         // pushi 134d
11402 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb [export 4 of script 0], 2d (test flag 134)
11403 	0x18,                               // not
11404 	0x31, 0x04,                         // bnt 4d [block 12]
11405 	0x35, 0x0b,                         // ldi 11d
11406 	0x33, 0x17,                         // jmp 23d [done, local[2]=acc]
11407                                         //
11408                                         // (block 12, prepend a time check)
11409 	0x89, 0x7b,                         // lsg global[123]
11410 	0x35, 0x05,                         // ldi 5d
11411 	0x24,                               // le?
11412 	0x31, 0x0e,                         // bnt 14d [else block]
11413                                         // (block 12, original ops continue here)
11414   	PATCH_END
11415 };
11416 
11417 // WORKAROUND: Script needed, because of differences in our pathfinding
11418 // algorithm.
11419 // When entering flipped stairways from the upper door, hero is initially
11420 // placed outside the walkable area. As a result, hero will float around
11421 // inappropriately in stairways leading to Tanya's room (620) and to the iron
11422 // safe (624).
11423 //
11424 // The polygon's first and final points are the top of the stairs. It's quite
11425 // narrow up there, and the final segment doesn't trace the wall very well. We
11426 // move the final point down and over to round out the path. Point 0 takes 19's
11427 // original place. Point 1 takes 0's original place.
11428 //
11429 // Disregard the responsible method's misleading name.
11430 //
11431 // Applies to at least: English CD, English floppy, German floppy
11432 // Responsible method: rm620Code::init() in script 633
11433 // Fixes bug: #10757
11434 static const uint16 qfg4StairwayPathfindingSignature[] = {
11435 	SIG_MAGICDWORD,
11436 	0x38, SIG_UINT16(0x00e2),           // pushi 226d (point 0 is top-left)
11437 	0x39, 0x20,                         // pushi 32d
11438 	0x38, SIG_UINT16(0x00ed),           // pushi 237d (point 1 is below on left)
11439 	0x39, 0x26,                         // pushi 38d
11440 	SIG_ADDTOOFFSET(+87),               // ...
11441 	0x38, SIG_UINT16(0x00e9),           // pushi 233d (point 19 is top-right)
11442 	0x39, 0x20,                         // pushi 32d
11443 	SIG_END
11444 };
11445 
11446 static const uint16 qfg4StairwayPathfindingPatch[] = {
11447 	0x38, PATCH_UINT16(0x00e9),         // pushi 233d (point 0 gets 19's coords)
11448 	0x39, 0x20,                         // pushi 32d
11449 	0x38, PATCH_UINT16(0x00e2),         // pushi 226d (point 1 gets 0's coords)
11450 	0x39, 0x20,                         // pushi 32d
11451 	PATCH_ADDTOOFFSET(+87),             // ...
11452 	0x38, PATCH_UINT16(0x00fd),         // pushi 253d (point 19 hugs the wall)
11453 	0x39, 0x2b,                         // pushi 43d
11454 	PATCH_END
11455 };
11456 
11457 // Whenever levitate is cast, a cryptic error message appears in-game.
11458 // "<Prop setScale:> y value less than vanishingY"
11459 //
11460 // There are typos where hero is passed to Prop::setScale(), a method which
11461 // expects integers. We change it to Prop::setScaler().
11462 //
11463 // Applies to at least: English CD, English floppy, German floppy
11464 // Responsible method: sLevitate::changeState(3) in script 31
11465 //                     sLevitating::changeState(0) in script 800 (CD only)
11466 // Fixes bug: #10726
11467 static const uint16 qfg4SetScalerSignature[] = {
11468 	SIG_MAGICDWORD,
11469 	0x38, SIG_SELECTOR16(setScale),     // pushi setScale
11470 	0x78,                               // push1
11471 	0x89, 0x00,                         // lsg global[0] (hero)
11472 	SIG_END
11473 };
11474 
11475 static const uint16 qfg4SetScalerPatch[] = {
11476 	0x38, PATCH_SELECTOR16(setScaler),  // pushi setScaler
11477 	PATCH_END
11478 };
11479 
11480 // The castle's crest-operated bookshelf has an unconditional HAND message
11481 // which always says, "you haven't found the trigger yet," even after it's
11482 // open.
11483 //
11484 // We schedule the walk-out script (sLeaveSecretly) at the end of the opening
11485 // script (sSecret) to force hero to leave immediately, preventing any
11486 // interaction with an open bookshelf.
11487 //
11488 // An automatic exit is consistent with the other bookshelf passage rooms:
11489 // Chandelier (662) and EXIT (661).
11490 //
11491 // Clobbers Glory::handsOn() and sSecret::dispose() to do Room::setScript().
11492 // Both of them are made redundant by setScript's built-in disposal and
11493 // sLeaveSecretly's immediate use of Glory::handsOff().
11494 //
11495 // This patch has two variants, toggled to match the detected edition with
11496 // enablePatch() below. Aside from the patched lofsa value, they are identical.
11497 //
11498 // Applies to at least: English CD
11499 // Responsible method: sSecret::changeState(4) in script 663
11500 // Fixes bug: #10756
11501 static const uint16 qfg4CrestBookshelfCDSignature[] = {
11502 	SIG_MAGICDWORD,
11503 	0x4a, SIG_UINT16(0x003e),           // send 62d
11504 	0x36,                               // push
11505 	SIG_ADDTOOFFSET(+5),                // ...
11506 	0x38, SIG_SELECTOR16(handsOn),      // pushi handsOn (begin clobbering)
11507 	0x76,                               // push0
11508 	0x81, 0x01,                         // lag global[1] (Glory)
11509 	0x4a, SIG_UINT16(0x0004),           // send 4d
11510 	0x38, SIG_SELECTOR16(dispose),      // pushi dispose
11511 	0x76,                               // push0
11512 	0x54, SIG_UINT16(0x0004),           // self 4d
11513 	SIG_END
11514 };
11515 
11516 static const uint16 qfg4CrestBookshelfCDPatch[] = {
11517 	PATCH_ADDTOOFFSET(+9),
11518 	0x38, PATCH_SELECTOR16(setScript),  // pushi setScript
11519 	0x78,                               // push1
11520 	0x72, PATCH_UINT16(0x01a4),         // lofsa sLeaveSecretly
11521 	0x36,                               // push
11522 	0x81, 0x02,                         // lag global[2] (rm663)
11523 	0x4a, PATCH_UINT16(0x0006),         // send 6d
11524 	0x34, PATCH_UINT16(0x0000),         // ldi 0 (waste 3 bytes)
11525 	PATCH_END
11526 };
11527 
11528 // Applies to at least: English floppy, German floppy
11529 static const uint16 qfg4CrestBookshelfFloppySignature[] = {
11530 	SIG_MAGICDWORD,
11531 	0x4a, SIG_UINT16(0x003e),           // send 62d
11532 	0x36,                               // push
11533 	SIG_ADDTOOFFSET(+5),                // ...
11534 	0x38, SIG_SELECTOR16(handsOn),      // pushi handsOn (begin clobbering)
11535 	0x76,                               // push0
11536 	0x81, 0x01,                         // lag global[1] (Glory)
11537 	0x4a, SIG_UINT16(0x0004),           // send 4d
11538 	0x38, SIG_SELECTOR16(dispose),      // pushi dispose
11539 	0x76,                               // push0
11540 	0x54, SIG_UINT16(0x0004),           // self 4d
11541 	SIG_END
11542 };
11543 
11544 static const uint16 qfg4CrestBookshelfFloppyPatch[] = {
11545 	PATCH_ADDTOOFFSET(+9),
11546 	0x38, PATCH_SELECTOR16(setScript),  // pushi setScript
11547 	0x78,                               // push1
11548 	0x72, PATCH_UINT16(0x018c),         // lofsa sLeaveSecretly
11549 	0x36,                               // push
11550 	0x81, 0x02,                         // lag global[2] (rm663)
11551 	0x4a, PATCH_UINT16(0x0006),         // send 6d
11552 	0x34, PATCH_UINT16(0x0000),         // ldi 0 (waste 3 bytes)
11553 	PATCH_END
11554 };
11555 
11556 // Modifies room 663's sLeaveSecretly to avoid obstacles.
11557 //
11558 // Originally intended to start when hero arrives at a doorMat region, room
11559 // 663's walk-out script (sLeaveSecretly) ignores obstacles. The crest
11560 // bookshelf patch repurposes this script and requires collision detection to
11561 // exit properly, walking around the open bookshelf.
11562 //
11563 // Class numbers for MoveTo and PolyPath differ between CD vs floppy editions.
11564 // Their intervals happen to be the same, so we simply offset whatever is
11565 // there.
11566 //
11567 // Applies to at least: English CD, English floppy, German floppy
11568 // Responsible method: sLeaveSecretly::changeState(1) in script 663
11569 // Fixes bug: #10756
11570 static const uint16 qfg4CrestBookshelfMotionSignature[] = {
11571 	0x51, SIG_ADDTOOFFSET(+1),          // class MoveTo
11572 	0x36,                               // push
11573 	SIG_MAGICDWORD,
11574 	0x39, 0x1d,                         // pushi x = 29d
11575 	0x38, SIG_UINT16(0x0097),           // pushi y = 151d
11576 	SIG_END
11577 };
11578 
11579 static const uint16 qfg4CrestBookshelfMotionPatch[] = {
11580 	0x51, PATCH_GETORIGINALBYTEADJUST(+1, +6), // class PolyPath
11581 	PATCH_END
11582 };
11583 
11584 // In the crest bookshelf room (663) connected to the upper door of the
11585 // bat-infested stairway, peering through the keyhole *always* reports bats.
11586 //
11587 // As you kill the bats, global plot flags are set. Normally flags 331-334
11588 // would be checked via proc0_4(). They're in a bitmask, among other flags, so
11589 // we can check all simultaneously (0000000000011110).
11590 //
11591 // global[520] & 30 == 30
11592 //
11593 // Patch 1: There was no space for this in the responsible method. Instead, we
11594 //  rewrite a different method that became obsolete after the crest bookshelf
11595 //  patch: sCloseSecretDoor::changeState().
11596 //
11597 // Patch 2: We modify sPeepingTom to call our rewritten sCloseSecretDoor. This
11598 //  has two variants, toggled to match the detected edition with enablePatch()
11599 //  below. Aside from the patched lofsa value, they are identical.
11600 //
11601 // Requires patch: qfg4CrestBookshelf (CD or Floppy)
11602 // Applies to at least: English CD, English floppy, German floppy
11603 // Responsible method: sPeepingTom::changeState(1) in script 663
11604 // Fixes bug: #10789
11605 static const uint16 qfg4UpperPeerBatsSignature1[] = {
11606 	0x87, 0x01,                         // lap param[1]
11607 	SIG_ADDTOOFFSET(+41),               // ...
11608 	SIG_MAGICDWORD,
11609 	0x4a, SIG_UINT16(0x0006),           // send 6d
11610 	0x35, 0x1e,                         // ldi 30d
11611 	0x65, SIG_ADDTOOFFSET(+1),          // aTop ticks
11612 	SIG_ADDTOOFFSET(+9),                // ...
11613 	0x38, SIG_SELECTOR16(setCycle),     // pushi setCycle
11614 	SIG_END
11615 };
11616 
11617 static const uint16 qfg4UpperPeerBatsPatch1[] = {
11618 	0x38, PATCH_SELECTOR16(say),        // pushi say (decide the message as args are stacked up)
11619 	0x39, 0x06,                         // pushi 6d
11620 	0x7a,                               // push2
11621 	0x38, PATCH_UINT16(0x009b),         // pushi 155d
11622 
11623 	0x39, 0x1e,                         // pushi 30d (stack up for eq)
11624 	0x3c,                               // dup (stack up another for AND)
11625 	0x80, PATCH_UINT16(0x0208),         // lag global[520] (plot flags bitmask)
11626 	0x12,                               // and
11627 	0x1a,                               // eq? (Were all dead bat flags set?)
11628 	0x2f, 0x04,                         // bt 4d [after this jmp]
11629 	0x39, 0x1d,                         // pushi 29d (bat message)
11630 	0x33, 0x02,                         // jmp 2d [after deciding message]
11631 
11632 	0x39, 0x1b,                         // pushi 27d (killed all bats, generic message)
11633 
11634 	0x78,                               // push1
11635 	0x76,                               // push0 (don't cue() afterward)
11636 	0x38, PATCH_UINT16(0x0280),         // pushi 640d
11637 	0x81, 0x5b,                         // lag global[91] (gloryMessager)
11638 	0x4a, PATCH_UINT16(0x0010),         // send 16d
11639 	0x48,                               // ret
11640 	0x34, PATCH_UINT16(0x0000),         // ldi 0 (erase 3 bytes to keep disasm aligned)
11641 	PATCH_END
11642 };
11643 
11644 // Applies to at least: English CD
11645 static const uint16 qfg4UpperPeerBatsCDSignature2[] = {
11646 	0x38, SIG_SELECTOR16(say),          // pushi say
11647 	SIG_ADDTOOFFSET(+3),
11648 	SIG_MAGICDWORD,
11649 	0x7a,                               // push2
11650 	0x38, SIG_UINT16(0x009b),           // pushi 155d
11651 	0x39, 0x1d,                         // pushi 29d (bat message)
11652 	SIG_ADDTOOFFSET(+7),
11653 	0x4a, SIG_UINT16(0x0010),           // send 16d (say: 2 155 29 1 self 640)
11654 	PATCH_END
11655 };
11656 
11657 static const uint16 qfg4UpperPeerBatsCDPatch2[] = {
11658 	0x38, PATCH_SELECTOR16(changeState), // pushi changeState
11659 	0x78,                               // push1
11660 	0x76,                               // push0
11661 	0x72, PATCH_UINT16(0x0176),         // lofsa sCloseSecretDoor
11662 	0x4a, PATCH_UINT16(0x0006),         // send 6d (call the rewritten method)
11663 	0x38, PATCH_SELECTOR16(cue),        // pushi cue
11664 	0x76,                               // push0
11665 	0x54, PATCH_UINT16(0x0004),         // self 4d (self-cue)
11666 	0x35, 0x00,                         // ldi 0 (waste 2 bytes)
11667 	0x35, 0x00,                         // ldi 0 (waste 2 bytes)
11668 	PATCH_END
11669 };
11670 
11671 // Applies to at least: English floppy, German floppy
11672 static const uint16 qfg4UpperPeerBatsFloppySignature2[] = {
11673 	0x38, SIG_SELECTOR16(say),          // pushi say
11674 	SIG_ADDTOOFFSET(+3),
11675 	SIG_MAGICDWORD,
11676 	0x7a,                               // push2
11677 	0x38, SIG_UINT16(0x009b),           // pushi 155d
11678 	0x39, 0x1d,                         // pushi 29d (bat message)
11679 	SIG_ADDTOOFFSET(+7),
11680 	0x4a, SIG_UINT16(0x0010),           // send 16d (say: 2 155 29 1 self 640)
11681 	PATCH_END
11682 };
11683 
11684 static const uint16 qfg4UpperPeerBatsFloppyPatch2[] = {
11685 	0x38, PATCH_SELECTOR16(changeState), // pushi changeState
11686 	0x78,                               // push1
11687 	0x76,                               // push0
11688 	0x72, PATCH_UINT16(0x0160),         // lofsa sCloseSecretDoor
11689 	0x4a, PATCH_UINT16(0x0006),         // send 6d (call the rewritten method)
11690 	0x38, PATCH_SELECTOR16(cue),        // pushi cue
11691 	0x76,                               // push0
11692 	0x54, PATCH_UINT16(0x0004),         // self 4d (self-cue)
11693 	0x35, 0x00,                         // ldi 0 (waste 2 bytes)
11694 	0x35, 0x00,                         // ldi 0 (waste 2 bytes)
11695 	PATCH_END
11696 };
11697 
11698 // In the room (644) connected to the lower door of the bat-infested stairway,
11699 // peering through the keyhole *always* reports bats.
11700 //
11701 // As you kill the bats, global plot flags are set. Normally flags 331-334
11702 // would be checked via proc0_4(). They're in a bitmask, among other flags, so
11703 // we can check all simultaneously (0000000000011110).
11704 //
11705 // global[520] & 30 == 30
11706 //
11707 // Room 644 has an IF-ELSE deciding between largely redundant calls to
11708 // gloryMessager::say(). We make room by combining them.
11709 //
11710 // Applies to at least: English CD, English floppy, German floppy
11711 // Responsible method: sPeepingTom::changeState(1) in script 644
11712 // Fixes bug: #10789
11713 static const uint16 qfg4LowerPeerBatsSignature[] = {
11714 	SIG_MAGICDWORD,
11715 	0x78,                               // push1 x (check if hero's near the left door)
11716 	0x76,                               // push0
11717 	0x81, 0x00,                         // lag global[0] (hero)
11718 	0x4a, SIG_UINT16(0x0004),           // send 4d
11719 	0x36,                               // push
11720 	0x35, 0x3c,                         // ldi 60d
11721 	0x22,                               // lt?
11722 	0x31, 0x18,                         // bnt 24d [else right door w/ bats]
11723 	0x38, SIG_SELECTOR16(say),          // pushi say (left door, generic message)
11724 	SIG_ADDTOOFFSET(+14),               // ...
11725 	0x81, 0x5b,                         // lag global[91] (gloryMessager)
11726 	0x4a, SIG_UINT16(0x0010),           // send 16d (say: 2 155 27 1 self 640)
11727 	0x33, SIG_ADDTOOFFSET(+1),          // jmp [end the case]
11728 	SIG_ADDTOOFFSET(+22),               // (right door, say(), 3rd arg is 29 for bat message)
11729 	0x33, SIG_ADDTOOFFSET(+1),          // jmp [end the case]
11730 	SIG_END
11731 };
11732 
11733 static const uint16 qfg4LowerPeerBatsPatch[] = {
11734 	0x38, PATCH_SELECTOR16(say),        // pushi say (decide the message as args are stacked up)
11735 	0x39, 0x06,                         // pushi 6d
11736 	0x7a,                               // push2
11737 	0x38, PATCH_UINT16(0x009b),         // pushi 155d
11738 
11739 	0x78,                               // push1 x (check if left door)
11740 	0x76,                               // push0
11741 	0x81, 0x00,                         // lag global[0] (hero)
11742 	0x4a, PATCH_UINT16(0x0004),         // send 4d
11743 	0x36,                               // push
11744 	0x35, 0x3c,                         // ldi 60d
11745 	0x22,                               // lt?
11746 	0x31, 0x04,                         // bnt 4d [after this jmp]
11747 	0x39, 0x1b,                         // pushi 27d (left door, generic message)
11748 	0x33, 0x10,                         // jmp 16d [after deciding message]
11749 
11750 	0x39, 0x1e,                         // pushi 30d (stack up for eq)
11751 	0x3c,                               // dup (stack up another for AND)
11752 	0x80, PATCH_UINT16(0x0208),         // lag global[520] (plot flags bitmask)
11753 	0x12,                               // and
11754 	0x1a,                               // eq? (Were all dead bat flags set?)
11755 	0x2f, 0x04,                         // bt 4d [after this jmp]
11756 	0x39, 0x1d,                         // pushi 29d (right door, bat message)
11757 	0x33, 0x02,                         // jmp 2d [after deciding message]
11758 
11759 	0x39, 0x1b,                         // pushi 27d (right door, killed all bats, generic message)
11760 
11761 	0x78,                               // push1
11762 	0x7c,                               // pushSelf
11763 	0x38, PATCH_UINT16(0x0280),         // pushi 640d
11764 	0x81, 0x5b,                         // lag global[91] (gloryMessager)
11765 	0x4a, PATCH_UINT16(0x0010),         // send 16d
11766 	0x33, PATCH_GETORIGINALBYTEADJUST(60, +7), // jmp [end the case]
11767 	PATCH_END
11768 };
11769 
11770 // The castle's great hall (630) has a doorMat region that intermittently sends
11771 // hero back to the room they just left (barrel room) the instant they arrive.
11772 //
11773 // Entry from room 623 starts hero at (0, 157), the edge of the doorMat. We
11774 // shrink the region by 2 pixels. Then sEnterTheRoom moves hero safely across.
11775 // The region is a rectangle. Point 0 is top-left. Point 3 is bottom-left.
11776 //
11777 // Does not apply to English floppy 1.0. It lacked a western doorMat entirely.
11778 //
11779 // Applies to at least: English CD, English floppy, German floppy
11780 // Responsible method: vClosentDoor::init() in script 630
11781 // Fixes bug: #10731
11782 static const uint16 qfg4GreatHallEntrySignature[] = {
11783 	SIG_MAGICDWORD,
11784 	0x76,                               // push0 (point 0)
11785 	0x38, SIG_UINT16(0x0088),           // pushi 136d
11786 	SIG_ADDTOOFFSET(+10),               // ...
11787 	0x76,                               // push0 0d (point 3)
11788 	0x38, SIG_UINT16(0x00b4),           // pushi 180d
11789 	SIG_END
11790 };
11791 
11792 static const uint16 qfg4GreatHallEntryPatch[] = {
11793 	0x7a,                               // push2
11794 	PATCH_ADDTOOFFSET(+13),             // ...
11795 	0x7a,                               // push2
11796 	PATCH_END
11797 };
11798 
11799 // In QFG4, the kernel func SetNowSeen() returns void - meaning it doesn't
11800 // modify the accumulator to store a result. Yet math was performed on it!
11801 //
11802 // The function updates boundary box properties of a given View object, prior
11803 // to collision tests. IF (collision detection is warranted, and IF those tests
11804 // are true), THEN respond to the collision.
11805 //
11806 // SetNowSeen() was inserted in the middle of the IF block's list of conditions.
11807 // That way it can be short-circuited, and it runs before the tests.
11808 //
11809 // Problem: void functions make no promise about their truth value. After the
11810 // call, acc will be *whatever* happened to already be in there. This is bad.
11811 //
11812 // "(| (SetNowSeen horror) $0001)"
11813 //
11814 // Someone wrapped the func in a bitwise OR against 1. Thus *every* value is
11815 // guaranteed to become non-zero, always true. And the IF block won't break.
11816 //
11817 // In later SCI2 versions, SetNowSeen() would change to return a boolean.
11818 //
11819 // Whether a lucky confusion or ugly hack, the wrapped void IF condition works.
11820 // When an object leaks into the accumulator. SSCI doesn't mind OR'ing it, too.
11821 // ScummVM detects unsafe arithmetic and crashes. ScummVM needs proper numbers.
11822 //
11823 // "Invalid arithmetic operation (bitwise OR - params: 002e:1694 and 0000:0001)"
11824 //
11825 // We leave the OR wrapper. When the call returns, we manually feed the OR a
11826 // literal 1. Same effect. The IF block goes on evaluating conditions.
11827 //
11828 // Wraith, Vorpal Bunny, and Badder scripts are not affected.
11829 //
11830 // Applies to at least: English CD, English floppy, German floppy
11831 // Responsible method:
11832 // (ego1, combat hero) Script 41 - xSlash::doit(), xDuck::doit(), xParryLow::doit()
11833 // (ego1, combat hero) Script 810 - slash::doit()
11834 //          (Revenant) Script 830 - revenantForward::doit()
11835 //            (Wyvern) Script 835 - doRSlash::doit(), doLSlash::doit(), tailAttack::doit()
11836 //          (Chernovy) Script 840 - doLSlash::doit(), doRSlash::doit()
11837 //        (Pit Horror) Script 855 - wipeSpell::doit()
11838 //         (Necrotaur) Script 870 - attackLeft::doit(), attackRight::doit(),
11839 //                                  headAttack::doit(), hurtMyself::changeState(1)
11840 // Fixes bug: #10138, #10419, #10710, #10814
11841 static const uint16 qfg4ConditionalVoidSignature[] = {
11842 	SIG_MAGICDWORD,
11843 	0x43, 0x0a, SIG_UINT16(0x0002),     // callk SetNowSeen, 2d (update bounds for a stacked View)
11844 	0x36,                               // push (void func didn't set acc!)
11845 	0x35, 0x01,                         // ldi 1d
11846 	0x14,                               // or (whatever that was, make it non-zero)
11847 	SIG_END
11848 };
11849 
11850 static const uint16 qfg4ConditionalVoidPatch[] = {
11851 	PATCH_ADDTOOFFSET(+4),              //
11852 	0x78,                               // push1 (feed OR a literal 1)
11853 	PATCH_END
11854 };
11855 
11856 // In the graveyard rescuing Igor, ropes are briefly obscured by crypt pillars
11857 // in the background Pic. The Pic assigns a priority to the pillars for depth.
11858 // Ropes are initialized without priority. Then there's a setPri() call.
11859 //
11860 // The floppy edition's Actor::doit() readily calls UpdateScreenItem(). Thus it
11861 // promptly responds to the new priority, bringing the ropes to the front.
11862 //
11863 // The CD edition changed Actor to require a bit flag on the "signal" property
11864 // before it would call UpdateScreenItem(). So the CD edition graphics don't
11865 // update until much later, when the ropes begin an animation.
11866 //
11867 // We patch the heap for script 500 (the graveyard) to give rope1 and rope2
11868 // that "signal" bit as soon as they're created. This'll be toggled with
11869 // enablePatch() below to only apply to the CD edition.
11870 //
11871 // Applies to at least: English CD
11872 // Responsible method: Actor::doit() in script 64998
11873 // Fixes bug: #10751
11874 static const uint16 qfg4GraveyardRopeSignature1[] = {
11875 	SIG_MAGICDWORD,                     // (rope1 properties)
11876 	SIG_UINT16(0x0064),                 // x = 100d
11877 	SIG_UINT16(0xfff6),                 // y = -10d
11878 	SIG_ADDTOOFFSET(+24),               // ...
11879 	SIG_UINT16(0x01f6),                 // view = 502d
11880 	SIG_ADDTOOFFSET(+8),                // ...
11881 	SIG_UINT16(0x6000),                 // signal = 0x6000
11882 	SIG_END
11883 };
11884 
11885 static const uint16 qfg4GraveyardRopePatch1[] = {
11886 	PATCH_ADDTOOFFSET(+38),
11887 	PATCH_UINT16(0x6001),               // signal = 0x6001
11888 	PATCH_END
11889 };
11890 
11891 static const uint16 qfg4GraveyardRopeSignature2[] = {
11892 	SIG_MAGICDWORD,                     // (rope2 properties)
11893 	SIG_UINT16(0x007f),                 // x = 127d
11894 	SIG_UINT16(0xfffb),                 // y = -5d
11895 	SIG_ADDTOOFFSET(+24),               // ...
11896 	SIG_UINT16(0x01f6),                 // view = 502d
11897 	SIG_ADDTOOFFSET(+8),                // ...
11898 	SIG_UINT16(0x6000),                 // signal = 0x6000
11899 	SIG_END
11900 };
11901 
11902 static const uint16 qfg4GraveyardRopePatch2[] = {
11903 	PATCH_ADDTOOFFSET(+38),
11904 	PATCH_UINT16(0x6001),               // signal = 0x6001
11905 	PATCH_END
11906 };
11907 
11908 // Rooms 622 and 623 play an extra door sound when entering. They both
11909 // delegate to script 645. It schedules sEnter, which indeed has an extra
11910 // sound. The CD edition removed the line. We remove it, too.
11911 //
11912 // Applies to at least: English floppy, German floppy
11913 // Responsible method: sEnter::changeState(4) in script 645
11914 // Fixes bug: #10827
11915 static const uint16 qfg4DoubleDoorSoundSignature[] = {
11916 	0x35, 0x04,                         // ldi 4d (state 4)
11917 	SIG_ADDTOOFFSET(+3),                // ...
11918 	SIG_MAGICDWORD,
11919 	0x39, SIG_SELECTOR8(play),          // pushi play
11920 	0x76,                               // push0
11921 	0x72, SIG_UINT16(0x0376),           // lofsa doorSound
11922 	0x4a, SIG_UINT16(0x0004),           // send 4d
11923 	SIG_END
11924 };
11925 
11926 static const uint16 qfg4DoubleDoorSoundPatch[] = {
11927 	PATCH_ADDTOOFFSET(+5),
11928 	0x33, 0x07,                         // jmp 7d [skip waste bytes]
11929 	PATCH_END
11930 };
11931 
11932 // In the castle's iron safe room (643), the righthand door may send hero west
11933 // instead of east - if it was oiled before it was opened (not picked).
11934 //
11935 // The room uses local[2] to remember which door it last decided was nearest.
11936 // The proximity check when opening the right door doesn't reliably set
11937 // local[2]. The assignment was buried inside an IF block testing the oiled
11938 // flag to decide whether the door should squeak. So if the door's been oiled,
11939 // local[2] is not set. If hero had entered the safe from from the west,
11940 // rm643::init() would set local[2] to the left door, and sOpenTheDoor would
11941 // remember LEFT as it decided where to send hero to next.
11942 //
11943 // We move the local[2] assignment out of the IF block, to always run.
11944 //
11945 // Applies to at least: English CD, English floppy, German floppy
11946 // Responsible method: sOpenTheDoor::changeState(0) in script 643
11947 // Fixes bug: #10829
11948 static const uint16 qfg4SafeDoorEastSignature[] = {
11949 	SIG_MAGICDWORD,                     // (else block, right door)
11950 	0x78,                               // push1 (1 call arg)
11951 	0x38, SIG_UINT16(0x00d7),           // pushi 215d (right door oiled flag)
11952 	0x45, 0x04, SIG_UINT16(0x0002),     // callb [export 4 of script 0], 2d (test flag 215)
11953 	0x18,                               // not
11954 	0x31, SIG_ADDTOOFFSET(+1),          // bnt ?? [end the else block]
11955                                         //
11956 	0x35, 0x00,                         // ldi 0
11957 	0xa3, 0x02,                         // sal local[2]
11958 	SIG_END
11959 };
11960 
11961 static const uint16 qfg4SafeDoorEastPatch[] = {
11962 	0x35, 0x00,                         // ldi 0
11963 	0xa3, 0x02,                         // sal local[2]
11964                                         //
11965 	0x78,                               // push1 (1 call arg)
11966 	0x38, PATCH_UINT16(0x00d7),         // pushi 215d (right door oiled flag)
11967 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb [export 4 of script 0], 2d (test flag 215)
11968 	0x18,                               // not
11969 	0x31, PATCH_GETORIGINALBYTEADJUST(10, -4), // bnt ?? [end the else block]
11970 	PATCH_END
11971 };
11972 
11973 // In the castle's iron safe room (643), plot flags are mixed up. When hero
11974 // oils either door, the other door's flag is set. Adjacent rooms oil their
11975 // respective doors properly from the outside. We switch the flags inside.
11976 //
11977 // Applies to at least: English CD, English floppy, German floppy
11978 // Responsible method: vBackDoor::doVerb(32), vLeftDoor::doVerb(32) in script 643
11979 // Fixes bug: #10829
11980 static const uint16 qfg4SafeDoorOilSignature[] = {
11981 	0x35, 0x20,                         // ldi 32d (vBackDoor::doVerb(oil), right door)
11982 	SIG_ADDTOOFFSET(+5),                // ...
11983 	SIG_MAGICDWORD,
11984 	0x38, SIG_UINT16(0x00d6),           // pushi 214d (left oiled flag!?)
11985 	0x45, 0x02, SIG_UINT16(0x0002),     // callb [export 2 of script 0], 2d (set flag 214)
11986 
11987 	SIG_ADDTOOFFSET(+152),              // ...
11988 
11989 	0x35, 0x20,                         // ldi 32d (vLeftDoor::doVerb(oil), left door)
11990 	SIG_ADDTOOFFSET(+5),                // ...
11991 	0x38, SIG_UINT16(0x00d7),           // pushi 215d (right oiled flag!?)
11992 	0x45, 0x02, SIG_UINT16(0x0002),     // callb [export 2 of script 0], 2d (set flag 215)
11993 	SIG_END
11994 };
11995 
11996 static const uint16 qfg4SafeDoorOilPatch[] = {
11997 	PATCH_ADDTOOFFSET(+7),
11998 	0x38, PATCH_UINT16(0x00d7),         // pushi 215d (right door, set right oiled flag)
11999 	PATCH_ADDTOOFFSET(+4+152+7),
12000 	0x38, PATCH_UINT16(0x00d6),         // pushi 214d (left door, set left oiled flag)
12001 	PATCH_END
12002 };
12003 
12004 // Waking after a dream by the staff in town (room 270) prevents the room from
12005 // creating a doorMat at nightfall, if hero rests repeatedly. The town gate
12006 // closes at night. Without the doorMat, hero isn't prompted to climb over the
12007 // gate. Instead, hero casually walks south and gets stuck in the next room
12008 // behind the closed gate.
12009 //
12010 // Since hero wakes in the morning, sAfterTheDream disposes any existing
12011 // doorMat. It neglects to reset local[2], which toggles rm270::doit()'s
12012 // constant checks for nightfall to replace the doorMat.
12013 //
12014 // We cache an object lookup and use the spare bytes to reset local[2].
12015 //
12016 // Note: There was never any sunrise detection. If hero rests repeatedly until
12017 // morning, the doorMat will linger to needlessly prompt about climbing the
12018 // then-open gate. Harmless. The prompt sets global[423] (1=climb, 2=levitate).
12019 // The gate room only honors that global at night, so hero will simply walk
12020 // through. Heroes unable to climb/levitate would be denied until they re-enter
12021 // the room.
12022 //
12023 // Applies to at least: English CD, English floppy, German floppy
12024 // Responsible method: sAfterTheDream::changeState(2) in script 270
12025 // Fixes bug: #10830
12026 static const uint16 qfg4DreamGateSignature[] = {
12027 	SIG_MAGICDWORD,
12028 	0x39, SIG_SELECTOR8(heading),       // pushi heading
12029 	0x76,                               // push0
12030 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa fSouth
12031 	0x4a, SIG_UINT16(0x0004),           // send 4d
12032 	0x31, 0x1a,                         // bnt 26d [skip disposing/nulling] (no heading)
12033                                         //
12034 	0x38, SIG_SELECTOR16(dispose),      // pushi dispose
12035 	0x76,                               // push0
12036 	0x39, SIG_SELECTOR8(heading),       // pushi heading
12037 	0x76,                               // push0
12038 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa fSouth
12039 	0x4a, SIG_UINT16(0x0004),           // send 4d (accumulate heading)
12040 	0x4a, SIG_UINT16(0x0004),           // send 4d (dispose heading)
12041                                         //
12042 	0x39, SIG_SELECTOR8(heading),       // pushi heading
12043 	0x78,                               // push1
12044 	0x76,                               // push0
12045 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa fSouth
12046 	0x4a, SIG_UINT16(0x0006),           // send 6d (set fSouth's heading to null)
12047 	SIG_END
12048 };
12049 
12050 static const uint16 qfg4DreamGatePatch[] = {
12051 	0x3f, 0x01,                         // link 1d (cache heading for reuse)
12052 	0x39, PATCH_SELECTOR8(heading),     // pushi heading
12053 	0x76,                               // push0
12054 	0x72, PATCH_GETORIGINALUINT16(4),   // lofsa fSouth
12055 	0x4a, PATCH_UINT16(0x0004),         // send 4d
12056 	0xa5, 0x00,                         // sat temp[0]
12057 	0x31, 0x13,                         // bnt 19d [skip disposing/nulling] (no heading)
12058                                         //
12059 	0x38, PATCH_SELECTOR16(dispose),    // pushi dispose
12060 	0x76,                               // push0
12061 	0x85, 0x00,                         // lat temp[0]
12062 	0x4a, PATCH_UINT16(0x0004),         // send 4d (dispose: heading:)
12063                                         //
12064 	0x39, PATCH_SELECTOR8(heading),     // pushi heading
12065 	0x78,                               // push1
12066 	0x76,                               // push0
12067 	0x72, PATCH_GETORIGINALUINT16(4),   // lofsa fSouth
12068 	0x4a, PATCH_UINT16(0x0006),         // send 6d (set fSouth's heading to null)
12069                                         //
12070 	0x76,                               // push0
12071 	0xab, 0x02,                         // ssl local[2] (let doit() watch for nightfall)
12072 	PATCH_END
12073 };
12074 
12075 // When approaching the town gate at night in room 270, dismissing the menu
12076 //  often doesn't work and instead repeats the gate message and menu. Upon
12077 //  entering the gate's doormat, sTo290Night moves hero up by 6 pixels, assuming
12078 //  that this places him outside the doormat. That assumption is usually wrong
12079 //  since it depends on hero's start position, walk/run mode, and game speed.
12080 //
12081 // We fix this by moving hero one pixel above the doormat instead.
12082 //
12083 // Applies to: All versions
12084 // Responsible method: sTo290Night:changeState(1)
12085 // Fixes bug: #10995
12086 static const uint16 qfg4TownGateDoormatSignature[] = {
12087 	0x7a,                               // push2 [ y ]
12088 	0x76,                               // push0
12089 	0x81, 0x00,                         // lag 00
12090 	0x4a, SIG_UINT16(0x0004),           // send 04 [ hero:y? ]
12091 	SIG_MAGICDWORD,
12092 	0x36,                               // push
12093 	0x35, 0x06,                         // ldi 06
12094 	0x04,                               // sub
12095 	0x36,                               // push [ hero:y - 6 ]
12096 	SIG_END
12097 };
12098 
12099 static const uint16 qfg4TownGateDoormatPatch[] = {
12100 	0x38, PATCH_UINT16(0x00b4),         // pushi 180d [ 1 pixel above doormat ]
12101 	0x33, 0x07,                         // jmp 07
12102 	PATCH_END
12103 };
12104 
12105 // Some inventory item properties leak across restarts. Most conspicuously, the
12106 // torch icon appears pre-lit after a restart, if it had been lit before.
12107 //
12108 // script 16 - thePiepan (item #28): loop, cel, value
12109 // script 35 - theBroom (item #39): cel, value
12110 // script 35 - theTorch (item #44): cel
12111 //
12112 // Glory::restart() tries to revert a bunch of globals to their original state.
12113 // Each value was individually loaded into acc and assigned (ldi+sag, ldi+sag,
12114 // ldi+sag, etc).
12115 //
12116 // One range of globals could be distilled to multiples of 45. We optimize
12117 // those with a loop. Another range was arbitrary. We stack up those values all
12118 // at once, then loop over the stack to pop(), assign, and do the next global.
12119 // We use the freed bytes to reset the 3 items' properties with a subroutine.
12120 //
12121 // Applies to at least: English CD, English floppy, German floppy
12122 // Responsible method: Glory::restart() in script 0
12123 // Fixes bug: #10768
12124 static const uint16 qfg4RestartSignature[] = {
12125 	SIG_MAGICDWORD,
12126 	0x76,                               // push0 (this range is multiples of 45)
12127 	0x35, 0x01,                         // ldi 1d
12128 	0xb1, 0x90,                         // sagi (global[144 + 1] = 0)
12129 	SIG_ADDTOOFFSET(+40),               // ...
12130 	0x38, SIG_UINT16(0x013b),           // pushi 315d
12131 	SIG_ADDTOOFFSET(+2),                // ldi ?? (array index here was typoed)
12132 	0xb1, 0x90,                         // sagi (global[144 + ?] = 315)
12133 
12134 	0x35, 0x14,                         // ldi 20d (this assignment doesn't fit a pattern)
12135 	0xa1, 0xc6,                         // sag global[198]
12136 
12137 	0x35, 0x02,                         // ldi 2d (this range has arbitrary values)
12138 	0xa0, SIG_UINT16(0x016f),           // sag global[367]
12139 	SIG_ADDTOOFFSET(+95),               // ...
12140 	0x35, 0x0a,                         // ldi 10d
12141 	0xa0, SIG_UINT16(0x0183),           // sag global[387]
12142 	SIG_END
12143 };
12144 
12145 static const uint16 qfg4RestartPatch[] = {
12146                                         // (loop to assign multiples of 45)
12147 	0x76,                               // push0
12148 	0xad, 0x00,                         // sst temp[0]
12149                                         //
12150 	0x8d, 0x00,                         // lst temp[0] (global[144 + n+1] = n*45)
12151 	0x35, 0x08,                         // ldi 8d
12152 	0x22,                               // lt?
12153 	0x31, 0x0c,                         // bnt 12d [end the loop]
12154 	0x8d, 0x00,                         // lst temp[0]
12155 	0x35, 0x2d,                         // ldi 45d
12156 	0x06,                               // mul
12157 	0x36,                               // push (temp[0] * 45)
12158 	0xc5, 0x00,                         // +at temp[0]
12159 	0xb1, 0x90,                         // sagi (global[144 + temp[0]])
12160 	0x33, 0xed,                         // jmp -19d (loop)
12161                                         // (that loop freed +30 bytes)
12162 
12163 	0x35, 0x14,                         // ldi 20d (leave this assignment as-is)
12164 	0xa1, 0xc6,                         // sag global[198]
12165 
12166                                         // (stack up arbitrary values; then loop to assign them)
12167 	0x7a,                               // push2     (global[367] = 2)
12168 	0x3c,                               // dup       (global[368] = 2)
12169 	0x39, 0x03,                         // pushi 3d  (global[369] = 3)
12170 	0x3c,                               // dup       (global[370] = 3)
12171 	0x3c,                               // dup       (global[371] = 3)
12172 	0x39, 0x04,                         // pushi 4d  (global[372] = 4)
12173 	0x39, 0x05,                         // pushi 5d  (global[373] = 5)
12174 	0x3c,                               // dup       (global[374] = 5)
12175 	0x39, 0x06,                         // pushi 6d  (global[375] = 6)
12176 	0x39, 0x07,                         // pushi 7d  (global[376] = 7)
12177 	0x39, 0x08,                         // pushi 8d  (global[377] = 8)
12178 	0x3c,                               // dup       (global[378] = 8)
12179 	0x39, 0x05,                         // pushi 5d  (global[379] = 5)
12180 	0x39, 0x0a,                         // pushi 10d (global[380] = 10)
12181 	0x39, 0x0f,                         // pushi 15d (global[381] = 15)
12182 	0x39, 0x14,                         // pushi 20d (global[382] = 20)
12183 	0x39, 0x06,                         // pushi 6d  (global[383] = 6)
12184 	0x39, 0x08,                         // pushi 8d  (global[384] = 8)
12185 	0x39, 0x07,                         // pushi 7d  (global[385] = 7)
12186 	0x39, 0x0a,                         // pushi 10d (global[386] = 10)
12187 	0x3c,                               // dup       (global[387] = 10)
12188                                         //
12189 	0x39, 0x15,                         // pushi 21d (pop() and set, backward from 20 to 0)
12190 	0xad, 0x00,                         // sst temp[0]
12191                                         //
12192 	0xed, 0x00,                         // -st temp[0]
12193 	0x35, 0x00,                         // ldi 0
12194 	0x20,                               // ge?
12195 	0x31, 0x07,                         // bnt 7d [end the loop]
12196 	0x85, 0x00,                         // lat temp[0]
12197 	0xb8, PATCH_UINT16(0x016f),         // ssgi (global[367 + n] = pop())
12198 	0x33, 0xf2,                         // jmp -14d (loop)
12199                                         // (that loop freed +52 bytes)
12200 
12201                                         // (reset properties for a few items)
12202 	0x33, 0x1f,                         // jmp 31d [skip subroutine declaration]
12203 	0x38, PATCH_SELECTOR16(loop),       // pushi loop
12204 	0x78,                               // push1
12205 	0x8f, 0x02,                         // lsp param[2] (loop varies)
12206 	0x38, PATCH_SELECTOR16(cel),        // pushi cel
12207 	0x78,                               // push1
12208 	0x8f, 0x03,                         // lsp param[3] (cel varies)
12209 	0x38, PATCH_SELECTOR16(value),      // pushi value (weight)
12210 	0x78,                               // push1
12211 	0x7a,                               // push2 (these items all weigh 2)
12212                                         //
12213 	0x39, PATCH_SELECTOR8(at),          // pushi at
12214 	0x78,                               // push1
12215 	0x8f, 0x01,                         // lsp param[1]
12216 	0x81, 0x09,                         // global[9] (gloryInv)
12217 	0x4a, PATCH_UINT16(0x0006),         // send 6d
12218                                         //
12219 	0x4a, PATCH_UINT16(0x0012),         // send 18d
12220 	0x48,                               // ret
12221 
12222 	0x39, 0x03,                         // pushi 3d (call has 3 args)
12223 	0x39, 0x1c,                         // pushi 28d (thePiePan)
12224 	0x7a,                               // push2 (loop)
12225 	0x39, 0x0a,                         // pushi 10d (cel)
12226 	0x40, PATCH_UINT16(0xffd5), PATCH_UINT16(0x0006), // call [-43], 6d
12227 
12228 	0x39, 0x03,                         // pushi 3d (call has 3 args)
12229 	0x39, 0x27,                         // pushi 39d (theBroom)
12230 	0x39, 0x0a,                         // pushi 10d (loop)
12231 	0x76,                               // push0 (cel)
12232 	0x40, PATCH_UINT16(0xffc9), PATCH_UINT16(0x0006), // call [-55], 6d
12233 
12234 	0x39, 0x03,                         // pushi 3d (call has 3 args)
12235 	0x39, 0x2c,                         // pushi 44d (theTorch)
12236 	0x39, 0x08,                         // pushi 8d (loop)
12237 	0x39, 0x09,                         // pushi 9d (cel)
12238 	0x40, PATCH_UINT16(0xffbc), PATCH_UINT16(0x0006), // call [-68], 6d
12239 
12240 	0x33, 0x0a,                         // jmp 10d [skip waste bytes]
12241 	PATCH_END
12242 };
12243 
12244 // At the squid monolith (room 800), using the grapnel on the eastern ledge
12245 // disposes hero's scaler to freeze hero's size. A scaler dynamically shrinks
12246 // hero into the horizon as y-pos increases. It makes sense that hero should
12247 // maintain their size while climbing a vertical rope.
12248 //
12249 // Problem: After climbing the rope, both standing on the ledge and back on the
12250 // ground, hero's scaler will remain null. An exception will occur if a script
12251 // calls Prop::setScaler(hero) while hero's scaler is null. Casting Trigger on
12252 // the monolith, from either location, does it. As will climbing down, then
12253 // casting Levitate. Both spells have auras intended to fit hero's size. They
12254 // expect hero to have a scaler, not null.
12255 //
12256 // Ideally the climb script would've swapped in a dummy scaler, then swapped
12257 // the original scaler back upon return to ground level. Implementing that with
12258 // patches would be messy. There's no room to patch setScaler() itself to
12259 // broadly tolerate nulls. That'd avoid exceptions but wouldn't restore normal
12260 // scaling after a climb.
12261 //
12262 // As a last resort, we simply leave the original scaler on hero, erasing the
12263 // setScale() call that would freeze hero's size. The hero shrinks/grows a
12264 // little while climbing as a side effect, but that's barely noticeable.
12265 //
12266 // Applies to at least: English CD, English floppy, German floppy
12267 // Responsible method: sUseTheGrapnel::changeState(5) in script 800
12268 // Fixes bug: #10837
12269 static const uint16 qfg4RopeScalerSignature[] = {
12270 	SIG_MAGICDWORD,
12271 	0x38, SIG_SELECTOR16(setScale),     // pushi setScale
12272 	0x76,                               // push0 (no args, disposes scaler & freezes size)
12273 	SIG_ADDTOOFFSET(+14),               // ...
12274 	0x81, 0x00,                         // lag global[0] (hero)
12275 	0x4a, SIG_UINT16(0x0026),           // send 38d
12276 	SIG_END
12277 };
12278 
12279 static const uint16 qfg4RopeScalerPatch[] = {
12280 	0x35, 0x01,                         // ldi 0 (erase 2 bytes)
12281 	0x35, 0x01,                         // ldi 0 (erase 2 bytes)
12282 	PATCH_ADDTOOFFSET(+14+2),           // ...
12283 	0x4a, PATCH_UINT16(0x0022),         // send 34d
12284 	PATCH_END
12285 };
12286 
12287 // The fortune teller's third reading has the wrong card at the center. The 3rd
12288 // and 4th reading are about different people, yet both have "Queen of Cups".
12289 //
12290 // The 1st reading establishes "Queen of Cups" as: a woman of wisdom and love,
12291 // kind, generous, and virtuous. This fits the 4th reading: she uses her power
12292 // joyfully, giving gracefully and lovingly to others.
12293 //
12294 // The 1st reading establishes "Queen of Swords" as: a deceiver or deceived,
12295 // having suffered through terrible hardship, she faces her sorrows bravely,
12296 // but with deep loneliness. This fits the 3rd reading better: some cruel event
12297 // shaped her life... ambition, self-deception, and she is falling in love.
12298 //
12299 // We change the 3rd reading's center card to "Queen of Swords".
12300 //
12301 // Applies to at least: English CD, English floppy, German floppy
12302 // Responsible method: sThirdReading:changeState(3) in script 475
12303 // Fixes bug: #10824
12304 static const uint16 qfg4Tarot3QueenSignature[] = {
12305 	SIG_MAGICDWORD,
12306 	0x34, SIG_UINT16(0x03f1),           // ldi 1009d ("Queen of Cups")
12307 	0xa3, 0x00,                         // sal local[0]
12308 	SIG_ADDTOOFFSET(+46),               // ...
12309 	0x39, 0x1f,                         // pushi 31d (say: 1 6 31 0 self)
12310 	SIG_END
12311 };
12312 
12313 static const uint16 qfg4Tarot3QueenPatch[] = {
12314 	0x34, PATCH_UINT16(0x03ed),         // ldi 1005d ("Queen of Swords")
12315 	PATCH_END
12316 };
12317 
12318 // The fortune teller's third reading displays a right-turned "The Devil" card,
12319 // but Magda says it is "Death".
12320 //
12321 // We change the card to a right-turned "Death".
12322 //
12323 // Applies to at least: English CD, English floppy, German floppy
12324 // Responsible method: sThirdReading:changeState(15) in script 475
12325 // Fixes bug: #10823
12326 static const uint16 qfg4Tarot3DeathSignature[] = {
12327 	SIG_MAGICDWORD,
12328 	0x34, SIG_UINT16(0x03fa),           // ldi 1018d ("The Devil")
12329 	0xa3, 0x00,                         // sal local[0]
12330 	SIG_ADDTOOFFSET(+46),               // ...
12331 	0x39, 0x23,                         // pushi 35d (say: 1 6 35 0 self)
12332 	SIG_END
12333 };
12334 
12335 static const uint16 qfg4Tarot3DeathPatch[] = {
12336 	0x34, PATCH_UINT16(0x03fd),         // ldi 1021d ("Death")
12337 	PATCH_END
12338 };
12339 
12340 // The fortune teller's third reading places the "Two of Cups" across another
12341 // card, off-center. That View (1023) is cropped (130x64). All other horizontal
12342 // cards (130x86, 130x87) are padded at the bottom with transparent pixels.
12343 //
12344 // A utility script (sShowCard) is scheduled to create each card with a given
12345 // View (passed as local[0]) and move it onto a given pile (passed as local[2]:
12346 // center, west, south, east, north). It has a switch block deciding x,y coords
12347 // depending on the pile.
12348 //
12349 // We optimize a couple cases by consolidating their common code in a
12350 // subroutine to make room. The rewritten case for the east pile checks if the
12351 // requested card was "Two of Cups". If so, a special Y value is used.
12352 //
12353 // Applies to at least: English CD, English floppy, German floppy
12354 // Responsible method: sShowCard::changeState() in script 475
12355 // Fixes bug: #10822
12356 static const uint16 qfg4Tarot3TwoOfCupsSignature[] = {
12357 	SIG_MAGICDWORD,
12358 	0x3c,                               // dup
12359 	0x35, 0x04,                         // ldi 4d (case 4)
12360 	0x1a,                               // eq?
12361 	SIG_ADDTOOFFSET(+7),                // ...
12362 	0x38, SIG_SELECTOR16(setStep),      // pushi setStep
12363 	SIG_ADDTOOFFSET(+11),               // ...
12364 	0x51, SIG_ADDTOOFFSET(+1),          // class Scaler
12365 	SIG_ADDTOOFFSET(+16),               // ...
12366 	0x51, SIG_ADDTOOFFSET(+1),          // class MoveTo
12367 	SIG_ADDTOOFFSET(+72),               // ...
12368 	0x3a,                               // toss (end of this local[2] switch)
12369 	0x32, SIG_ADDTOOFFSET(+2),          // jmp [end the state switch]
12370 	SIG_END
12371 };
12372 
12373 static const uint16 qfg4Tarot3TwoOfCupsPatch[] = {
12374 	0x33, 0x31,                         // jmp 49d [skip subroutine declaration]
12375 	0x38, PATCH_SELECTOR16(moveSpeed),  // pushi moveSpeed
12376 	0x78,                               // push1
12377 	0x76,                               // push0
12378 	0x38, PATCH_SELECTOR16(setStep),    // pushi setStep
12379 	0x7a,                               // push2
12380 	0x39, 0x1e,                         // pushi 30d
12381 	0x39, 0x0a,                         // pushi 10d
12382 	0x38, PATCH_SELECTOR16(setScaler),  // pushi setScaler
12383 	0x39, 0x05,                         // pushi 5d
12384 	0x51, PATCH_GETORIGINALBYTE(26),    // class Scaler
12385 	0x36,                               // push
12386 	0x39, 0x64,                         // pushi 100d
12387 	0x39, 0x23,                         // pushi 35d
12388 	0x38, PATCH_UINT16(0x0096),         // pushi 150d
12389 	0x8f, 0x01,                         // lsp param[1] (setScalar, arg 5 varies)
12390 	0x38, PATCH_SELECTOR16(setMotion),  // pushi setMotion
12391 	0x39, 0x04,                         // pushi 4d
12392 	0x51, PATCH_GETORIGINALBYTE(44),    // class MoveTo
12393 	0x36,                               // push
12394 	0x8f, 0x02,                         // lsp param[2] (setMotion, x arg varies)
12395 	0x8f, 0x03,                         // lsp param[3] (setMotion, y arg varies)
12396 	0x7c,                               // pushSelf
12397 	0x83, 0x01,                         // lal local[1]
12398 	0x4a, PATCH_UINT16(0x0028),         // send 40d
12399 	0x48,                               // ret
12400 
12401 	0x3c,                               // dup
12402 	0x35, 0x04,                         // ldi 4d (case 4)
12403 	0x1a,                               // eq?
12404 	0x31, 0x1b,                         // bnt 27d [next case]
12405 	0x39, 0x03,                         // pushi 3d (call has 3 args)
12406 	0x39, 0x6e,                         // pushi 110d (setScalar, arg 5)
12407 	0x38, PATCH_UINT16(0x00d2),         // pushi 210d (setMotion, x arg)
12408                                         //
12409 	0x8b, 0x00,                         // lsl local[0] (test the card's View number)
12410 	0x34, PATCH_UINT16(0x03ff),         // ldi 1023d ("Two of Cups" is special)
12411 	0x1a,                               // eq?
12412 	0x31, 0x04,                         // bnt 4d [regular y arg]
12413 	0x39, 0x66,                         // pushi 102d  (setMotion, special y arg)
12414 	0x33, 0x02,                         // jmp 2d [to the call]
12415 	0x39, 0x6e,                         // pushi 110d (setMotion, regular y arg)
12416                                         //
12417 	0x41, 0xb0, PATCH_UINT16(0x0006),   // call [-80], 6d
12418 	0x33, 0x13,                         // jmp 19d [end the local[2] switch]
12419 
12420 	0x3c,                               // dup
12421 	0x35, 0x05,                         // ldi 5d (case 5)
12422 	0x1a,                               // eq?
12423 	0x31, 0x0d,                         // bnt 13d [end the local[2] switch]
12424 	0x39, 0x03,                         // pushi 3d (call has 3 args)
12425 	0x39, 0x32,                         // pushi 50d (setScalar, arg 5)
12426 	0x38, PATCH_UINT16(0x0090),         // pushi 144d (setMotion, x arg)
12427 	0x39, 0x32,                         // pushi 50d (setMotion, y arg)
12428 	0x41, 0x9b, PATCH_UINT16(0x0006),   // call [-101], 6d
12429 
12430 	0x33, 0x0c,                         // jmp 12d [skip to the original toss that ends this switch]
12431 	PATCH_END
12432 };
12433 
12434 // The fortune teller's third reading places horizontal cards on top of
12435 // vertical ones. Some then fall *through* the vertical card to the bottom.
12436 //
12437 // A utility script (sShowCard) creates each card and moves it onto a pile
12438 // (center, west, south, east, north). Then the card is assigned a priority
12439 // with setPri(). Generally these piles are two cards deep. Center has one.
12440 //
12441 // Every pile ought to start with priority 0 and increment thereafter. Somebody
12442 // mixed up the setPri() sequence. We change 0;1,0;1,0 to 0;0,1;0,1.
12443 //
12444 // Applies to at least: English CD, English floppy, German floppy
12445 // Responsible method: sThirdReading:changeState() in script 475
12446 // Fixes bug: #10845
12447 static const uint16 qfg4Tarot3PrioritySignature[] = {
12448 	0x78,                               // push1 (setPri: 1, "Eight of Swords", West, V)
12449 	SIG_ADDTOOFFSET(+14),               // ...
12450 	0x39, 0x20,                         // pushi 32d (say cond:32)
12451 	SIG_ADDTOOFFSET(+68),               // ...
12452 	0x76,                               // push0 (setPri: 0, "Strength", West, H)
12453 	SIG_ADDTOOFFSET(+84),               // ...
12454 	0x78,                               // push1 (setPri: 1, "The Magician", South, V)
12455 	SIG_ADDTOOFFSET(+84),               // ...
12456 	0x76,                               // push0 (setPri: 0, "Death", South, H)
12457 	SIG_ADDTOOFFSET(+12),               // ...
12458 	SIG_MAGICDWORD,
12459 	0x39, 0x06,                         // pushi 6d (say verb:6)
12460 	0x39, 0x23,                         // pushi 36d (say cond:35)
12461 	SIG_END
12462 };
12463 
12464 static const uint16 qfg4Tarot3PriorityPatch[] = {
12465 	0x76,                               // push0 (setPri: 0, "Eight of Swords", West, V)
12466 	PATCH_ADDTOOFFSET(+84),             // ...
12467 	0x78,                               // push1 (setPri: 1, "Strength", West, H)
12468 	PATCH_ADDTOOFFSET(+84),             // ...
12469 	0x76,                               // push0 (setPri: 0, "The Magician", South, V)
12470 	PATCH_ADDTOOFFSET(+84),             // ...
12471 	0x78,                               // push1 (setPri: 1, "Death", South, H)
12472 	PATCH_END
12473 };
12474 
12475 // The fortune teller's fifth reading is unusual. It places all cards at the
12476 // center pile, periodically fading out to clear the table. When Magda
12477 // talks about the Sense Ritual, the "Six of Swords" (view 1048) sinks below
12478 // the previous card.
12479 //
12480 // A shared utility script that creates and places cards (sSetTheSignificator)
12481 // uses priority 12 as it deals, after which each card is given a lower value
12482 // with setPri(). "The Falling Tower" (view 1031) did *not* get a new priority.
12483 // Thus "Six of Swords", when given priority 1, sinks below 12.
12484 //
12485 // "Six of Swords" is the last card before a fade. We simply leave its priority
12486 // at 12 as well. Being the most recent card, it will be on top. No worry
12487 // about covering a subsequent card because the table will be cleared.
12488 //
12489 // Applies to at least: English CD, English floppy, German floppy
12490 // Responsible method: sFifthReading:changeState(32) in script 475
12491 // Fixes bug: #10846
12492 static const uint16 qfg4Tarot5PrioritySignature[] = {
12493 	0x39, SIG_ADDTOOFFSET(+1),          // pushi setPri
12494 	0x78,                               // push1
12495 	0x78,                               // push1 (setPri: 1, "Six of Swords")
12496 	SIG_MAGICDWORD,
12497 	0x83, 0x01,                         // lal local[1] (card obj)
12498 	0x4a, SIG_UINT16(0x0006),           // send 6d
12499 	SIG_ADDTOOFFSET(+9),                // ...
12500 	0x39, 0x44,                         // pushi 68d (say cond:68)
12501 	SIG_END
12502 };
12503 
12504 static const uint16 qfg4Tarot5PriorityPatch[] = {
12505 	0x33, 0x07,                         // jmp 7d [skip the setPri() send]
12506 	PATCH_END
12507 };
12508 
12509 // When crossing the cave's tightrope in room 710, a tentacle emerges, and then
12510 // its animation freezes - in ScummVM, not the original interpreter. This
12511 // happens because of an extraneous argument passed to setCycle().
12512 //
12513 // (tentacle setCycle: RandCycle tentacle)
12514 //
12515 // RandCycle can accept an optional arg, but it expects a number, not an
12516 // object. ScummVM doesn't catch the faulty arithmetic and behaves abnormally.
12517 // We remove the bad arg.
12518 //
12519 // Applies to at least: English CD, English floppy, German floppy
12520 // Responsible method: sTentacleDeath::changeState(3) in script 710
12521 // Fixes bug: #10615
12522 static const uint16 qfg4TentacleWriggleSignature[] = {
12523 	SIG_MAGICDWORD,
12524 	0x38, SIG_SELECTOR16(setCycle),     // pushi setCycle
12525 	0x7a,                               // push2
12526 	0x51, SIG_ADDTOOFFSET(+1),          // class RandCycle
12527 	0x36,                               // push
12528 	0x72, SIG_ADDTOOFFSET(+2),          // loffsa tentacle
12529 	0x36,                               // push
12530 	0x72, SIG_ADDTOOFFSET(+2),          // loffsa tentacle
12531 	0x4a, SIG_UINT16(0x0008),           // send 8d
12532 	SIG_END
12533 };
12534 
12535 static const uint16 qfg4TentacleWrigglePatch[] = {
12536 	PATCH_ADDTOOFFSET(+3),
12537 	0x78,                               // push1 (1 setCycle arg)
12538 	PATCH_ADDTOOFFSET(+3),              // ...
12539 	0x35, 0x00,                         // ldi 0 (erase 2 bytes)
12540 	0x35, 0x00,                         // ldi 0 (erase 2 bytes)
12541 	PATCH_ADDTOOFFSET(+3),              // ...
12542 	0x4a, PATCH_UINT16(0x0006),         // send 6d
12543 	PATCH_END
12544 };
12545 
12546 // When crossing the cave's tightrope in room 710, a tentacle emerges. The
12547 // tentacle is supposed to reach a state where it waits indefinitely for an
12548 // external cue() to retract. If the speed slider is too high, a fighter
12549 // reaches the other side and sends a cue() before the tentacle is ready to
12550 // receive it. The tentacle never retracts.
12551 //
12552 // The fighter script (crossByHand) drains stamina in state 2 as hero moves
12553 // across. A slower speed would cost extra stamina. We add a delay after that
12554 // part is over, in state 3, just as hero is about to dismount. When state 4
12555 // cues, the tentacle script (sTentacleDeath) will be ready (state 3).
12556 //
12557 // To create that delay we set the "cycles" property for a countdown and remove
12558 // all other advancement mechanisms. State 3 had a cue from say() and a
12559 // self-cue(). The former's "self" arg becomes null. The latter is erased.
12560 //
12561 // Crossing from the left (crossByHandLeft) doesn't require fixing.
12562 //
12563 // This patch doesn't apply to the NRS version which ships with the GOG release
12564 //  as it throttles the frequency of crossByHand:doit which fixes the bug.
12565 //
12566 // Applies to at least: English CD, English floppy, German floppy
12567 // Responsible method: crossByHand::changeState(3) in script 710
12568 // Fixes bug: #10615
12569 static const uint16 qfg4PitRopeFighterSignature[] = {
12570 	0x65, SIG_ADDTOOFFSET(+1),          // aTop state
12571 	SIG_ADDTOOFFSET(+269),              // ...
12572 	0x31, 0x1e,                         // bnt 30d (set a flag and say() on 1st crossing, else cue)
12573 	SIG_ADDTOOFFSET(+8),                // ...
12574 	0x38, SIG_SELECTOR16(say),          // pushi say ("You just barely made it")
12575 	0x38, SIG_UINT16(0x0005),           // pushi 5d
12576 	0x39, 0x0a,                         // pushi 10d
12577 	0x39, 0x06,                         // pushi 6d
12578 	0x39, 0x20,                         // pushi 32d
12579 	0x76,                               // push0
12580 	0x7c,                               // pushSelf
12581 	SIG_ADDTOOFFSET(+5),                // ...
12582 	0x32, SIG_ADDTOOFFSET(+2),          // jmp ?? [end the switch]
12583 	SIG_MAGICDWORD,
12584 	0x38, SIG_SELECTOR16(cue),          // pushi cue
12585 	0x76,                               // push0
12586 	0x54, SIG_UINT16(0x0004),           // self 4d
12587 	0x32, SIG_ADDTOOFFSET(+2),          // jmp ?? [end the switch]
12588 	SIG_END
12589 };
12590 
12591 static const uint16 qfg4PitRopeFighterPatch[] = {
12592 	PATCH_ADDTOOFFSET(+271),            // ... (2 + 269)
12593 	0x31, 0x1b,                         // bnt 26d [skip the say w/o ending the switch]
12594 	PATCH_ADDTOOFFSET(+21),             // ... (8 + 13)
12595 	0x76,                               // push0 (null caller, so say won't cue crossByHand)
12596 	PATCH_ADDTOOFFSET(+5),              // ...
12597                                         // (no jmp, self-cue becomes cycles)
12598 	0x35, 0x20,                         // ldi 32d
12599 	0x65, PATCH_GETORIGINALBYTEADJUST(1, +6), // aTop cycles (property offset = @state + 6d)
12600 	0x33, 0x04,                         // jmp 4d [skip waste bytes, end the switch]
12601 	PATCH_END
12602 };
12603 
12604 // As above, mages at high speed can get across the pit in room 710 before
12605 // tentacle is ready to receive a cue().
12606 //
12607 // As luck would have it, hero's speed is cached and restored. Twice actually.
12608 //
12609 // Overview of the mage script (sLevitateOverPit)
12610 //  State 1-3:  Cache hero's slider-based speed.
12611 //              Set a temporary speed as hero unfurls the cloth.
12612 //              Restore the original value.
12613 //              Call handsOn(). Wait for an external cue().
12614 //  State 4:    Call handsOff().
12615 //              If cued by the Levitate spell (script 21), go to 5-7.
12616 //              A plain cue() from anywhere else, leads to state 8.
12617 //  State 5-7:  Move across the pit. Skip to state 9.
12618 //  State 8:    An abort message.
12619 //  State 9-10: Cache hero's speed again.
12620 //              Set a temporary speed as hero folds up the cloth.
12621 //              Restore the original value, and normalize hero. Call handsOn().
12622 //
12623 // Patch 1: We overwrite some derelict code in state 5, caching the
12624 // slider-based speed again, in case the player adjusted it before casting
12625 // Levitate, then setting a fixed speed of our own for the crossing.
12626 //
12627 // Patch 2: Patch 1 already cached and clobbered the speed. We remove the
12628 // original attempt to cache again in state 9.
12629 //
12630 // The result is caching/restoration at the beginning, aborting or caching and
12631 // crossing with our fixed value, and a restoration at the end (whichever value
12632 // was last cached). The added travel time has no side effect for mages.
12633 //
12634 // Mages have no other script to levitate across from left to right. At some
12635 // point in development, the meaning of "register" changed. The derelict
12636 // state 5 code thought 0/1 meant move right/left. Whereas state 4 decides 0/1
12637 // means abort/cross, only ever moving left. The rightward MoveTo never runs.
12638 //
12639 // We also include a version of this for the instruction sizes in the NRS patch,
12640 //  which is important as that ships with the GOG version.
12641 //
12642 // Applies to at least: English CD, English floppy, German floppy
12643 // Responsible method: sLevitateOverPit::changeState(5) in script 710
12644 // Fixes bug: #10615
12645 static const uint16 qfg4PitRopeMageSignature1[] = {
12646 	0x30, SIG_UINT16(0x0017),           // bnt 23d [if register == 0 (never), move right]
12647 	SIG_ADDTOOFFSET(+20),               // ... (move left)
12648 	0x32, SIG_ADDTOOFFSET(+2),          // jmp ?? [end the switch]
12649 
12650 	0x38, SIG_SELECTOR16(setMotion),    // pushi setMotion (move right)
12651 	0x38, SIG_UINT16(0x0004),           // pushi 4d
12652 	0x51, SIG_ADDTOOFFSET(+1),          // class MoveTo
12653 	0x36,                               // push
12654 	SIG_MAGICDWORD,
12655 	0x38, SIG_UINT16(0x00da),           // pushi 218d
12656 	0x39, 0x30,                         // pushi 48d
12657 	0x7c,                               // pushSelf
12658 	0x81, 0x00,                         // lag global[0] (hero)
12659 	0x4a, SIG_UINT16(0x000c),           // send 12d
12660 	0x32, SIG_ADDTOOFFSET(+2),          // jmp ?? [end the switch]
12661 	SIG_END
12662 };
12663 
12664 static const uint16 qfg4PitRopeMagePatch1[] = {
12665 	0x34, PATCH_UINT16(0x0000),         // ldi 0 (erase the branch)
12666 	PATCH_ADDTOOFFSET(+20),             // ...
12667 
12668 	0x38, PATCH_SELECTOR16(cycleSpeed), // pushi cycleSpeed
12669 	0x76,                               // push0
12670 	0x81, 0x00,                         // lag global[0] (hero)
12671 	0x4a, PATCH_UINT16(0x0004),         // send 4d
12672 	0xa3, 0x02,                         // sal local[2] (cache again)
12673 	                                    //
12674 	0x38, PATCH_SELECTOR16(setSpeed),   // pushi setSpeed
12675 	0x78,                               // push1
12676 	0x39, 0x08,                         // pushi 8d (set our fixed speed)
12677 	0x81, 0x00,                         // lag global[0] (hero)
12678 	0x4a, PATCH_UINT16(0x0006),         // send 6d
12679 	0x5c,                               // selfID (erase 1 byte to keep disasm aligned)
12680 	PATCH_END
12681 };
12682 
12683 static const uint16 qfg4PitRopeMageNrsSignature1[] = {
12684 	0x30, SIG_UINT16(0x0016),           // bnt 22d [if register == 0 (never), move right]
12685 	SIG_ADDTOOFFSET(+19),               // ... (move left)
12686 	0x32, SIG_ADDTOOFFSET(+2),          // jmp ?? [end the switch]
12687 
12688 	0x38, SIG_SELECTOR16(setMotion),    // pushi setMotion (move right)
12689 	0x39, 0x04,                         // pushi 4d
12690 	0x51, SIG_ADDTOOFFSET(+1),          // class MoveTo
12691 	0x36,                               // push
12692 	SIG_MAGICDWORD,
12693 	0x38, SIG_UINT16(0x00da),           // pushi 218d
12694 	0x39, 0x30,                         // pushi 48d
12695 	0x7c,                               // pushSelf
12696 	0x81, 0x00,                         // lag global[0] (hero)
12697 	0x4a, SIG_UINT16(0x000c),           // send 12d
12698 	0x32, SIG_ADDTOOFFSET(+2),          // jmp ?? [end the switch]
12699 	SIG_END
12700 };
12701 
12702 static const uint16 qfg4PitRopeMageNrsPatch1[] = {
12703 	0x34, PATCH_UINT16(0x0000),         // ldi 0 (erase the branch)
12704 	PATCH_ADDTOOFFSET(+19),             // ...
12705 
12706 	0x38, PATCH_SELECTOR16(cycleSpeed), // pushi cycleSpeed
12707 	0x76,                               // push0
12708 	0x81, 0x00,                         // lag global[0] (hero)
12709 	0x4a, PATCH_UINT16(0x0004),         // send 4d
12710 	0xa3, 0x02,                         // sal local[2] (cache again)
12711 	                                    //
12712 	0x38, PATCH_SELECTOR16(setSpeed),   // pushi setSpeed
12713 	0x78,                               // push1
12714 	0x39, 0x08,                         // pushi 8d (set our fixed speed)
12715 	0x81, 0x00,                         // lag global[0] (hero)
12716 	0x4a, PATCH_UINT16(0x0006),         // send 6d
12717 	PATCH_END
12718 };
12719 
12720 // Responsible method: sLevitateOverPit::changeState(9) in script 710
12721 static const uint16 qfg4PitRopeMageSignature2[] = {
12722 	SIG_MAGICDWORD,
12723 	0x35, 0x09,                         // ldi 9d (case 9 label)
12724 	0x38, SIG_SELECTOR16(cycleSpeed),   // pushi cycleSpeed
12725 	SIG_ADDTOOFFSET(+6),                // ...
12726 	0xa3, 0x02,                         // sal local[2] (original re-cache)
12727 	SIG_ADDTOOFFSET(+48),               // ...
12728 	0x38, SIG_SELECTOR16(cycleSpeed),   // pushi cycleSpeed
12729 	0x78,                               // push1
12730 	0x8b, 0x02,                         // lsl local[2] (restore cached speed)
12731 	SIG_END
12732 };
12733 
12734 static const uint16 qfg4PitRopeMagePatch2[] = {
12735 	PATCH_ADDTOOFFSET(+11),             // (don't cache our fixed speed)
12736 	0x35, 0x00,                         // ldi 0 (erase 2 bytes)
12737 	PATCH_ADDTOOFFSET(+48),             // ...
12738 	0x38, PATCH_SELECTOR16(setSpeed),   // pushi setSpeed (keep cycleSpeed & moveSpeed sync'd)
12739 	PATCH_END
12740 };
12741 
12742 // WORKAROUND: Script needed, because of differences in our pathfinding
12743 // algorithm.
12744 // When entering forest room 557 from the east (563), hero is supposed to move
12745 // only a short distance into the room. ScummVM's pathfinding sends hero off
12746 // course, to the middle of the room and back.
12747 //
12748 // There's an unwalkable stream in the SE corner, and hero's coords were within
12749 // its polygon. We lower the top two points to keep hero on the outside.
12750 //
12751 // Applies to at least: English CD, English floppy, German floppy
12752 // Responsible method: rm557::init() in script 557
12753 // Fixes bug: #10857
12754 static const uint16 qfg4Forest557PathfindingSignature[] = {
12755 	SIG_MAGICDWORD,
12756 	0x38, SIG_UINT16(0x0119),           // pushi 281d (point 3)
12757 	0x38, SIG_UINT16(0x0087),           // pushi 135d
12758 	0x38, SIG_UINT16(0x013f),           // pushi 319d (point 4)
12759 	0x38, SIG_UINT16(0x0087),           // pushi 135d
12760 	SIG_END
12761 };
12762 
12763 static const uint16 qfg4Forest557PathfindingPatch[] = {
12764 	PATCH_ADDTOOFFSET(+3),
12765 	0x38, PATCH_UINT16(0x0089),         // pushi 137d
12766 	PATCH_ADDTOOFFSET(+3),
12767 	0x38, PATCH_UINT16(0x0089),         // pushi 137d
12768 	PATCH_END
12769 };
12770 
12771 // The Trigger spell stalls and never reaches handsOn when preceded by a
12772 // successful Summon Staff. An IF block calls hero::setCycle(Beg, self), which
12773 // cues self on completion. Its condition tests hero's "view" property and
12774 // executes if the staff is absent. There are no means to advance when the
12775 // staff is present.
12776 //
12777 // Open (script 13) is largely identical w/o this bug. We match its behavior.
12778 //
12779 // Due to differences between editions, this is addressed with two patches. The
12780 // first inserts a "seconds" property assignment before the IF, where it'll
12781 // always cue. We make room by condensing the IF conditions. There are two
12782 // "view" comparisons. Instead of sending for it twice, we recycle the view
12783 // with pprev. The second patch removes setCycle's cue by nulling its last arg.
12784 //
12785 // Applies to at least: English CD, English floppy, German floppy
12786 // Responsible method: castTriggerScript::changeState(2) in script 11
12787 // Fixes bug: #10860
12788 static const uint16 qfg4TriggerStaffSignature1[] = {
12789 	0x65, SIG_ADDTOOFFSET(+1),          // aTop register
12790 
12791 	SIG_ADDTOOFFSET(+9),                // ... (send for hero's view, push for comparison)
12792 	0x35, 0x11,                         // ldi 17d
12793 	0x1e,                               // gt?
12794 	0x31, SIG_ADDTOOFFSET(+1),          // bnt ?? [to the not]
12795 	SIG_ADDTOOFFSET(+9),                // ... (send for hero's view and push again)
12796 	SIG_MAGICDWORD,
12797 	0x35, 0x15,                         // ldi 21d
12798 	0x22,                               // lt?
12799 	0x31, SIG_ADDTOOFFSET(+1),          // bnt ?? [to the not]
12800 	//0x33, 0x00,                       // (Floppy has a jmp 0 here)
12801 	//0x18,                             // not ( !(view > 17 && view < 21) )
12802 	SIG_END
12803 };
12804 
12805 static const uint16 qfg4TriggerStaffPatch1[] = {
12806 	PATCH_ADDTOOFFSET(+2),                    // (free bytes later, use them up here)
12807 	0x35, 0x03,                               // ldi 3d
12808 	0x65, PATCH_GETORIGINALBYTEADJUST(1, -8), // aTop seconds (property offset = @register - 8d)
12809 	0x35, 0x00,                         // ldi 0 (waste 2 bytes)
12810 	0x34, PATCH_UINT16(0x0000),         // ldi 0 (waste 3 bytes)
12811 
12812 	0x39, PATCH_SELECTOR8(view),        // pushi view
12813 	0x76,                               // push0
12814 	0x81, 0x00,                         // lag global[0] (hero)
12815 	0x4a, PATCH_UINT16(0x0004),         // send 4d
12816                                         //
12817 	0x39, 0x11,                         // pushi 17d (push the literal, leave view in acc)
12818 	0x22,                               // lt? (view > 17 becomes 17 < view, set prev = acc)
12819 	0x31, PATCH_GETORIGINALBYTEADJUST(15, -8), // bnt ?? [to the not]
12820                                         //
12821 	0x60,                               // pprev (push the view from prev, ldi 21 comparison is next)
12822 	PATCH_END
12823 };
12824 
12825 static const uint16 qfg4TriggerStaffSignature2[] = {
12826 	SIG_MAGICDWORD,
12827 	0x31, 0x0d,                         // bnt 13d [conditions failed, skip the send]
12828 	0x38, SIG_SELECTOR16(setCycle),     // pushi setCycle
12829 	0x7a,                               // push2
12830 	0x51, SIG_ADDTOOFFSET(+1),          // class Beg
12831 	0x36,                               // push
12832 	0x7c,                               // pushSelf (caller arg is cued afterward)
12833 	SIG_END
12834 };
12835 
12836 static const uint16 qfg4TriggerStaffPatch2[] = {
12837 	PATCH_ADDTOOFFSET(+9),
12838 	0x76,                               // push0 (null caller arg, no cue)
12839 	PATCH_END
12840 };
12841 
12842 // The Open and Trigger spells init a green Prop for their effect. They don't
12843 // dispose it the first time, and the effect is absent on further castings.
12844 //
12845 // The author specifically nerfed dispose() on its first call by testing if a
12846 // variable has been set before allowing super::dispose(). We erase the
12847 // branch to ensure that the effect is always disposed.
12848 //
12849 // Applies to at least: English CD, English floppy, German floppy
12850 // Responsible method: triggerEffect::dispose() in script 11
12851 //                     openEffect::dispose() in script 13
12852 // Fixes bug: #10860
12853 static const uint16 qfg4EffectDisposalSignature[] = {
12854 	0x83, SIG_ADDTOOFFSET(+1),          // lal local[?] (0 on first call, 1 thereafter)
12855 	SIG_MAGICDWORD,
12856 	0x31, 0x0a,                         // bnt 10d [skip super::dispose()]
12857 	0x38, SIG_SELECTOR16(dispose),      // pushi dispose
12858 	0x76,                               // push0
12859 	0x57, SIG_ADDTOOFFSET(+1), SIG_UINT16(0x0004), // super Prop, 4d
12860 	0x33, 0x04,                         // jmp 4d [ret]
12861 
12862 	0x35, 0x01,                         // ldi 1d (enable normal disposal)
12863 	0xa3, SIG_ADDTOOFFSET(+1),          // sal local[?]
12864 	SIG_END
12865 };
12866 
12867 static const uint16 qfg4EffectDisposalPatch[] = {
12868 	PATCH_ADDTOOFFSET(+2),
12869 	0x35, 0x00,                         // ldi 0 (erase the branch to always dispose)
12870 	PATCH_END
12871 };
12872 
12873 // After hero is geas'd in the dungeon (room 670) and teleported to the gate
12874 // (600), hero can walk through the closed gate and exit north to the castle
12875 // entrance. Two IF blocks with inconsistent conditions decide whether the
12876 // gate is open and whether to use a polygon that extends beyond the gate.
12877 // When re-entering from the forest (552), the gate is only open and passable
12878 // if hero is qualified.
12879 //
12880 // The room has distinct situations for merely teleporting from the dungeon
12881 // (local[0] = 11) and for entering from the forest while geas'd and carrying
12882 // all the ritual scrolls (local[0] = 10). The latter sets a vital plot flag.
12883 // Adding those checks and the flag to the former, plus opening the gate,
12884 // would be non-trivial.
12885 //
12886 // We edit the polygon's IF condition to remove the dungeon check, making the
12887 // closed gate impassable so hero will have to return from the forest.
12888 //
12889 // Applies to at least: English CD, English floppy, German floppy
12890 // Responsible method: rm600::init() in script 600
12891 // Fixes bug: #10871
12892 static const uint16 qfg4DungeonGateSignature[] = {
12893 	0x39, 0x05,                         // pushi 5d (5 call args)
12894 	0x89, 0x0c,                         // lsg global[12] (needle value)
12895 	SIG_MAGICDWORD,
12896 	0x38, SIG_UINT16(0x029e),           // pushi 670 (Dungeon)
12897 	0x38, SIG_UINT16(0x032a),           // pushi 810 (Combat)
12898 	0x38, SIG_UINT16(0x0262),           // pushi 610 (Castle entrance)
12899 	0x38, SIG_UINT16(0x0276),           // pushi 630 (Great hall)
12900 	0x46, SIG_UINT16(0xfde7), SIG_UINT16(0x0005), SIG_UINT16(0x000a), // calle [export 5 of script 64999], 10d (is needle in haystack?)
12901 	SIG_END
12902 };
12903 
12904 static const uint16 qfg4DungeonGatePatch[] = {
12905 	0x39, 0x04,                         // pushi 4d (4 call args)
12906 	PATCH_ADDTOOFFSET(+2),              // ...
12907 	0x34, PATCH_UINT16(0x0000),         // ldi 0 (erase the Dungeon arg)
12908 	PATCH_ADDTOOFFSET(+9),              // ...
12909 	0x46, PATCH_UINT16(0xfde7), PATCH_UINT16(0x0005), PATCH_UINT16(0x0008), // calle [export 5 of script 64999], 8d (is needle in haystack?)
12910 	PATCH_END
12911 };
12912 
12913 // In the room (644) attached to the lower door of the bat-infested stairway,
12914 // a rogue will get stuck when attempting to open either door. Unlike in other
12915 // castle rooms, the door Tellers here aren't arranging to be cued after the
12916 // "It won't budge" message. Without the cue, a Teller won't clean() and return
12917 // control to the player.
12918 //
12919 // We follow the style of other rooms and replace gloryMessager::say() with
12920 // super::sayMessage(), which implicitly cues.
12921 //
12922 // Applies to at least: English CD, English floppy, German floppy
12923 // Responsible method: leftDoorTeller::sayMessage(), rightDoorTeller::sayMessage() in script 644
12924 // Fixes bug: #10874
12925 static const uint16 qfg4StuckDoorSignature[] = {
12926 	0x38, SIG_SELECTOR16(say),          // pushi say
12927 	0x38, SIG_UINT16(0x0006),           // pushi 6d
12928 	0x39, 0x03,                         // pushi 3d
12929 	SIG_MAGICDWORD,
12930 	0x39, 0x06,                         // pushi 6d
12931 	0x39, 0x09,                         // pushi 9d
12932 	0x78,                               // push1
12933 	0x76,                               // push0
12934 	0x38, SIG_UINT16(0x0280),           // pushi 640d
12935 	0x81, 0x5b,                         // lag global[91]
12936 	0x4a, SIG_UINT16(0x0010),           // send 16d
12937 	SIG_ADDTOOFFSET(+89),               // ...
12938 	0x57, SIG_ADDTOOFFSET(+1), SIG_UINT16(0x0004), // super Teller, 4d
12939 	SIG_END
12940 };
12941 
12942 static const uint16 qfg4StuckDoorPatch[] = {
12943 	0x38, PATCH_SELECTOR16(sayMessage), // pushi sayMessage
12944 	0x39, 0x03,                         // pushi 3d
12945 	0x3c,                               // dup
12946 	0x39, 0x06,                         // pushi 6d
12947 	0x39, 0x09,                         // pushi 9d
12948 	0x59, 0x01,                         // &rest 1d
12949 	0x57, PATCH_GETORIGINALBYTE(112), PATCH_UINT16(0x000a), // super Teller, 10d
12950 	0x32, PATCH_UINT16(0x0003),         // jmp 3d [skip waste bytes]
12951 	PATCH_END
12952 };
12953 
12954 // In the thieves' guild (room 430), the tunnel is not immediately walkable
12955 // when it is revealed (by moving a barrel and solving a puzzle). Hero must
12956 // re-enter the room to update the polygon.
12957 //
12958 // Curing Chief *will* immediately replace the polygon. However, most players
12959 // will lack the item necessary on the first visit. Meeting Chief is how they
12960 // learn about the item. If they go get it, they'll re-enter the room.
12961 //
12962 // The room's init has a cond block to check plot flags and declare one of 3
12963 // polygons. The 3rd condition also inits secritExit, an invisible Feature
12964 // that sends hero out of town when walked upon.
12965 //
12966 // Patch 1: Other patches ensure the passage will be walkable the moment it is
12967 //  revealed. Chief is standing inside it. We skip the original code that would
12968 //  set up the passage as he gets cured. It is redundant now. If hero can reach
12969 //  him, the passage is already revealed. We won't let secritExit init twice.
12970 //
12971 // Patch 2: We free bytes in rm340::init() by condensing Feature inits with a
12972 //  loop. Stack up their addresses. Pop & send repeatedly. Then we declare a
12973 //  subroutine that disposes any existing obstacles, jumps into the cond block
12974 //  to declare the 3rd poly, jumps back, passes it to addObstacles(), and inits
12975 //  secritExit.
12976 //
12977 //  When the cond block's 3rd condition runs, we immediately call our
12978 //  subroutine to do everything and end the cond, leaving the original polygon
12979 //  declaration intact below the jump.
12980 //
12981 // Patch 3: The passage starts opening at sBarrelMove state 8. We need more
12982 //  room than case 8 can offer, so we arrange for *multiple* cases to run
12983 //  during state 8 - by omitting the final jump that would short-circuit.
12984 //
12985 //  Cases 1-5 have derelict code, once intended to move the barrel back and
12986 //  forth, now only left. This is because barrel::doVerb(4) schedules
12987 //  sBarrelMove in the absence of flag 254 and sets register=1 if the barrel is
12988 //  in the left position already. Case 0 uses the same criteria in deciding to
12989 //  skip to state 6. Thus cases 1-5 never see register==1. The barrel never
12990 //  moves back, and bytes predicated on register==1 are available.
12991 //
12992 //  We reduce case 2 to only the necessary ops and splice in a new case that
12993 //  runs during state 8 as a prologue to the original case 8. Our prologue
12994 //  calls the subroutine to add the 3rd polygon. This patch has two variants,
12995 //  toggled to match the detected edition with enablePatch() below. Aside from
12996 //  the call offset, they are identical.
12997 //
12998 // Applies to at least: English CD, English floppy, German floppy
12999 // Responsible method: sChangeThief::changeState() in script 340
13000 // Fixes bug: #9894
13001 static const uint16 qfg4GuildWalkSignature1[] = {
13002 	0x38, SIG_SELECTOR16(dispose),      // pushi dispose
13003 	SIG_ADDTOOFFSET(+20),               // ... (dispose and null global[2]'s "obstacles" property)
13004 	0x4a, SIG_UINT16(0x0006),           // send 6d
13005 	SIG_ADDTOOFFSET(+85),               // ...
13006 	SIG_ADDTOOFFSET(+238),              // ... (secritExit init and addObstacle)
13007 	SIG_MAGICDWORD,
13008 	0x4a, SIG_UINT16(0x00a6),           // send 166d (polygon init)
13009 	0x36,                               // push
13010 	SIG_ADDTOOFFSET(+5),                // ... (global[2] addObstacle: polygon)
13011 	SIG_END
13012 };
13013 
13014 static const uint16 qfg4GuildWalkPatch1[] = {
13015 	0x32, PATCH_UINT16(0x0017),         // jmp 23d (skip obstacles disposal)
13016 	PATCH_ADDTOOFFSET(+108),
13017 	0x32, PATCH_UINT16(0x00f4),         // jmp 244d (skip secritExit and polygon)
13018 	PATCH_END
13019 };
13020 
13021 // Responsible method: rm340::init() in script 340
13022 static const uint16 qfg4GuildWalkSignature2[] = {
13023 	0x38, SIG_SELECTOR16(init),          // pushi init
13024 	0x76,                                // push0
13025 	0x38, SIG_SELECTOR16(approachVerbs), // pushi approachVerbs
13026 	0x78,                                // push1
13027 	0x39, 0x04,                          // pushi 4d
13028 	0x72, SIG_ADDTOOFFSET(+2),           // lofsa steps1
13029 	0x4a, SIG_UINT16(0x000a),            // send 10d
13030 
13031 	SIG_ADDTOOFFSET(+10),               // ... (similar inits follow)
13032 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa steps2
13033 	SIG_ADDTOOFFSET(+13),               // ...
13034 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa barrels1
13035 	SIG_ADDTOOFFSET(+13),               // ...
13036 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa barrels2
13037 	SIG_ADDTOOFFSET(+13),               // ...
13038 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa crack1
13039 	SIG_ADDTOOFFSET(+13),               // ...
13040 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa crack2
13041 	SIG_ADDTOOFFSET(+13),               // ...
13042 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa pillar
13043 	SIG_ADDTOOFFSET(+3),                // ...
13044 
13045 	SIG_ADDTOOFFSET(+26),               // ... (global[78]::add() steps1 and steps2)
13046 
13047 	SIG_ADDTOOFFSET(+459),              // ... (cond block for polygons)
13048 	SIG_MAGICDWORD,
13049 	0x32, SIG_UINT16(0x00f7),           // jmp 247d [end the cond] (2nd condition done)
13050 
13051                                         // (else condition)
13052 	0x38, SIG_SELECTOR16(init),         // pushi init
13053 	0x76,                               // push0
13054 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa secritExit
13055 	0x4a, SIG_UINT16(0x0004),           // send 4d
13056 	SIG_ADDTOOFFSET(+4),                // ... (addObstacle and its arg count)
13057 	SIG_ADDTOOFFSET(+228),              // ... (3rd Polygon type, init, and push)
13058 	SIG_ADDTOOFFSET(+5),                // ... (end of the polygons cond)
13059 
13060 	0x38, SIG_SELECTOR16(init),         // pushi init (super init:)
13061 	SIG_END
13062 };
13063 
13064 static const uint16 qfg4GuildWalkPatch2[] = {
13065 	0x3f, 0x02,                         // link 02 (set up loop vars, op affects the stack)
13066 	0x74, PATCH_GETORIGINALUINT16(11),  // lofss steps1
13067 	0x74, PATCH_GETORIGINALUINT16(27),  // lofss steps2
13068 	0x74, PATCH_GETORIGINALUINT16(43),  // lofss barrels1
13069 	0x74, PATCH_GETORIGINALUINT16(59),  // lofss barrels2
13070 	0x74, PATCH_GETORIGINALUINT16(75),  // lofss crack1
13071 	0x74, PATCH_GETORIGINALUINT16(91),  // lofss crack2
13072 	0x74, PATCH_GETORIGINALUINT16(107), // lofss pillar
13073                                         //
13074 	0x35, 0x08,                         // ldi 8d (decrement and send 7 times, while != 0)
13075 	0xa5, 0x00,                         // sat temp[0]
13076                                         //
13077 	0xe5, 0x00,                         // -at temp[0]
13078 	0x31, 0x13,                         // bnt 19d [on 0, end the loop]
13079 	0xad, 0x01,                            // sst temp[1] (pop the next object into a temp var)
13080 	0x38, PATCH_SELECTOR16(init),          // pushi init
13081 	0x76,                                  // push0
13082 	0x38, PATCH_SELECTOR16(approachVerbs), // pushi approachVerbs
13083 	0x78,                                  // push1
13084 	0x39, 0x04,                            // pushi 4d
13085 	0x85, 0x01,                            // lat temp[1] (accumulate the object)
13086 	0x4a, PATCH_UINT16(0x000a),            // send 10d
13087 	0x33, 0xe9,                         // jmp -23d [loop]
13088 
13089 	0x33, 0x33,                         // jmp 51d [skip subroutine declaration]
13090 	0x38, PATCH_SELECTOR16(obstacles),  // pushi obstacles (look up "obstacles", might be null)
13091 	0x76,                               // push0
13092 	0x81, 0x02,                         // lag global[2]
13093 	0x4a, PATCH_UINT16(0x0004),         // send 4d
13094 	0x31, 0x11,                         // bnt 17d [skip disposal and nulling]
13095 	0x38, PATCH_SELECTOR16(dispose),    // pushi dispose
13096 	0x76,                               // push0
13097 	0x4a, PATCH_UINT16(0x0004),         // send 4d ((global[2] obstacles?) dispose:)
13098                                         //
13099 	0x38, PATCH_SELECTOR16(obstacles),  // pushi obstacles (null the "obstacles" property)
13100 	0x78,                               // push1
13101 	0x76,                               // push0
13102 	0x81, 0x02,                         // lag global[2]
13103 	0x4a, PATCH_UINT16(0x0006),         // send 6d
13104                                         //
13105 	0x38, PATCH_SELECTOR16(addObstacle), // pushi addObstacle
13106 	0x78,                               // push1
13107 	0x32, PATCH_UINT16(0x020f),         // jmp 527d [3rd polygon type, init, and push]
13108                                         // (That will jmp back here)
13109 	0x81, 0x02,                         // lag global[2]
13110 	0x4a, PATCH_UINT16(0x0006),         // send 6d
13111                                         //
13112 	0x38, PATCH_SELECTOR16(init),       // pushi init
13113 	0x76,                               // push0
13114 	0x72, PATCH_GETORIGINALUINT16(605), // lofsa secritExit
13115 	0x4a, PATCH_UINT16(0x0004),         // send 4d
13116 	0x48,                               // ret
13117 
13118 	0x33, 0x07,                         // jmp 7d [skip waste bytes, to (global[78] add: steps1)]
13119 	0x5c,                               // selfID (waste 1 byte)
13120 	PATCH_ADDTOOFFSET(+494),            // ...
13121 	0x76,                               // push0 (0 call args, clobber the old secritExit init)
13122 	0x40, PATCH_UINT16(0xfdd6), PATCH_UINT16(0x0000), // call 0d [-554] (the subroutine does everything)
13123 	0x32, PATCH_UINT16(0x00ee),         // jmp 238d [end the cond]
13124 	0x5c,                               // selfID (waste 1 byte)
13125 	PATCH_ADDTOOFFSET(+4),              // ...
13126 	PATCH_ADDTOOFFSET(+228),            // ... (3rd polygon type, init, and push)
13127 	0x32, PATCH_UINT16(0xfd0a),         // jmp -758d [back into the subroutine]
13128 	0x35, 0x00,                         // ldi 0 (erase 2 bytes to keep disasm aligned)
13129 	PATCH_END
13130 };
13131 
13132 // Applies to at least: English CD
13133 // Responsible method: sBarrelMove::changeState(2) in script 340
13134 static const uint16 qfg4GuildWalkCDSignature3[] = {
13135 	SIG_MAGICDWORD,
13136 	0x30, SIG_UINT16(0x0032),           // bnt 50d [next case]
13137 	0x35, 0x02,                         // ldi 2d (case 2 label)
13138 	SIG_ADDTOOFFSET(+26),               // ... (register branch and derelict say())
13139 	SIG_ADDTOOFFSET(+19),               // ... (else, the rest of case 2 is a necessary say())
13140 	0x32, SIG_ADDTOOFFSET(+2),          // jmp ?? [end the switch]
13141 	SIG_END
13142 };
13143 
13144 static const uint16 qfg4GuildWalkCDPatch3[] = {
13145 	0x31, 0x15,                         // bnt 21d [next case]
13146 	0x38, PATCH_SELECTOR16(say),        // pushi say
13147 	0x39, 0x05,                         // pushi 5d
13148 	0x39, 0x06,                         // pushi 6d
13149 	0x39, 0x04,                         // pushi 4d
13150 	0x39, 0x13,                         // pushi 19d
13151 	0x76,                               // push0
13152 	0x7c,                               // pushSelf
13153 	0x81, 0x5b,                         // lag global[91]
13154 	0x4a, PATCH_UINT16(0x000e),         // send 14d
13155 	0x32, PATCH_GETORIGINALUINT16ADJUST(51, +30), // jmp ?? [end the switch]
13156 
13157 	0x3c,                               // dup (case 8 prologue)
13158 	0x35, 0x08,                         // ldi 8d
13159 	0x1a,                               // eq?
13160 	0x31, 0x06,                         // bnt 6d [next case]
13161 	0x76,                               // push0 (0 call args)
13162 	0x40, PATCH_UINT16(0xf592), PATCH_UINT16(0x0000), // call [-2670], 0d (patch 2's subroutine)
13163 	0x33, 0x10,                         // jmp 16d [skip waste bytes]
13164 	PATCH_END                           // (don't end the switch, keep testing cases)
13165 };
13166 
13167 // Applies to at least: English floppy, German floppy
13168 // Responsible method: sBarrelMove::changeState(2) in script 340
13169 static const uint16 qfg4GuildWalkFloppySignature3[] = {
13170 	SIG_MAGICDWORD,
13171 	0x30, SIG_UINT16(0x0032),           // bnt 50d [next case]
13172 	0x35, 0x02,                         // ldi 2d (case 2 label)
13173 	SIG_ADDTOOFFSET(+26),               // ... (register branch and derelict say())
13174 	SIG_ADDTOOFFSET(+19),               // ... (else, the rest of case 2 is a necessary say())
13175 	0x32, SIG_ADDTOOFFSET(+2),          // jmp ?? [end the switch]
13176 	SIG_END
13177 };
13178 
13179 static const uint16 qfg4GuildWalkFloppyPatch3[] = {
13180 	0x31, 0x15,                         // bnt 21d [next case]
13181 	0x38, PATCH_SELECTOR16(say),        // pushi say
13182 	0x39, 0x05,                         // pushi 5d
13183 	0x39, 0x06,                         // pushi 6d
13184 	0x39, 0x04,                         // pushi 4d
13185 	0x39, 0x13,                         // pushi 19d
13186 	0x76,                               // push0
13187 	0x7c,                               // pushSelf
13188 	0x81, 0x5b,                         // lag global[91]
13189 	0x4a, PATCH_UINT16(0x000e),         // send 14d
13190 	0x32, PATCH_GETORIGINALUINT16ADJUST(51, +30), // jmp ?? [end the switch]
13191 
13192 	0x3c,                               // dup (case 8 prologue)
13193 	0x35, 0x08,                         // ldi 8d
13194 	0x1a,                               // eq?
13195 	0x31, 0x06,                         // bnt 6d [next case]
13196 	0x76,                               // push0 (0 call args)
13197 	0x40, PATCH_UINT16(0xf5a8), PATCH_UINT16(0x0000), // call [-2648], 0d (patch 2's subroutine)
13198 	0x33, 0x10,                         // jmp 16d [skip waste bytes]
13199 	PATCH_END                           // (don't end the switch, keep testing cases)
13200 };
13201 
13202 // Rations are not properly decremented by daily scheduled meal consumption.
13203 // Rations are consumed periodically as time advances. If rations are the
13204 // active inventory item when the last of them is eaten, that icon will persist
13205 // in the verb bar.
13206 //
13207 // We make room by consolidating common gloryMessager::say() args in a
13208 // subroutine and cache values with temp variables. We add code to clean up
13209 // the verb bar when rations are exhausted (test if rations were the active
13210 // item, advance the cursor, hide the bar's invItem icon) - similar to the
13211 // localproc in script 16, called by combinable items to remove themselves.
13212 //
13213 // Applies to at least: English CD, English floppy, German floppy
13214 // Responsible method: hero::eatMeal() in script 28
13215 // Fixes bug: #10772
13216 static const uint16 qfg4LeftoversSignature[] = {
13217 	0x3f, 0x01,                         // link 1d
13218 	SIG_ADDTOOFFSET(+9),                // ...
13219 	SIG_MAGICDWORD,
13220 	0xe1, 0x88,                         // -ag global[136] (digest a preemptively eaten meal)
13221 	0x35, 0x01,                         // ldi 1d
13222 	SIG_ADDTOOFFSET(+238),              // ...
13223 	0x85, 0x00,                         // lat temp[0] (eaten, unset flags if true)
13224 	SIG_END
13225 };
13226 
13227 static const uint16 qfg4LeftoversPatch[] = {
13228 	0x3f, 0x03,                         // link 3d (3 temp vars)
13229 
13230 	PATCH_ADDTOOFFSET(+15),             // (cond 1, preemptively eaten meals)
13231 	0x32, PATCH_UINT16(0x00bb),         // jmp 187d [end the cond]
13232 
13233                                         // (cond 2)
13234 	0x39, PATCH_SELECTOR8(at),          // pushi at
13235 	0x78,                               // push1
13236 	0x39, 0x04,                         // pushi 4d (itemId 4, theRations)
13237 	0x81, 0x09,                         // lag global[9] (gloryInv)
13238 	0x4a, PATCH_UINT16(0x0006),         // send 6d
13239 	0xa5, 0x01,                         // sat temp[1] (theRations)
13240 
13241 	0x38, PATCH_SELECTOR16(amount),     // pushi amount
13242 	0x76,                               // push0
13243 	0x4a, PATCH_UINT16(0x0004),         // send 4d (theRations amount:)
13244 	0xa5, 0x02,                         // sat temp[2] (amount)
13245 
13246 	0x31, 0x50,                         // bnt 80d [next condition]
13247 
13248 	0x38, PATCH_SELECTOR16(amount),     // pushi amount
13249 	0x78,                               // push1
13250 	0xed, 0x02,                         // -st temp[2] (amount)
13251 	0x85, 0x01,                         // lat temp[1] (theRations)
13252 	0x4a, PATCH_UINT16(0x0006),         // send 6d (decrement amount)
13253 
13254 	0x85, 0x02,                         // lat temp[2] (amount)
13255 	0x2f, 0x3b,                         // bt 59d [skip exhausted item removal]
13256 
13257 	0x38, PATCH_SELECTOR16(owner),      // pushi owner
13258 	0x78,                               // push1
13259 	0x76,                               // push0
13260 	0x85, 0x01,                         // lat temp[1] (theRations)
13261 	0x4a, PATCH_UINT16(0x0006),         // send 6d
13262 
13263 	0x38, PATCH_SELECTOR16(curInvIcon), // pushi curInvIcon
13264 	0x76,                               // push0
13265 	0x81, 0x45,                         // lag global[69] (mainIconBar)
13266 	0x4a, PATCH_UINT16(0x0004),         // send 4d
13267 	0x8d, 0x01,                         // lst temp[1] (theRations)
13268 	0x1a,                               // eq?
13269 	0x31, 0x23,                         // bnt 35d [skip icon bar disabling]
13270 
13271 	0x38, PATCH_SELECTOR16(curInvIcon), // pushi curInvIcon
13272 	0x78,                               // push1
13273 	0x76,                               // push0
13274 	0x38, PATCH_SELECTOR16(advanceCurIcon), // pushi advanceCurIcon
13275 	0x76,                               // push0
13276 	0x38, PATCH_SELECTOR16(disable),    // pushi disable
13277 	0x78,                               // push1
13278 	0x39, 0x06,                         // pushi 6d
13279 	0x81, 0x45,                         // lag global[69] (mainIconBar)
13280 	0x4a, PATCH_UINT16(0x0010),         // send 16d
13281 
13282 	0x38, PATCH_SELECTOR16(hide),       // pushi hide
13283 	0x76,                               // push0
13284                                         //
13285 	0x7a,                               // push2 (2 call args)
13286 	0x39, 0x24,                         // pushi 36d
13287 	0x78,                               // push1
13288 	0x43, 0x02, PATCH_UINT16(0x0004),   // callk ScriptID, 4d (ScriptID 36 1, invItem)
13289                                         //
13290 	0x4a, PATCH_UINT16(0x0004),         // send 4d (invItem hide:)
13291                                         // (exhausted item removal end)
13292 
13293 	0x35, 0x01,                         // ldi 1d
13294 	0xa5, 0x00,                         // sat temp[0] (eaten)
13295 
13296 	0x33, 0x54,                         // jmp 84d [end the cond]
13297 
13298                                         // (cond 3)
13299 	0x78,                               // push1 (1 call arg)
13300 	0x39, 0x03,                         // pushi 3d ("hungry" flag)
13301 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb [export 4 of script 0], 2d (test flag 3)
13302 	0x31, 0x26,                         // bnt 38d [next condition]
13303                                         //
13304 	0x38, PATCH_SELECTOR16(useStamina), // pushi useStamina
13305 	0x7a,                               // push2
13306 	0x39, 0x08,                         // pushi 8d
13307 	0x76,                               // push0
13308 	0x54, PATCH_UINT16(0x0008),         // self 8d (hero useStamina: 8 0)
13309                                         //
13310 	0x31, 0x09,                         // bnt 9d [hero dies]
13311 	0x78,                               // push1 (1 call arg)
13312 	0x39, 0x05,                         // pushi 5d (say cond:5, "You're starving.")
13313 	0x41, 0x3a, PATCH_UINT16(0x0002),   // call [58], 2d (gloryMessager say: 1 6 5 1 0 28)
13314 	0x33, 0x36,                         // jmp 54d [end the cond]
13315                                         //
13316 	0x39, 0x04,                         // pushi 4d (4 call args)
13317 	0x39, 0x08,                         // pushi 8d
13318 	0x39, 0x1c,                         // pushi 28d
13319 	0x38, PATCH_UINT16(0x03e3),         // pushi 995d
13320 	0x78,                               // push1
13321 	0x47, 0x1a, 0x00, PATCH_UINT16(0x0008), // calle [export 0 of script 26], 8d (hero dies)
13322 	0x33, 0x25,                         // jmp 37d [end the cond]
13323 
13324                                         // (cond 4)
13325 	0x78,                               // push1 (1 call arg)
13326 	0x7a,                               // push2 ("missed meal" flag)
13327 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb [export 4 of script 0], 2d (test flag 2)
13328 	0x31, 0x10,                         // bnt 16d [next condition]
13329                                         //
13330 	0x78,                               // push1 (1 call arg)
13331 	0x39, 0x03,                         // pushi 3d ("hungry" flag)
13332 	0x45, 0x02, PATCH_UINT16(0x0002),   // callb [export 2 of script 0], 2d (set flag 3)
13333                                         //
13334 	0x78,                               // push1 (1 call arg)
13335 	0x39, 0x06,                         // pushi 6d (say cond:6, "Really getting hungry.")
13336 	0x41, 0x11, PATCH_UINT16(0x0002),   // call [17], 2d (gloryMessager say: 1 6 6 1 0 28)
13337 	0x33, 0x0d,                         // jmp 13d [end the cond]
13338 
13339                                         // (cond else)
13340 	0x78,                               // push1 (1 call arg)
13341 	0x7a,                               // push2 ("missed meal" flag)
13342 	0x45, 0x02, PATCH_UINT16(0x0002),   // callb [export 2 of script 0], 2d (set flag 2)
13343                                         //
13344 	0x78,                               // push1 (1 call arg)
13345 	0x39, 0x04,                         // pushi 4d (say cond:4, "Get food soon.")
13346 	0x41, 0x02, PATCH_UINT16(0x0002),   // call [2], 2d (gloryMessager say: 1 6 4 1 0 28)
13347 
13348                                         // (cond end)
13349 
13350 	0x33, 0x14,                         // jmp 20d [skip subroutine declaration]
13351 	0x38, PATCH_SELECTOR16(say),        // pushi say
13352 	0x39, 0x06,                         // pushi 6d
13353 	0x78,                               // push1 (noun)
13354 	0x39, 0x06,                         // pushi 6d (verb)
13355 	0x8f, 0x01,                         // lsp param[1] (cond varies)
13356 	0x78,                               // push1 (seq)
13357 	0x76,                               // push0 (caller)
13358 	0x39, 0x1c,                         // pushi 28d (message pool)
13359 	0x81, 0x5b,                         // lag global[91] (gloryMessager say: 1 6 ? 1 0 28)
13360 	0x4a, PATCH_UINT16(0x0010),         // send 16d
13361 	0x48,                               // ret
13362 
13363 	0x33, 0x16,                         // jmp 22d [skip waste bytes]
13364 	0x35, 0x00,                         // ldi 0 (erase 2 bytes to keep disasm aligned)
13365 	PATCH_END
13366 };
13367 
13368 // The runes puzzle in room 800 often rejects the correct answer. When a letter
13369 //  is selected it turns red but it's not applied until after a 30 tick delay
13370 //  with no visual indicator. If a letter is clicked during that delay then the
13371 //  previous letter is silently skipped, which is common since the correct
13372 //  answer contains the same letter consecutively.
13373 //
13374 // There are two approaches to fixing this: remove the delay or disable input
13375 //  during it. We do both. Letters are now applied as soon as they turn red, but
13376 //  the delay prevented the puzzle from ending abruptly, and so we still pause
13377 //  after the puzzle is solved but disable input. This preserves the puzzle's
13378 //  external behavior while making it impossible to click too fast.
13379 //
13380 // This bug is in all versions but the CD version exacerbates it with its broken
13381 //  dial. Sierra upgraded the Cycle classes which changed the behavior the dial
13382 //  depends on. CycleTo no longer supports wrapping around cel ranges and so the
13383 //  dial can't move from "C" to "O" (cels 7 to 0) and doesn't spin around when
13384 //  selecting the same letter twice as it did in floppy.
13385 //
13386 // Applies to: All versions
13387 // Responsible methods: proc_58 in script 801, runePuz:handleEvent, sTurnTheDial:changeState
13388 // Fixes bug: #10965
13389 static const uint16 qfg4RunesPuzzleSignature1[] = {
13390 	SIG_MAGICDWORD,
13391 	0x38, SIG_UINT16(0x0163),           // pushi 0163
13392 	0x45, 0x02, SIG_UINT16(0x0002),     // callb proc0_2 [ set flag 355, puzzle is solved ]
13393 	0x38, SIG_SELECTOR16(dispose),      // pushi dispose
13394 	0x76,                               // push0
13395 	0x72, SIG_UINT16(0x0010),           // lofsa runesPuz
13396 	0x4a, SIG_UINT16(0x0004),           // send 04 [ runesPuz dispose: ]
13397 	SIG_END
13398 };
13399 
13400 static const uint16 qfg4RunesPuzzlePatch1[] = {
13401 	PATCH_ADDTOOFFSET(+7),
13402 	0x32, PATCH_UINT16(0x0007),         // jmp 0007 [ don't exit puzzle immediately ]
13403 	PATCH_END
13404 };
13405 
13406 static const uint16 qfg4RunesPuzzleSignature2[] = {
13407 	// runePuz:handleEvent
13408 	0x63, SIG_ADDTOOFFSET(+1),          // pToa register [ always zero, the following code is unused ]
13409 	SIG_MAGICDWORD,
13410 	0x31, 0x21,                         // bnt 21 [ handle mouse/key down events ]
13411 	0x39, 0x04,                         // pushi 04
13412 	0x39, SIG_ADDTOOFFSET(+1),          // pushi type
13413 	0x76,                               // push0
13414 	0x87, 0x01,                         // lap 01
13415 	0x4a, SIG_UINT16(0x0004),           // send 04 [ event type? ]
13416 	SIG_ADDTOOFFSET(+495),
13417 	0x39, SIG_SELECTOR8(claimed),       // pushi claimed
13418 	SIG_ADDTOOFFSET(+852),
13419 	// sTurnTheDial:changeState
13420 	0x30, SIG_UINT16(0x0112),           // bnt 0112 [ state 2 ]
13421 	SIG_ADDTOOFFSET(+268),
13422 	0x35, 0x1e,                         // ldi 1e
13423 	0x65, SIG_ADDTOOFFSET(+1),          // aTop ticks
13424 	0x33, 0x20,                         // jmp 20 [ end of method ]
13425 	0x3c,                               // dup
13426 	0x35, 0x02,                         // ldi 02
13427 	0x1a,                               // eq?
13428 	0x31, 0x1a,                         // bnt 1a [ end of method ]
13429 	0x76,                               // push0
13430 	0x40, SIG_ADDTOOFFSET(+2),          // call proc_58 [ apply letter to puzzle ]
13431 	      SIG_UINT16(0x0000),
13432 	0x38, SIG_SELECTOR16(canControl),   // pushi canControl
13433 	0x78,                               // push1
13434 	0x78,                               // push1
13435 	0x51, SIG_ADDTOOFFSET(+1),          // class User
13436 	0x4a, SIG_UINT16(0x0006),           // send 06 [ User canControl: 1 (unnecessary) ]
13437 	0x38, SIG_SELECTOR16(dispose),      // pushi dispose
13438 	0x76,                               // push0
13439 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa sTurnTheDial
13440 	0x4a, SIG_UINT16(0x0004),           // send 04 [ sTurnTheDial dispose: (unnecessary) ]
13441 	SIG_END
13442 };
13443 
13444 static const uint16 qfg4RunesPuzzlePatch2[] = {
13445 	// runePuz:handleEvent
13446 	0x78,                               // push1
13447 	0x38, PATCH_UINT16(0x0163),         // pushi 0163
13448 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb proc0_3 [ is puzzle solved? ]
13449 	0x31, 0x1b,                         // bnt 1b   [ handle mouse/key down events ]
13450 	0x32, PATCH_UINT16(0x01f0),         // jmp 01f0 [ ignore events if puzzle is solved ]
13451 	PATCH_ADDTOOFFSET(+1350),
13452 	// sTurnTheDial:changeState
13453 	0x30, PATCH_UINT16(0x0123),         // bnt 0123 [ state 2 ]
13454 	PATCH_ADDTOOFFSET(+268),
13455 	0x76,                               // push0
13456 	0x40, PATCH_GETORIGINALUINT16ADJUST(+1648, +12), // call proc_58 [ apply letter to puzzle ]
13457 	      PATCH_UINT16(0x0000),
13458 	0x39, 0x01,                         // push1
13459 	0x38, PATCH_UINT16(0x0163),         // pushi 0163
13460 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb proc0_3 [ is puzzle solved? ]
13461 	0x31, 0x10,                         // bnt 10 [ end of method ]
13462 	0x35, 0x1e,                         // ldi 1e
13463 	0x65, PATCH_GETORIGINALBYTE(+1637), // aTop ticks [ pause 30 ticks before exiting puzzle ]
13464 	0x33, 0x0a,                         // jmp 0a [ end of method ]
13465 	0x38, PATCH_SELECTOR16(dispose),    // pushi dispose
13466 	0x76,                               // push0
13467 	0x72, PATCH_UINT16(0x0010),         // lofsa runesPuz
13468 	0x4a, PATCH_UINT16(0x0004),         // send 04 [ runesPuz dispose: ]
13469 	0x3a,                               // toss
13470 	0x48,                               // ret
13471 	PATCH_END
13472 };
13473 
13474 // The Domovoi in room 320 has a complex bug in the CD version. If you don't
13475 //  talk to him before Bella wakes you up then you can't get the doll and the
13476 //  game can't be completed. The event logic was changed in the floppy patch and
13477 //  again in the CD version, which introduced the bug. Working backwards...
13478 //
13479 // To get the doll from the inn's cabinet:
13480 // - It must be late at night
13481 // - Inn event 15 has occurred or is occurring
13482 //
13483 // To trigger inn event 15 ("You see that the Domovoi is here again"):
13484 // - It must be late at night
13485 // - You saved the monastery's Domovoi
13486 // - You clicked Talk on the Domovoi during inn event 3 (new requirement in CD)
13487 // - You didn't just enter from your room and hear crying
13488 //
13489 // To trigger inn event 3 ("You have the feeling you are being watched"):
13490 // - It must be late at night
13491 // - You haven't already clicked Talk on the Domovoi during inn event 3
13492 // - You haven't been woken by Bella (new requirement in CD)
13493 // - You didn't just enter from your room and hear crying
13494 //
13495 // The two new requirements create an unwinnable state. Once Bella wakes you,
13496 //  event 3 is no longer possible, cascading to event 15 and the doll. This also
13497 //  prevents the Domovoi from appearing in your room as that requires talking
13498 //  to him during event 3. Putting it all together, the new Bella requirement's
13499 //  only effect is to suppress a mandatory event, and so it is safe to remove.
13500 //
13501 // Applies to: English CD
13502 // Responsible methods: rm320:init, heroTeller:respond
13503 // Fixes bug: #10978
13504 static const uint16 qfg4DomovoiInnSignature[] = {
13505 	SIG_MAGICDWORD,
13506 	0x78,                               // push1
13507 	0x38, SIG_UINT16(0x0088),           // pushi 0088
13508 	0x45, 0x04, SIG_UINT16(0x0002),     // callb proc0_4 [ is flag 136 set? (has Bella woken you up?) ]
13509 	0x18,                               // not
13510 	0x31,                               // bnt [ skip inn event 3 ]
13511 	SIG_END
13512 };
13513 
13514 static const uint16 qfg4DomovoiInnPatch[] = {
13515 	0x32, PATCH_UINT16(0x0008),         // jmp 0008 [ skip flag 136 check ]
13516 	PATCH_END
13517 };
13518 
13519 // During the final battle with Ad Avis his initial timer is never stopped,
13520 //  reducing the intended time the player has to complete the sequence by more
13521 //  than half, and bringing Ad Avis back to life after he's killed.
13522 //
13523 // sTimeItOut state 0 sets a timeout when the battle starts. Its length depends
13524 //  on the detected cpu speed and game version. In the floppy versions this was
13525 //  a minimum of 400 seconds, which is so long that it masked the bug, but in CD
13526 //  it was reduced to 20 seconds. This is supposed to be how long the player has
13527 //  to tell the joke, after which sUltimakeJoke sets a second timeout in which
13528 //  the character-specific actions are to be done, but sTimeItOut finishes first
13529 //  and forces the player to complete both phases during the first shorter one.
13530 //  When Ad Avis is killed his death scripts only stop sUltimakeJoke, as they
13531 //  don't expect sTimeItOut to be running, and so when sTimeItOut times out it
13532 //  kills the player unless the death script has already called avis:dispose.
13533 //
13534 // We fix this by patching sTimeItOut state 1 to abort the script if the joke
13535 //  has been told. This is equivalent to the NRS patch that ships with the GOG
13536 //  version, which disposes sTimeItOut when telling the joke, and so this patch
13537 //  is applied to all versions except that one.
13538 //
13539 // Applies to: All versions
13540 // Responsible method: sTimeItOut:changeState(1)
13541 // Fixes bug: #10844
13542 static const uint16 qfg4AdAvisTimeoutSignature[] = {
13543 	0x30, SIG_UINT16(0x002c),           // bnt 002c [ state 1 ]
13544 	SIG_ADDTOOFFSET(+0x29),
13545 	SIG_MAGICDWORD,
13546 	0x32, SIG_UINT16(0x00ae),           // jmp 00ae [ end of method ]
13547 	0x3c,                               // dup
13548 	0x35, 0x01,                         // ldi 01
13549 	0x1a,                               // eq?
13550 	0x30, SIG_UINT16(0x0027),           // bnt 0027 [ state 2 ]
13551 	SIG_END
13552 };
13553 
13554 static const uint16 qfg4AdAvisTimeoutPatch[] = {
13555 	0x30, PATCH_UINT16(0x0029),         // bnt 0029 [ state 1 ]
13556 	PATCH_ADDTOOFFSET(+0x29),
13557 	0x3c,                               // dup
13558 	0x35, 0x01,                         // ldi 01
13559 	0x1a,                               // eq?
13560 	0x31, 0x2b,                         // bnt 2b [ state 2 ]
13561 	0x83, 0x04,                         // lal 04 [ has joke been told? ]
13562 	0x2f, 0x27,                         // bt 27  [ abort script if joke has been told ]
13563 	PATCH_END
13564 };
13565 
13566 // During the final battle with Ad Avis if the player casts a non-fatal spell at
13567 //  him then they can't cast any more spells. Instead they receive a message
13568 //  about being too busy or it not being a good place and must wait to die.
13569 //  Another symptom of this bug is that fighters and thieves don't get to see
13570 //  the staff transform if they've previously thrown a weapon.
13571 //
13572 // SpellItem:doVerb(4) determines if a spell is allowed. If hero:view is the
13573 //  wrong value then it says "This isn't a good place..." and if the room has
13574 //  a script it says "You're too busy...". avis:getHurt breaks one or both of
13575 //  these conditions by setting the room script to sMessages. If the game speed
13576 //  is set to less than high then avis:getHurt runs while the "project" room
13577 //  script is animating hero. Setting the room script to sMessages interrupts
13578 //  this and hero is left on view 14, breaking the first spell condition. Even
13579 //  if the game speed is set to high and hero's animation completes, sMessages
13580 //  fails to dispose itself, leaving it as the room script when it's complete
13581 //  and breaking the second spell condition.
13582 //
13583 // We fix this by reassigning sMessages from the room's script to midBlast, an
13584 //  arbitrary Prop that no scripts depend on. project is no longer interrupted,
13585 //  hero's animation completes at all speeds, and it no longer matters that
13586 //  sMessage fails to dispose itself. Due to script changes, this patch is only
13587 //  applied once to floppy and twice to CD.
13588 //
13589 // We also include a version of this for the offsets in the NRS patch, which is
13590 //  important as that ships with the GOG version.
13591 //
13592 // Applies to: All versions
13593 // Responsible method: avis:getHurt
13594 // Fixes bug: #10835
13595 static const uint16 qfg4AdAvisSpellsFloppySignature[] = {
13596 	SIG_MAGICDWORD,
13597 	0x72, SIG_UINT16(0x0096),           // lofsa sMessages
13598 	0x36,                               // push
13599 	0x81, 0x02,                         // lag 02
13600 	0x4a, SIG_UINT16(0x0006),           // send 06 [ rm730 setScript: sMessages ]
13601 	SIG_END
13602 };
13603 
13604 static const uint16 qfg4AdAvisSpellsFloppyPatch[] = {
13605 	0x74, PATCH_ADDTOOFFSET(+2),        // lofss sMessages
13606 	0x72, PATCH_UINT16(0x0668),         // lofsa midBlast
13607 	SIG_END
13608 };
13609 
13610 static const uint16 qfg4AdAvisSpellsCDSignature[] = {
13611 	SIG_MAGICDWORD,
13612 	0x72, SIG_UINT16(0x00a6),           // lofsa sMessages
13613 	0x36,                               // push
13614 	0x81, 0x02,                         // lag 02
13615 	0x4a, SIG_UINT16(0x0006),           // send 06 [ rm730 setScript: sMessages ]
13616 	SIG_END
13617 };
13618 
13619 static const uint16 qfg4AdAvisSpellsCDPatch[] = {
13620 	0x74, PATCH_ADDTOOFFSET(+2),        // lofss sMessages
13621 	0x72, PATCH_UINT16(0x06b6),         // lofsa midBlast
13622 	SIG_END
13623 };
13624 
13625 static const uint16 qfg4AdAvisSpellsNrsSignature[] = {
13626 	SIG_MAGICDWORD,
13627 	0x72, SIG_UINT16(0x00a8),           // lofsa sMessages
13628 	0x36,                               // push
13629 	0x81, 0x02,                         // lag 02
13630 	0x4a, SIG_UINT16(0x0006),           // send 06 [ rm730 setScript: sMessages ]
13631 	SIG_END
13632 };
13633 
13634 static const uint16 qfg4AdAvisSpellsNrsPatch[] = {
13635 	0x74, PATCH_ADDTOOFFSET(+2),        // lofss sMessages
13636 	0x72, PATCH_UINT16(0x06b8),         // lofsa midBlast
13637 	SIG_END
13638 };
13639 
13640 // If the magic user defeats Ad Avis with the game speed set to less than high
13641 //  then they aren't allowed to cast the final summon staff spell and complete
13642 //  the game. Instead they receive "You're too busy to cast a spell right now."
13643 //
13644 // Spells can't be cast if a room script is set, as described in the above patch
13645 //  notes. When Ad Avis is killed, avis:getHurt sets hero's view and loop before
13646 //  running sAdavisDies. If the game speed isn't set to high then the "project"
13647 //  script that deployed the final projectile spell is still running and waiting
13648 //  on hero's animation to complete. By changing the view and loop, avis:getHurt
13649 //  prevents project from advancing to its next state and completing, leaving it
13650 //  stuck as the room script and blocking the final spell.
13651 //
13652 // We can't prevent project from being the room script as that's game-wide
13653 //  behavior, and we can't prevent it from being interrupted since avis:getHurt
13654 //  needs to set hero's final view/loop, but we can still fix the bug by setting
13655 //  sAdavisDies as the room's script instead of hero's. This disposes project if
13656 //  it's still running and guarantees that the room script is cleared since
13657 //  sAdavisDies always disposes of itself.
13658 //
13659 // Applies to: All versions
13660 // Responsible method: avis:getHurt
13661 // Fixes bug: #10835
13662 static const uint16 qfg4AdAdvisLastSpellSignature[] = {
13663 	0x38, SIG_SELECTOR16(setScript),    // pushi setScript
13664 	0x78,                               // push1
13665 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa sAdavisDies
13666 	SIG_MAGICDWORD,
13667 	0x36,                               // push
13668 	0x81, 0x00,                         // lag 00
13669 	0x4a, SIG_UINT16(0x0006),           // send 06 [ hero setScript: sAdavisDies ]
13670 	SIG_END
13671 };
13672 
13673 static const uint16 qfg4AdAdvisLastSpellPatch[] = {
13674 	PATCH_ADDTOOFFSET(+8),
13675 	0x81, 0x02,                         // lag 02 [ rm730 ]
13676 	SIG_END
13677 };
13678 
13679 // When throwing a weapon or casting a spell at Ad Avis in room 730, sMessages
13680 //  tests the projectile type incorrectly and transposes the message responses.
13681 //
13682 // Applies to: All versions
13683 // Responsible method: sMessages:changeState(2)
13684 // Fixes bug: #10989
13685 static const uint16 qfg4AdAvisMessageSignature[] = {
13686 	0x8b, SIG_MAGICDWORD, 0x01,         // lsl 01 [ 0 if weapon thrown, else a spell ]
13687 	0x35, 0x00,                         // ldi 00
13688 	0x1a,                               // eq?
13689 	SIG_END
13690 };
13691 
13692 static const uint16 qfg4AdAvisMessagePatch[] = {
13693 	PATCH_ADDTOOFFSET(+4),
13694 	0x1c,                               // ne?
13695 	PATCH_END
13696 };
13697 
13698 // Throwing a rock or dagger at Ad Avis after telling the joke kills him.
13699 //  avis:getHurt fails to test the projectile type correctly in CD, or at all in
13700 //  floppy, and so all versions mistake this for casting a spell with the staff.
13701 //
13702 // We fix this by testing the projectile type and not allowing a thrown weapon
13703 //  to kill Ad Avis. This replaces an unnecessary hero:script test.
13704 //
13705 // Applies to: All versions
13706 // Responsible method: avis:getHurt
13707 // Fixes bug: #10989
13708 static const uint16 qfg4AdAvisThrowWeaponSignature[] = {
13709 	SIG_MAGICDWORD,
13710 	0x38, SIG_SELECTOR16(script),       // pushi script
13711 	0x76,                               // push0
13712 	0x81, 0x00,                         // lag 00
13713 	0x4a, SIG_UINT16(0x0004),           // send 04 [ hero script? ]
13714 	0x18,                               // not
13715 	0x30, SIG_ADDTOOFFSET(+2),          // bnt [ projectile doesn't kill ad avis ]
13716 	0x39, SIG_SELECTOR8(view),          // pushi view
13717 	SIG_END
13718 };
13719 
13720 static const uint16 qfg4AdAvisThrowWeaponPatch[] = {
13721 	0x83, 0x01,                        // lal 01 [ 0 if weapon thrown, else a spell ]
13722 	0x33, 0x06,                        // jmp 06 [ throwing a weapon doesn't kill ad avis ]
13723 	PATCH_END
13724 };
13725 
13726 // When a fighter or paladin selects the staff in the final battle with Ad Avis
13727 //  after throwing a rock or dagger they enter an infinite animation loop due to
13728 //  not clearing hero:cycler. Multiple bugs in this room prevented getting this
13729 //  far, but we fixed those, so we also fix this by clearing the cycler.
13730 //
13731 // Applies to: All versions
13732 // Responsible method: sDoTheStaff:changeState(3)
13733 // Fixes bug: #10835
13734 static const uint16 qfg4FighterSpearSignature[] = {
13735 	0x39, SIG_SELECTOR8(view),          // pushi view [ start of fighter code, same as paladin ]
13736 	SIG_ADDTOOFFSET(0x3e),
13737 	0x3c,                               // dup
13738 	0x35, SIG_MAGICDWORD, 0x03,         // ldi 03
13739 	0x1a,                               // eq? [ is paladin? (last condition so always true) ]
13740 	0x30, SIG_UINT16(0x0022),           // bnt 0022
13741 	0x39, SIG_SELECTOR8(view),          // pushi view
13742 	0x78,                               // push1
13743 	0x39, 0x0a,                         // pushi 0a
13744 	0x38, SIG_SELECTOR16(setLoop),      // pushi setLoop
13745 	0x7a,                               // push2
13746 	0x76,                               // push0
13747 	0x78,                               // push1
13748 	0x38, SIG_SELECTOR16(setCel),       // pushi setCel
13749 	SIG_ADDTOOFFSET(+13),
13750 	0x4a, SIG_UINT16(0x001c),           // send 1c [ hero view: 10 setLoop 0 1 setCel: 0 ... ]
13751 	SIG_END
13752 };
13753 
13754 static const uint16 qfg4FighterSpearPatch[] = {
13755 	0x33, 0x3e,                         // jmp 3e [ use patched paladin code for fighter ]
13756 	PATCH_ADDTOOFFSET(0x3e),
13757 	0x39, PATCH_SELECTOR8(view),        // pushi view
13758 	0x78,                               // push1
13759 	0x39, 0x0a,                         // pushi 0a
13760 	0x38, PATCH_SELECTOR16(setLoop),    // pushi setLoop
13761 	0x7a,                               // push2
13762 	0x76,                               // push0
13763 	0x78,                               // push1
13764 	0x38, PATCH_SELECTOR16(setCel),     // pushi setCel
13765 	0x39, 0x01,                         // pushi 01
13766 	0x39, 0x00,                         // pushi 00
13767 	0x38, PATCH_SELECTOR16(setCycle),   // pushi setCycle
13768 	PATCH_ADDTOOFFSET(+13),
13769 	0x4a, PATCH_UINT16(0x0022),         // send 22 [ hero view: 10 setLoop 0 1 setCel: 0 setCycle: 0 ... ]
13770 	PATCH_END
13771 };
13772 
13773 // Clicking Do on the inn door in room 260 from certain coordinates crashes the
13774 //  CD version. This is one of several related crashes where the Grooper or
13775 //  Grycler classes send a selector to a non-object in only the CD version.
13776 //
13777 // The inn door script isn't buggy, and neither are Grooper or Grycler. Instead,
13778 //  Sierra "upgraded" the core Cycle classes in the CD version with drastically
13779 //  different behavior after the game was already written for the first ones.
13780 //  It's unclear what they were attempting to accomplish, but the conspicuous
13781 //  regressions include hero stuttering when walking on every screen, the runes
13782 //  dial refusing to spin a full rotation, random crashes at the inn door and
13783 //  on the slippery path in room 800, and probably other problems. Meanwhile
13784 //  GK1, a relatively stable SCI32 game released at the same time, used the same
13785 //  Cycle classes in all its versions as QFG4 floppy without motion problems.
13786 //
13787 // The crashes result from complex motion edge cases but involve hero ending up
13788 //  without a cycler at the wrong moment. These can be avoided by adding a call
13789 //  to hero:normalize to reset a lot of state and set hero:cycler to StopWalk
13790 //  and hero:looper to stopGroop. This is a bit of a kitchen-sink solution but
13791 //  it does the job without side effects and only requires 4 bytes.
13792 //
13793 // We prevent the inn door crash by calling hero:normalize in sInInnDoor.
13794 //
13795 // Applies to: English CD
13796 // Responsible method: sInInnDoor:changeState(1)
13797 // Fixes bug: #10760
13798 static const uint16 qfg4InnDoorCDSignature[] = {
13799 	0x30, SIG_MAGICDWORD,               // bnt 000e [ state 2 ]
13800 	      SIG_UINT16(0x000e),
13801 	0x38, SIG_UINT16(0x0111),           // pushi setHeading [ hard-coded for CD ]
13802 	0x7a,                               // push2
13803 	0x76,                               // push0
13804 	0x7c,                               // pushSelf
13805 	0x81, 0x00,                         // lag 00
13806 	0x4a, SIG_UINT16(0x0008),           // send 08  [ hero setHeading: 0 self ]
13807 	0x32, SIG_UINT16(0x00c3),           // jmp 00c3 [ end of method ]
13808 	SIG_END
13809 };
13810 
13811 static const uint16 qfg4InnDoorCDPatch[] = {
13812 	0x31, 0x0f,                         // bnt 0f [ state 2 ]
13813 	0x38, PATCH_SELECTOR16(normalize),  // pushi normalize
13814 	0x76,                               // push0
13815 	0x38, PATCH_UINT16(0x0111),         // pushi setHeading [ hard-coded for CD ]
13816 	0x7a,                               // push2
13817 	0x76,                               // push0
13818 	0x7c,                               // pushSelf
13819 	0x81, 0x00,                         // lag 00
13820 	0x4a, PATCH_UINT16(0x000c),         // send 0c [ hero normalize: setHeading: 0 self ]
13821 	PATCH_END
13822 };
13823 
13824 // In room 800, at the start of the game, automatically sliding down the top of
13825 //  the slope can crash the CD version in Grooper:doit. See the inn door patch
13826 //  above for details on these motion regressions and their solution.
13827 //
13828 // We fix this by calling hero:normalize at the start of sFallsBackSide to reset
13829 //  hero's state before starting the motion sequence.
13830 //
13831 // This patch is not applied to the NRS fan-patch included in the GOG version.
13832 //  It fixes this bug by adding a spin loop delay that's relative to game speed
13833 //  to sFallsBackSide.
13834 //
13835 // Applies to: English CD
13836 // Responsible method: sFallsBackSide:changeState(0)
13837 // Fixes bug: #9801
13838 static const uint16 qfg4SlidingDownSlopeCDSignature[] = {
13839 	0x87, 0x01,                       // lap 01
13840 	0x65, 0x16,                       // aTop state
13841 	0x36,                             // push
13842 	0x3c,                             // dup
13843 	0x35, SIG_MAGICDWORD, 0x00,       // ldi 00
13844 	0x1a,                             // eq?
13845 	0x31, 0x30,                       // bnt 30 [ state 1 ]
13846 	SIG_ADDTOOFFSET(+42),
13847 	0x4a, SIG_UINT16(0x0014),         // send 14 [ hero: setStep: 1 1 ... ]
13848 	SIG_END
13849 };
13850 
13851 static const uint16 qfg4SlidingDownSlopeCDPatch[] = {
13852 	PATCH_ADDTOOFFSET(+5),
13853 	0x2f, 0x34,                         // bt 34 [ state 1 ]
13854 	0x38, PATCH_SELECTOR16(normalize),  // pushi normalize
13855 	0x76,                               // push0
13856 	PATCH_ADDTOOFFSET(+42),
13857 	0x4a, PATCH_UINT16(0x0018),         // send 18 [ hero: normalize: setStep: 1 1 ... ]
13858 	PATCH_END
13859 };
13860 
13861 // Walking around the base of the slippery slope in room 800 can crash the CD
13862 //  version in either the Grooper or Grycler classes. See the inn door patch
13863 //  above for details on these regressions and their solution.
13864 //
13865 // The script sSlippery runs when walking up the slope and sWalksDown runs when
13866 //  walking down. Both are vulnerable to Grooper/Grycler crashes and both can be
13867 //  fixed by adding hero:normalize calls.
13868 //
13869 // We also include a version of the sWalksDown patch for the instruction sizes
13870 //  in the NRS patch, which is important as that ships with the GOG version.
13871 //
13872 // Applies to: English CD
13873 // Responsible methods: sSlippery:changeState(0), sWalksDown:changeState(0)
13874 // Fixes bug: #10747
13875 static const uint16 qfg4WalkUpSlopeCDSignature[] = {
13876 	SIG_MAGICDWORD,
13877 	0x38, SIG_UINT16(0x0142),           // pushi setMotion [ hard-coded for CD ]
13878 	0x78,                               // push1
13879 	0x76,                               // push0
13880 	SIG_ADDTOOFFSET(+8),
13881 	0x4a, SIG_UINT16(0x000e),           // send 0e [ hero setMotion: 0 ... ]
13882 	SIG_END
13883 };
13884 
13885 static const uint16 qfg4WalkUpSlopeCDPatch[] = {
13886 	0x38, PATCH_SELECTOR16(normalize),  // pushi normalize
13887 	0x39, 0x00,                         // pushi 00
13888 	PATCH_ADDTOOFFSET(+8),
13889 	0x4a, PATCH_UINT16(0x000c),         // send 0c [ hero normalize: ... ]
13890 	PATCH_END
13891 };
13892 
13893 static const uint16 qfg4WalkDownSlopeCDSignature[] = {
13894 	0x3c,                               // dup
13895 	0x35, SIG_MAGICDWORD, 0x00,         // ldi 00
13896 	0x1a,                               // eq?
13897 	0x31, 0x1e,                         // bnt 1e [ state 1 ]
13898 	0x38, SIG_UINT16(0x0218),           // pushi handsOff [ hard-coded for CD ]
13899 	SIG_ADDTOOFFSET(+15),
13900 	0x4a, SIG_UINT16(0x0008),           // send 08 [ hero setStep: ... ]
13901 	SIG_END
13902 };
13903 
13904 static const uint16 qfg4WalkDownSlopeCDPatch[] = {
13905 	0x2f, 0x22,                         // bt 22 [ state 1 ]
13906 	0x38, PATCH_SELECTOR16(normalize),  // pushi normalize
13907 	0x76,                               // push0
13908 	PATCH_ADDTOOFFSET(+18),
13909 	0x4a, PATCH_UINT16(0x000c),         // send 0c [ hero normalize: setStep: ... ]
13910 	PATCH_END
13911 };
13912 
13913 static const uint16 qfg4WalkDownSlopeNrsSignature[] = {
13914 	0x3c,                               // dup
13915 	0x35, SIG_MAGICDWORD, 0x00,         // ldi 00
13916 	0x1a,                               // eq?
13917 	0x30, SIG_UINT16(0x001f),           // bnt 001f [ state 1 ]
13918 	0x38, SIG_UINT16(0x0218),           // pushi handsOff [ hard-coded for CD ]
13919 	SIG_ADDTOOFFSET(+15),
13920 	0x4a, SIG_UINT16(0x0008),           // send 08 [ hero setStep: ... ]
13921 	SIG_END
13922 };
13923 
13924 static const uint16 qfg4WalkDownSlopeNrsPatch[] = {
13925 	0x2e, PATCH_UINT16(0x0023),         // bt 0023 [ state 1 ]
13926 	0x38, PATCH_SELECTOR16(normalize),  // pushi normalize
13927 	0x76,                               // push0
13928 	PATCH_ADDTOOFFSET(+18),
13929 	0x4a, PATCH_UINT16(0x000c),         // send 0c [ hero normalize: setStep: ... ]
13930 	PATCH_END
13931 };
13932 
13933 // The NRS fan-patch for wraiths has a bug which locks up the game. This occurs
13934 //  when a wraith initializes while game time is greater than $7fff. The patch
13935 //  throttles wraith:doit to execute no more than once per game tick, which it
13936 //  does by storing the previous game time in a new local variable whose initial
13937 //  value is zero. This technique is used in several patches but this one is
13938 //  missing a call to Abs that the others have. Once game time reaches $8000 or
13939 //  greater, the signed less-than test will always pass when the local variable
13940 //  is zero, and wraith:doit won't execute.
13941 //
13942 // We fix this by changing the signed less-than comparison to unsigned.
13943 //
13944 // Applies to: English CD with NRS patches 53.HEP/SCR
13945 // Responsible method: wraith:doit
13946 // Fixes bug: #10711
13947 static const uint16 qfg4WraithLockupNrsSignature[] = {
13948 	SIG_MAGICDWORD,
13949 	0x89, 0x58,                         // lsg 58
13950 	0x83, 0x04,                         // lal 04
13951 	0x04,                               // sub
13952 	0x36,                               // push
13953 	0x35, 0x01,                         // ldi 01
13954 	0x22,                               // lt? [ (gameTime - prevGameTime) < 1 ]
13955 	SIG_END
13956 };
13957 
13958 static const uint16 qfg4WraithLockupNrsPatch[] = {
13959 	PATCH_ADDTOOFFSET(+8),
13960 	0x2a,                               // ult?
13961 	PATCH_END
13962 };
13963 
13964 // The script that determines how much money a revenant has is missing the first
13965 //  parameter to kRandom, which should be zero as it is with other monsters.
13966 //  Instead of awarding the intended 15 to 40 kopeks, it always awards 15 and
13967 //  reseeds the random number generator to 25.
13968 //
13969 // Applies to: All versions
13970 // Responsible method: sSearchMonster:changeState(1)
13971 // Fixes bug: #10966
13972 static const uint16 qfg4SearchRevenantSignature[] = {
13973 	0x39, 0x0f,                         // pushi 0f
13974 	0x78,                               // push1
13975 	0x39, SIG_MAGICDWORD, 0x19,         // pushi 19
13976 	0x43, 0x5d, SIG_UINT16(0x0002),     // callk Random 02
13977 	0x02,                               // add [ 15 + Random 25 ]
13978 	SIG_END
13979 };
13980 
13981 static const uint16 qfg4SearchRevenantPatch[] = {
13982 	0x39, 0x02,                         // pushi 02
13983 	0x39, 0x0f,                         // pushi 0f
13984 	0x39, 0x28,                         // pushi 28
13985 	0x43, 0x5d, PATCH_UINT16(0x0004),   // callk Random 04 [ Random 15 40 ]
13986 	PATCH_END
13987 };
13988 
13989 // During combat, if a rabbit is all the way to the right and attacks then it
13990 //  won't make any more moves, forcing the player to run away to end the fight.
13991 //  This is due to rabbitCombat failing to pass a caller to the rabbitAttack
13992 //  script and so it gets stuck. We pass the missing "self" parameter.
13993 //
13994 // Applies to: All versions
13995 // Responsible method: rabbitCombat:changeState(1)
13996 // Fixes bug: #11000
13997 static const uint16 qfg4RabbitCombatSignature[] = {
13998 	0x38, SIG_SELECTOR16(setScript),    // pushi setScript
13999 	0x78,                               // push1
14000 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa rabbitAttack
14001 	0X36,                               // push
14002 	SIG_MAGICDWORD,
14003 	0x54, SIG_UINT16(0x0006),           // self 06 [ self setScript: rabbitAttack ]
14004 	0x32, SIG_UINT16(0x014b),           // jmp 014b
14005 	SIG_END
14006 };
14007 
14008 static const uint16 qfg4RabbitCombatPatch[] = {
14009 	PATCH_ADDTOOFFSET(+3),
14010 	0x7a,                               // push2
14011 	0x74, PATCH_ADDTOOFFSET(+2),        // lofss rabbitAttack
14012 	0x7c,                               // pushSelf
14013 	0x54, PATCH_UINT16(0x0008),         // self 08 [ self setScript: rabbitAttack self ]
14014 	PATCH_END
14015 };
14016 
14017 // Attempting to open the monastery door in room 250 while Igor is present
14018 //  randomly locks up the game. sHectapusDeath stands Igor up, but this can be
14019 //  interrupted by sIgorCarves animating him at random intervals, leaving
14020 //  sHectapusDeath stuck in handsOff mode.
14021 //
14022 // We fix this by first stopping sIgorCarves as other scripts in this room do.
14023 //
14024 // Applies to: All versions
14025 // Responsible method: sHectapusDeath:changeState(4)
14026 // Fixes bug: #10994
14027 static const uint16 qfg4HectapusDeathSignature[] = {
14028 	0x30, SIG_UINT16(0x0027),           // bnt 0027
14029 	SIG_ADDTOOFFSET(+13),
14030 	0x30, SIG_UINT16(0x0017),           // bnt 0017
14031 	0x38, SIG_MAGICDWORD,               // pushi setLoop
14032 	      SIG_SELECTOR16(setLoop),
14033 	0x7a,                               // push2
14034 	0x7a,                               // push2
14035 	0x78,                               // push1
14036 	0x38, SIG_SELECTOR16(setCycle),     // pushi setCycle
14037 	0x7a,                               // push2
14038 	0x51, SIG_ADDTOOFFSET(+1),          // class End
14039 	0x36,                               // push
14040 	0x7c,                               // pushSelf
14041 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa igor
14042 	0x4a, SIG_UINT16(0x0010),           // send 10 [ igor setLoop: 2 1 setCycle: End self ]
14043 	0x32, SIG_ADDTOOFFSET(+2),          // jmp [ end of method ]
14044 	0x35, 0x01,                         // ldi 01
14045 	0x65, SIG_ADDTOOFFSET(+1),          // aTop cycles
14046 	0x32, SIG_ADDTOOFFSET(+2),          // jmp [ end of method ]
14047 	SIG_END
14048 };
14049 
14050 static const uint16 qfg4HectapusDeathPatch[] = {
14051 	0x30, PATCH_UINT16(0x002b),         // bnt 002b
14052 	PATCH_ADDTOOFFSET(+13),
14053 	0x30, PATCH_UINT16(0x001b),         // bnt 001b
14054 	PATCH_ADDTOOFFSET(+17),
14055 	0x38, PATCH_SELECTOR16(setScript),  // pushi setScript
14056 	0x78,                               // push1
14057 	0x76,                               // push0
14058 	0x4a, PATCH_UINT16(0x0016),         // send 16 [ igor setLoop: 2 1 setCycle: End self setScript: 0 ]
14059 	0x3a,                               // toss
14060 	0x48,                               // ret
14061 	0x78,                               // push1
14062 	0x69, PATCH_GETORIGINALBYTE(+45),   // sTop cycles
14063 	PATCH_END
14064 };
14065 
14066 // Floppy 1.0 locks up when Ad Avis captures you with a Necrotaur in room 552,
14067 //  and possibly other rooms. sBlackOut:changeState has one too many states
14068 //  and doesn't increment the state number and cue enough in all scenarios.
14069 //
14070 // We fix this by removing the empty state 3 as later floppy versions do.
14071 //
14072 // Applies to: English Floppy 1.0
14073 // Responsible method: sBlackOut:changeState
14074 // Fixes bug: #11001
14075 static const uint16 qfg4AdAvisCaptureSignature[] = {
14076 	0x31, 0x05,                         // bnt 05 [ state 3 ]
14077 	SIG_ADDTOOFFSET(+6),
14078 	0x35, SIG_MAGICDWORD, 0x03,         // ldi 03
14079 	0x1a,                               // eq?
14080 	0x31, 0x05,                         // bnt 05 [ state 4 ]
14081 	SIG_ADDTOOFFSET(+6),
14082 	0x35, 0x04,                         // ldi 04
14083 	SIG_ADDTOOFFSET(+83),
14084 	0x35, 0x05,                         // ldi 05
14085 	SIG_END
14086 };
14087 
14088 static const uint16 qfg4AdAvisCapturePatch[] = {
14089 	0x31, 0x10,                         // bnt 10 [ new state 3 ]
14090 	PATCH_ADDTOOFFSET(+17),
14091 	0x35, 0x03,                         // ldi 03 [ state 4 is now state 3 ]
14092 	PATCH_ADDTOOFFSET(+83),
14093 	0x35, 0x04,                         // ldi 04 [ state 5 is now state 4 ]
14094 	PATCH_END
14095 };
14096 
14097 // The character selection screen in room 140 can select the wrong character.
14098 //  The showOff script brings each through their door and sets showOff:register
14099 //  as they animate so that myChar:doVerb knows which animating character was
14100 //  clicked. showOff:register is supposed to be 1 - 3 but showOff sets the wrong
14101 //  fighter value and sets the rest at the wrong times.
14102 //
14103 // showOff state 0 sets register to 30 instead of 1 and so clicking the fighter
14104 //  door during the first four seconds doesn't select any character. Instead it
14105 //  proceeds to the skill screen without updating the character type global.
14106 //  This global's initial value is zero, which happens to be the fighter value,
14107 //  and so this appears to work unless a different character was first selected
14108 //  and cancel was clicked. The magic user and thief register values are correct
14109 //  but they're set one state too late, creating 2 - 4 second windows where
14110 //  clicking their doors selects the previously animated character.
14111 //
14112 // We fix this by setting showOff:register to the correct fighter value and
14113 //  setting the magic user and thief values one state earlier.
14114 //
14115 // Applies to: All versions
14116 // Responsible method: showOff:changeState
14117 // Fixes bug: #11002
14118 static const uint16 qfg4CharacterSelectSignature[] = {
14119 	// state 0
14120 	0x35, 0x1e,                         // ldi 1e
14121 	0x65, SIG_ADDTOOFFSET(+1),          // aTop register
14122 	SIG_ADDTOOFFSET(+461),
14123 	// state 5
14124 	SIG_MAGICDWORD,
14125 	0x32, SIG_UINT16(0x031e),           // jmp 031e [ end of method ]
14126 	0x3c,                               // dup
14127 	SIG_ADDTOOFFSET(+219),
14128 	// state 9
14129 	0x35, 0x02,                         // ldi 02
14130 	0x65, SIG_ADDTOOFFSET(+1),          // aTop seconds
14131 	0x32, SIG_UINT16(0x023b),           // jmp 023b [ end of method ]
14132 	SIG_END
14133 };
14134 
14135 static const uint16 qfg4CharacterSelectPatch[] = {
14136 	// state 0
14137 	0x35, 0x01,                         // ldi 01
14138 	PATCH_ADDTOOFFSET(+463),
14139 	// state 5
14140 	0x7a,                               // push2
14141 	0x69, PATCH_GETORIGINALBYTE(+3),    // sTop register
14142 	PATCH_ADDTOOFFSET(+220),
14143 	// state 9
14144 	0x7a,                               // push2
14145 	0x69, PATCH_GETORIGINALBYTE(+691),  // sTop seconds
14146 	0x39, 0x03,                         // pushi 03
14147 	0x69, PATCH_GETORIGINALBYTE(+3),    // sTop register
14148 	PATCH_END
14149 };
14150 
14151 // Clicking Look in the dungeon (room 670) responds with the dungeon description
14152 //  followed by the generic message for not seeing anything. rm670:doVerb is
14153 //  missing a return statement and so it proceeds with generic verb handling.
14154 //
14155 // Applies to: All versions
14156 // Responsible method: rm670:doVerb
14157 static const uint16 qfg4LookDungeonSignature[] = {
14158 	0x38, SIG_SELECTOR16(say),          // pushi say
14159 	0x38, SIG_UINT16(0x0006),           // pushi 0006
14160 	SIG_MAGICDWORD,
14161 	0x76,                               // push0
14162 	0x78,                               // push1
14163 	0x76,                               // push0
14164 	0x76,                               // push0
14165 	0x76,                               // push0
14166 	0x38, SIG_UINT16(0x029e),           // pushi 029e
14167 	0x81, 0x5b,                         // lag 5b
14168 	0x4a, SIG_UINT16(0x0010),           // send 10 [ gloryMessager say: 0 1 0 0 0 670 ]
14169 	SIG_END
14170 };
14171 
14172 static const uint16 qfg4LookDungeonPatch[] = {
14173 	PATCH_ADDTOOFFSET(+11),
14174 	0x89, 0x0b,                         // lsg 0b [ room number, saves a byte ]
14175 	0x81, 0x5b,                         // lag 5b
14176 	0x4a, PATCH_UINT16(0x0010),         // send 10 [ gloryMessager say: 0 1 0 0 0 670 ]
14177 	0x48,                               // ret
14178 	PATCH_END
14179 };
14180 
14181 // When approaching the door to the great hall in staircase room 627 at night,
14182 //  the message "You hear voices..." continues to occur even after witnessing
14183 //  the argument between Katrina and Ad Avis in the floppy version. This is due
14184 //  to not testing flag 112, which is set by the argument scene, and was fixed
14185 //  in the CD version. We add the missing flag test.
14186 //
14187 // This incomplete logic to determine if Katrina and Ad Avis are in the great
14188 //  hall is duplicated throughout this script. Although Sierra fixed this
14189 //  instance in the CD version, it's the only one they fixed, while adding more
14190 //  that lack the flag test. We fix those bugs in subsequent patches.
14191 //
14192 // Applies to: English Floppy, German Floppy
14193 // Responsible method: sDisplay:changeState(0)
14194 // Fixes bug: #10799
14195 static const uint16 qfg4ArgumentMessageFloppySignature[] = {
14196 	SIG_MAGICDWORD,
14197 	0x83, 0x02,                         // lal 02   [ message already said? ]
14198 	0x30, SIG_UINT16(0x0013),           // bnt 0013 [ say message ]
14199 	0x38, SIG_SELECTOR16(handsOn),      // pushi handsOn
14200 	0x76,                               // push0
14201 	0x81, 0x01,                         // lag 01
14202 	0x4a, SIG_UINT16(0x0004),           // send 04 [ Glory handsOn: ]
14203 	0x38, SIG_SELECTOR16(dispose),      // pushi dispose
14204 	0x76,                               // push0
14205 	0x54, SIG_UINT16(0x0004),           // self 04
14206 	0x32, SIG_UINT16(0x0079),           // jmp 0079 [ end of method ]
14207 	SIG_ADDTOOFFSET(+38),
14208 	0x38, SIG_SELECTOR16(dispose),      // pushi dispose
14209 	0x76,                               // push0
14210 	0x54, SIG_UINT16(0x0004),           // self 04  [ self dispose: ]
14211 	0x32, SIG_UINT16(0x0049),           // jmp 0049 [ end of method ]
14212 	SIG_END
14213 };
14214 
14215 static const uint16 qfg4ArgumentMessageFloppyPatch[] = {
14216 	PATCH_ADDTOOFFSET(+2),
14217 	0x2f, 0x09,                         // bt 09 [ skip message if already said ]
14218 	0x78,                               // push1
14219 	0x39, 0x70,                         // pushi 70 [ flag 112 ]
14220 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb proc0_4 02 [ has argument occurred? ]
14221 	0x31, 0x0b,                         // bnt 0b [ say message ]
14222 	0x38, PATCH_SELECTOR16(handsOn),    // pushi handsOn
14223 	0x76,                               // push0
14224 	0x81, 0x01,                         // lag 01
14225 	0x4a, PATCH_UINT16(0x0004),         // send 04 [ Glory handsOn: ]
14226 	0x33, 0x26,                         // jmp 26 [ self dispose:, end of method ]
14227 	PATCH_END
14228 };
14229 
14230 // The great hall door options in room 627 are incorrect at night after Katrina
14231 //  and Ad Avis argue. rm620Code:init is missing a flag test to prevent the
14232 //  argument options from reoccurring. We add the missing flag test.
14233 //
14234 // Applies to: All versions
14235 // Responsible methods: rm620Code:init
14236 // Fixes bug: #10799
14237 static const uint16 qfg4Room627DoorOptionsSignature[] = {
14238 	0x89, 0x0b,                         // lsg 0b [ room number ]
14239 	SIG_ADDTOOFFSET(+28),
14240 	0x39, 0x05,                         // pushi 05 [ argument door options ]
14241 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa doorTopTeller
14242 	0x4a, SIG_UINT16(0x000e),           // send 0e [ doorTopTeller init: pUpperDoor 620 8 155 5 ]
14243 	SIG_MAGICDWORD,
14244 	0x33, 0x19,                         // jmp 19
14245 	0x38, SIG_SELECTOR16(init),         // pushi init
14246 	0x38, SIG_UINT16(0x0005),           // pushi 0005
14247 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa pUpperDoor
14248 	0x36,                               // push
14249 	0x38, SIG_UINT16(0x026c),           // pushi 026c
14250 	0x39, 0x08,                         // pushi 08
14251 	0x38, SIG_UINT16(0x009b),           // pushi 009b
14252 	0x78,                               // push1 [ normal door options ]
14253 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa doorTopTeller
14254 	0x4a, SIG_UINT16(0x000e),           // send 0e [ doorTopTeller init: pUpperDoor 620 8 155 1 ]
14255 	SIG_END
14256 };
14257 
14258 static const uint16 qfg4Room627DoorOptionsPatch[] = {
14259 	0x33, 0x0a,                         // jmp 0a
14260 	PATCH_ADDTOOFFSET(+28),
14261 	0x89, 0x0b,                         // lsg 0b [ room number ]
14262 	0x34, PATCH_UINT16(0x0273),         // ldi 627d
14263 	0x1a,                               // eq?
14264 	0x31, 0x14,                         // bnt 14 [ normal door options ]
14265 	0x81, 0x79,                         // lag 79 [ night ]
14266 	0x31, 0x10,                         // bnt 10 [ normal door options ]
14267 	0x78,                               // push1
14268 	0x39, 0x70,                         // pushi 70 [ flag 112 ]
14269 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb proc0_4 02 [ has argument occurred? ]
14270 	0x2f, 0x07,                         // bt 07 [ normal door options ]
14271 	0x39, 0x05,                         // pushi 05
14272 	0x33, 0x04,                         // jmp 04 [ argument door options ]
14273 	PATCH_END
14274 };
14275 
14276 // The responses to the great hall door options in room 627 have problems. The
14277 //  floppy version is missing message handlers for Open Door and Knock on Door
14278 //  during the argument and so the door doesn't open. The CD version displays
14279 //  the wrong Knock on Door message. All versions fail to test flag 112 to see
14280 //  if the argument has already occurred.
14281 //
14282 // We fix all of this with a two part patch. First, sListened:register now
14283 //  controls which message is displayed before the door opens. This allows
14284 //  doorTopTeller:sayMessage to specify the correct message before running it.
14285 //  To add flag tests, new message handlers, and set sListened:register, we take
14286 //  advantage of identical message handlers in doorTopTeller:sayMessage. These
14287 //  provide plenty of room for new code that then jumps into another handler to
14288 //  continue and complete the work.
14289 //
14290 // Applies to: All versions
14291 // Responsible methods: sListened:changeState(0), doorTopTeller:sayMessage
14292 // Fixes bug: #10799
14293 static const uint16 qfg4Room627DoorResponsesSignature1[] = {
14294 	0x65, SIG_ADDTOOFFSET(+17),         // aTop state
14295 	0x38, SIG_SELECTOR16(say),          // pushi say
14296 	0x38, SIG_UINT16(0x0006),           // pushi 0006
14297 	0x39, 0x08,                         // pushi 08
14298 	0x38, SIG_UINT16(0x009b),           // pushi 009b
14299 	0x39, SIG_MAGICDWORD, 0x09,         // pushi 09
14300 	0x78,                               // push1
14301 	0x7c,                               // pushSelf
14302 	0x38, SIG_UINT16(0x026c),           // pushi 026c
14303 	0x81, 0x5b,                         // lag 5b
14304 	0x4a, SIG_UINT16(0x0010),           // send 10 [ gloryMessager say: 8 155 9 1 self 620 ]
14305 	0x32,                               // jmp ... [ end of method ]
14306 	SIG_END
14307 };
14308 
14309 static const uint16 qfg4Room627DoorResponsesPatch1[] = {
14310 	PATCH_ADDTOOFFSET(+21),
14311 	0x39, 0x06,                         // pushi 06
14312 	0x39, 0x08,                         // pushi 08
14313 	0x38, PATCH_UINT16(0x009b),         // pushi 009b
14314 	0x39, 0x09,                         // pushi 09
14315 	0x63, PATCH_GETORIGINALBYTEADJUST(+1, 0x10), // pToa register
14316 	0x02,                               // add
14317 	0x36,                               // push
14318 	0x78,                               // push1
14319 	0x7c,                               // pushSelf
14320 	0x38, PATCH_UINT16(0x026c),         // pushi 026c
14321 	0x81, 0x5b,                         // lag 5b
14322 	0x4a, PATCH_UINT16(0x0010),         // send 10 [ gloryMessager say: 8 155 (9 + register) 1 self 620 ]
14323 	PATCH_END
14324 };
14325 
14326 static const uint16 qfg4Room627DoorResponsesFloppySignature2[] = {
14327 	// Pick Lock (no argument) - missing flag check
14328 	SIG_MAGICDWORD,
14329 	0x30, SIG_UINT16(0x0072),           // bnt 0072 [ next message handler ]
14330 	0x81, 0x79,                         // lag 79 [ night ]
14331 	0x30, SIG_UINT16(0x0028),           // bnt 0028
14332 	0x89, 0x0b,                         // lsg 0b [ room number ]
14333 	0x34, SIG_UINT16(0x0273),           // ldi 627d
14334 	0x1a,                               // eq?
14335 	0x30, SIG_UINT16(0x001f),           // bnt 001f
14336 	0x38, SIG_UINT16(0x00fe),           // pushi clean [ hard-coded for floppy ]
14337 	0x76,                               // push0
14338 	0x54, SIG_UINT16(0x0004),           // self 04
14339 	0x78,                               // push1
14340 	0x39, 0x71,                         // pushi 71
14341 	0x45, 0x02, SIG_UINT16(0x0002),     // callb proc0_2 02
14342 	0x38, SIG_SELECTOR16(setScript),    // pushi setScript
14343 	0x78,                               // push1
14344 	0x72, SIG_UINT16(0x0082),           // lofsa sListened
14345 	0x36,                               // push
14346 	0x72, SIG_UINT16(0x01ae),           // lofsa pUpperDoor
14347 	0x4a, SIG_UINT16(0x0006),           // send 06 [ pUpperDoor setScript: sListened ]
14348 	0x32, SIG_UINT16(0x0131),           // jmp 0131
14349 	0x38, SIG_UINT16(0x0338),           // pushi trySkill [ hard-coded for floppy ]
14350 	0x7a,                               // push2
14351 	0x39, 0x09,                         // pushi 09
14352 	0x88, SIG_UINT16(0x01a6),           // lsg 01a6
14353 	0x81, 0x00,                         // lag 00
14354 	0x4a, SIG_UINT16(0x0008),           // send 08 [ hero trySkill: 9 global422 ]
14355 	SIG_ADDTOOFFSET(+0xe4),
14356 	// Open Door (no argument) - missing flag check
14357 	0x78,                               // push1
14358 	0x39, 0x71,                         // pushi 71
14359 	0x45, 0x02, SIG_UINT16(0x0002),     // callb proc0_2 02
14360 	0x38, SIG_UINT16(0x00fe),           // pushi clean [ hard-coded for floppy ]
14361 	0x76,                               // push0
14362 	0x54, SIG_UINT16(0x0004),           // self 04
14363 	SIG_END
14364 };
14365 
14366 static const uint16 qfg4Room627DoorResponsesFloppyPatch2[] = {
14367 	// Pick Lock (no argument) - missing flag check
14368 	0x30, PATCH_UINT16(0x001a),         // bnt 001a [ next message handler ]
14369 	PATCH_ADDTOOFFSET(+2),
14370 	0x30, PATCH_UINT16(0x00a1),         // bnt 00a1 [ normal pick lock code in duplicate handler ]
14371 	PATCH_ADDTOOFFSET(+6),
14372 	0x30, PATCH_UINT16(0x0098),         // bnt 0098 [ normal pick lock code in duplicate handler ]
14373 	0x78,                               // push1
14374 	0x39, 0x70,                         // pushi 70 [ flag 112 ]
14375 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb proc0_4 02 [ has argument occurred? ]
14376 	0x2e, PATCH_UINT16(0x008e),         // bt 008e [ normal pick lock code in duplicate handler ]
14377 	0x33, 0x6d,                         // jmp 6d  [ continue argument code in duplicate handler ]
14378 	// Open Door (argument) - missing from floppy
14379 	0x3c,                               // dup
14380 	0x35, 0x0a,                         // ldi 0a
14381 	0x1a,                               // eq?
14382 	0x31, 0x07,                         // bnt 07 [ next message handler ]
14383 	0x38, PATCH_SELECTOR16(register),   // pushi register
14384 	0x78,                               // push1
14385 	0x78,                               // push1 [ "Ignoring the voices, you fling the door open..." ]
14386 	0x33, 0x0b,                         // jmp 0b
14387 	// Knock on Door (argument) - missing from floppy
14388 	0x3c,                               // dup
14389 	0x35, 0x0b,                         // ldi 0b
14390 	0x1a,                               // eq?
14391 	0x31, 0x45,                         // bnt 45 [ next message handler ]
14392 	0x38, PATCH_SELECTOR16(register),   // pushi register
14393 	0x78,                               // push1
14394 	0x7a,                               // push2 [ "Very polite of you. The door opens to your knock..." ]
14395 	0x72, PATCH_UINT16(0x0082),         // lofsa sListened
14396 	0x4a, PATCH_UINT16(0x0006),         // send 0006 [ sListened register: 1 or 2 ]
14397 	0x32, PATCH_UINT16(0x004c),         // jmp 004c [ continue argument code in duplicate handler ]
14398 	PATCH_ADDTOOFFSET(+0xe4),
14399 	// Open Door (no argument) - missing flag check
14400 	0x78,                               // push1
14401 	0x39, 0x70,                         // pushi 70 [ flag 112 ]
14402 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb proc0_4 02 [ has argument occurred? ]
14403 	0x2f, 0x16,                         // bt 16 [ skip argument code if argument occurred ]
14404 	0x32, PATCH_UINT16(0xff5c),         // jmp ff5c [ continue argument code in duplicate handler ]
14405 	PATCH_END
14406 };
14407 
14408 static const uint16 qfg4Room627DoorResponsesCDSignature2[] = {
14409 	// Pick Lock (no argument) - missing flag check
14410 	0x38, SIG_UINT16(0x0101),           // pushi clean [ hard-coded for CD ]
14411 	0x76,                               // push0
14412 	SIG_MAGICDWORD,
14413 	0x54, SIG_UINT16(0x0004),           // self 04
14414 	0x78,                               // push1
14415 	0x39, 0x71,                         // pushi 71
14416 	0x45, 0x02, SIG_UINT16(0x0002),     // callb proc0_2 02
14417 	SIG_ADDTOOFFSET(+0x13b),
14418 	// Open Door (no argument) - missing flag check
14419 	0x78,                               // push1
14420 	0x39, 0x71,                         // pushi 71
14421 	0x45, 0x02, SIG_UINT16(0x0002),     // callb proc0_2 02
14422 	0x38, SIG_UINT16(0x0101),           // pushi clean [ hard-coded for CD ]
14423 	0x76,                               // push0
14424 	0x54, SIG_UINT16(0x0004),           // self 04
14425 	SIG_ADDTOOFFSET(+0x7c),
14426 	// Knock on Door (argument) - wrong message (CD regression)
14427 	0x78,                               // push1
14428 	0x39, 0x71,                         // pushi 71
14429 	0x45, 0x02, SIG_UINT16(0x0002),     // callb proc0_2 02
14430 	0x38, SIG_UINT16(0x0101),           // pushi clean [ hard-coded for CD ]
14431 	0x76,                               // push0
14432 	0x54, SIG_UINT16(0x0004),           // self 04
14433 	SIG_END
14434 };
14435 
14436 static const uint16 qfg4Room627DoorResponsesCDPatch2[] = {
14437 	// Pick Lock (no argument) - missing flag check
14438 	0x78,                               // push1
14439 	0x39, 0x70,                         // pushi 70 [ flag 112 ]
14440 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb proc0_4 02 [ has argument occurred? ]
14441 	0x2f, 0x16,                         // bt 16 [ skip argument code if argument occurred ]
14442 	0x32, PATCH_UINT16(0x0089),         // jmp 0089 [ continue argument code in duplicate handler ]
14443 	PATCH_ADDTOOFFSET(+0x13d),
14444 	// Open Door (no argument) - missing flag check
14445 	0x78,                               // push1
14446 	0x39, 0x70,                         // pushi 70 [ flag 112 ]
14447 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb proc0_4 02 [ has argument occurred? ]
14448 	0x2f, 0x16,                         // bt 16 [ skip argument code if argument occurred ]
14449 	0x32, PATCH_UINT16(0xff40),         // jmp ff40 [ continue argument code in duplicate handler ]
14450 	PATCH_ADDTOOFFSET(+0x7e),
14451 	// Knock on Door (argument) - wrong message (CD regression)
14452 	0x38, PATCH_SELECTOR16(register),   // pushi register
14453 	0x78,                               // push1
14454 	0x7a,                               // push2 [ "Very polite of you. The door opens to your knock..." ]
14455 	0x72, PATCH_UINT16(0x00b6),         // lofsa sListened
14456 	0x4a, PATCH_UINT16(0x0006),         // send 0006 [ sListened register: 2 ]
14457 	0x32, PATCH_UINT16(0xfeb4),         // jmp feb4 [ continue argument code in duplicate handler ]
14458 	PATCH_END
14459 };
14460 
14461 // When entering the great hall from room 627 while Katrina and Ad Avis argue,
14462 //  room 627 says that the door squeaks even if it was oiled. The flag tests are
14463 //  incorrect and out of sync with the logic in the great hall that plays the
14464 //  squeak that kills ego. This logic had other bugs in the floppy version,
14465 //  including setting an incorrect flag, and this patch fixes those too. The end
14466 //  result is that the squeak message isn't displayed if the oiled flag is set.
14467 //
14468 // Applies to: All versions
14469 // Responsible methods: sListened:changeState(2), sListened2:changeState(2) (CD)
14470 // Fixes bug: #10799
14471 static const uint16 qfg4Room627SqueakFloppySignature[] = {
14472 	0x89, 0x7d,                         // lsg 7d [ character type ]
14473 	0x35, SIG_MAGICDWORD, 0x02,         // ldi 02 [ thief ]
14474 	0x1a,                               // eq?    [ is thief? ]
14475 	0x31, 0x08,                         // bnt 08 [ skip oil test if thief (incorrect, removed from CD) ]
14476 	0x78,                               // push1
14477 	0x39, 0x72,                         // pushi 72 [ flag 114 ]
14478 	0x45, 0x04, SIG_UINT16(0x0002),     // callb proc0_4 02 [ is door oiled? ]
14479 	0x18,                               // not
14480 	0x2f, 0x07,                         // bt 07 [ squeak message ]
14481 	0x78,                               // push1
14482 	0x39, 0x71,                         // pushi 71 [ flag 113 ]
14483 	0x45, 0x04, SIG_UINT16(0x0002),     // callb proc0_4 02 [ selected action other than listening? (incorrect) ]
14484 	0x31, 0x33,                         // bnt 33 [ skip squeak message ]
14485 	SIG_ADDTOOFFSET(+20),
14486 	0x78,                               // push1
14487 	0x39, 0x71,                         // pushi 71 [ flag 113 ]
14488 	0x45, 0x02, SIG_UINT16(0x0002),     // callb proc0_2 02 [ set flag 113 (incorrect, removed from CD) ]
14489 	SIG_END
14490 };
14491 
14492 static const uint16 qfg4Room627SqueakFloppyPatch[] = {
14493 	0x33, 0x05,                         // jmp 05 [ skip thief test, always test oil flag ]
14494 	PATCH_ADDTOOFFSET(+15),
14495 	0x32, PATCH_UINT16(0x0039),         // jmp 0039 [ skip squeak message if door has been oiled ]
14496 	PATCH_ADDTOOFFSET(+26),
14497 	0x32, PATCH_UINT16(0x0004),         // jmp 0004 [ don't set flag 113 ]
14498 	PATCH_END
14499 };
14500 
14501 static const uint16 qfg4Room627SqueakCDSignature[] = {
14502 	0x78,                               // push1
14503 	0x39, 0x72,                         // pushi 72 [ flag 114 ]
14504 	0x45, 0x04, SIG_UINT16(0x0002),     // callb proc0_4 02 [ is door oiled? ]
14505 	SIG_MAGICDWORD,
14506 	0x18,                               // not
14507 	0x2f, 0x07,                         // bt 07 [ squeak message ]
14508 	0x78,                               // push1
14509 	0x39, 0x71,                         // pushi 71 [ flag 113 ]
14510 	0x45, 0x04, SIG_UINT16(0x0002),     // callb proc0_4 02 [ selected action other than listening? (incorrect) ]
14511 	0x31, 0x2c,                         // bnt 2c [ skip squeak message ]
14512 	SIG_END
14513 };
14514 
14515 static const uint16 qfg4Room627SqueakCDPatch[] = {
14516 	PATCH_ADDTOOFFSET(+10),
14517 	0x32, PATCH_UINT16(0x0032),         // jmp 0032 [ skip squeak message if door has been oiled ]
14518 	PATCH_END
14519 };
14520 
14521 // Looking through the keyhole in room 627 into the great hall responds with a
14522 //  generic message instead of the specific room description. sPeepingTom only
14523 //  says the great hall message at night, which isn't relevant, and didn't work
14524 //  since doorTeller wouldn't offer the keyhole option at night due to a missing
14525 //  flag check which we fix. We restore the message by removing the night test.
14526 //
14527 // Applies to: All versions
14528 // Responsible method: sPeepingTom:changeState(1)
14529 // Fixes bug: #10799
14530 static const uint16 qfg4GreatHallKeyholeSignature[] = {
14531 	SIG_MAGICDWORD,
14532 	0x81, 0x79,                         // lag 79 [ night ]
14533 	0x31, 0x1a,                         // bnt 1a [ skip keyhole message during day ]
14534 	0x38, SIG_SELECTOR16(say),          // pushi say
14535 	SIG_END
14536 };
14537 
14538 static const uint16 qfg4GreatHallKeyholePatch[] = {
14539 	0x33, 0x02,                         // jmp 02 [ say keyhole message regardless of time ]
14540 	PATCH_END
14541 };
14542 
14543 // You can talk to the Burgomeister in his office when he's not there. Clicking
14544 //  Talk on hero shows Burgomeister options even when alone because rm300:init
14545 //  initializes heroTeller no matter who is in the room.
14546 //
14547 // We fix this by only initializing heroTeller when someone is in the room. The
14548 //  Burgomeister appears when the time of day is 3 or less and Gypsy Davy
14549 //  appears during room events 4, 5, and 6.
14550 //
14551 // Applies to: All versions
14552 // Responsible method: rm300:init
14553 // Fixes bug: #10754
14554 static const uint16 qfg4EmptyBurgoRoomSignature[] = {
14555 	// start of heroTeller init: ...
14556 	0x38, SIG_SELECTOR16(init),         // pushi init
14557 	SIG_MAGICDWORD,
14558 	0x38, SIG_UINT16(0x0005),           // pushi 0005
14559 	0x89, 0x00,                         // lsg 00     [ hero ]
14560 	0x38, SIG_UINT16(0x012c),           // pushi 300d [ modNum ]
14561 	0x39, 0x19,                         // pushi 25d  [ noun ]
14562 	0x38, SIG_UINT16(0x0080),           // pushi 128d [ verb ]
14563 	0x8b, 0x00,                         // lsl 00 [ event number ]
14564 	0x3c,                               // dup
14565 	0x35, 0x01,                         // ldi 01
14566 	0x1a,                               // eq?
14567 	0x31, 0x05,                         // bnt 05
14568 	0x35, 0x10,                         // ldi 10 [ cond ]
14569 	0x32, SIG_UINT16(0x0048),           // jmp 0048
14570 	0x3c,                               // dup
14571 	0x35, 0x02,                         // ldi 02
14572 	0x1a,                               // eq?
14573 	0x31, 0x04,                         // bnt 04
14574 	0x35, 0x11,                         // ldi 11 [ cond ]
14575 	0x33, 0x3e,                         // jmp 3e
14576 	0x3c,                               // dup
14577 	0x35, 0x04,                         // ldi 04
14578 	0x1a,                               // eq?
14579 	0x31, 0x04,                         // bnt 04
14580 	0x35, 0x13,                         // ldi 13 [ cond ]
14581 	0x33, 0x34,                         // jmp 34
14582 	0x3c,                               // dup
14583 	0x35, 0x05,                         // ldi 05
14584 	SIG_END
14585 };
14586 
14587 static const uint16 qfg4EmptyBurgoRoomPatch[] = {
14588 	0x89, 0x7b,                         // lsg 7b
14589 	0x35, 0x03,                         // ldi 03
14590 	0x24,                               // le?   [ time of day <= 3 ]
14591 	0x2f, 0x0e,                         // bt 0e [ burgomeister is here, call heroTeller:init ]
14592 	0x8b, 0x00,                         // lsl 00
14593 	0x35, 0x04,                         // ldi 04
14594 	0x22,                               // lt?   [ event number < 4 ]
14595 	0x2f, 0x5f,                         // bt 5f [ no gypsy, skip heroTeller:init ]
14596 	0x8b, 0x00,                         // lsl 00
14597 	0x35, 0x06,                         // ldi 06
14598 	0x1e,                               // gt?   [ event number > 6 ]
14599 	0x2f, 0x58,                         // bt 58 [ no gypsy, skip heroTeller:init ]
14600 	// start of heroTeller init: ...
14601 	0x38, PATCH_SELECTOR16(init),       // pushi init
14602 	0x39, 0x05,                         // pushi 05
14603 	0x89, 0x00,                         // lsg 00     [ hero ]
14604 	0x89, 0x0b,                         // lsg 0b     [ modNum ]
14605 	0x39, 0x19,                         // pushi 25d  [ noun ]
14606 	0x38, PATCH_UINT16(0x0080),         // pushi 128d [ verb ]
14607 	0x83, 0x00,                         // lal 00 [ event number ]
14608 	0x36,                               // push
14609 	0x31, 0x13,                         // bnt 13 [ event number == 0, next condition ]
14610 	0x3c,                               // dup
14611 	0x35, 0x05,                         // ldi 05
14612 	0x1e,                               // gt?
14613 	0x2f, 0x0d,                         // bt 0d [ event number > 5, next condition ]
14614 	0x3c,                               // dup
14615 	0x35, 0x0f,                         // ldi 0f
14616 	0x02,                               // add [ cond = event number + 15d ]
14617 	0x33, 0x31,                         // jmp 31
14618 	PATCH_END
14619 };
14620 
14621 // Ad Avis and his necrotaurs chase and catch hero to take him to the dungeon,
14622 //  but this one-time event can repeat along with the entire dungeon sequence.
14623 //
14624 // The chase code is complex and spread across many scripts with many flags.
14625 //  There is no code that resets all the flags upon capture and no code that
14626 //  correctly tests if capture has occurred. This results in many ways to leave
14627 //  at least one flag set and repeat the chase. What these code paths all have
14628 //  in common is that the player has to walk through the town gate, room 290.
14629 //
14630 // We fix this by adding code to the start of rm290:init that clears all of the
14631 //  relevant chase flags if capture has already occurred. Fortunately, this
14632 //  method starts with unused debugging code that can be overwritten.
14633 //
14634 // Applies to: All versions
14635 // Responsible method: rm290:init
14636 // Fixes bug: #11056
14637 static const uint16 qfg4ChaseRepeatsSignature[] = {
14638 	SIG_MAGICDWORD,
14639 	0x81, 0xc9,                         // lag c9 [ debug mode ]
14640 	0x31, 0x4e,                         // bnt 4e [ skip debug code ]
14641 	0x78,                               // push1
14642 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa prompt
14643 	0x36,                               // push
14644 	0x46, SIG_UINT16(0xfaff),           // calle proc64255_1 02
14645 	      SIG_UINT16(0x0001),
14646 	      SIG_UINT16(0x0002),
14647 	0xa3, 0x02,                         // sal 02
14648 	0x39, 0x05,                         // pushi 05
14649 	0x36,                               // push
14650 	0x78,                               // push1
14651 	0x7a,                               // push2
14652 	0x39, 0x03,                         // pushi 03
14653 	0x39, 0x04,                         // pushi 04
14654 	0x46, SIG_UINT16(0xfde7),           // calle proc64999_5 0a
14655 	      SIG_UINT16(0x0005),
14656 	      SIG_UINT16(0x000a),
14657 	SIG_END
14658 };
14659 
14660 static const uint16 qfg4ChaseRepeatsPatch[] = {
14661 	0x78,                               // push1
14662 	0x39, 0x6e,                         // pushi 6e [ flag 110 ]
14663 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb proc0_4 02 [ have you been captured? ]
14664 	0x31, 0x49,                         // bnt 49 [ don't clear chase flags ]
14665 	0x78,                               // push1
14666 	0x39, 0x50,                         // pushi 50 [ flag 80 ]
14667 	0x45, 0x03, PATCH_UINT16(0x0002),   // callb proc0_3 02 [ clear chase flag 80 ]
14668 	0x78,                               // push1
14669 	0x39, 0x51,                         // pushi 51 [ flag 81 ]
14670 	0x45, 0x03, PATCH_UINT16(0x0002),   // callb proc0_3 02 [ clear chase flag 81 ]
14671 	0x78,                               // push1
14672 	0x38, PATCH_UINT16(0x00a4),         // pushi 00a4 [ flag 164 ]
14673 	0x45, 0x03, PATCH_UINT16(0x0002),   // callb proc0_3 02 [ clear chase flag 164 ]
14674 	0x33, 0x31,                         // jmp 31 [ continue room init ]
14675 	PATCH_END
14676 };
14677 
14678 // When the necrotaurs catch hero in the woods to take him to the dungeon, two
14679 //  different script bugs randomly cause interpreter errors. This patch fixes
14680 //  calling kUpdateScreenItem on a necrotaur with a deleted screen item.
14681 //
14682 // When hero is caught, the screen turns black before going to the dungeon.
14683 //  There is an inconsistent delay and the necrotaurs will sometimes reappear
14684 //  briefly on the black screen. These symptoms hint at the script's problems.
14685 //  sBlackOut sets a 300 cycle delay but this rarely has an effect. Instead each
14686 //  actor is hidden while their motions continue and sBlackOut advances when an
14687 //  invisible necrotaur completes its JumpTo motion. JumpTo stores its client's
14688 //  signal on initialization and unconditionally restores it upon completion. If
14689 //  JumpTo completes after View:hide calls kDeleteScreenItem and sets the hidden
14690 //  signal bit then signal is reverted and Actor:doit calls kUpdateScreenItem.
14691 //
14692 // We fix this by disposing of each cast member before painting black instead of
14693 //  hiding them. This hides them and terminates their motions. This exposes the
14694 //  previously unused 300 cycle delay, which is much longer than normal, so we
14695 //  also change that to 4 seconds. This is consistent with existing behavior
14696 //  and close to the delay used in room 290's working version of this script.
14697 //  The majority of this patch is to free up the single byte needed to change
14698 //  the 8-bit hide selector to 16-bit dispose.
14699 //
14700 // Several rooms that sBlackOut takes place in, such as 557, have doit methods
14701 //  that can initiate new necrotaur motions after the cast has been disposed,
14702 //  which also crashes. We fix this by clearing flag 35 as each of these rooms
14703 //  requires it to be set to set a necrotaur motion. This is the "hunt" flag.
14704 //  It's okay to clear it here as it gets cleared in the dungeon.
14705 //
14706 // Applies to: All versions
14707 // Responsible method: sBlackOut:changeState(3)
14708 // Fixes bug: #11056
14709 static const uint16 qfg4NecrotaurBlackoutSignature[] = {
14710 	0x31, 0x4f,                         // bnt 4f [ next state ]
14711 	SIG_ADDTOOFFSET(+11),
14712 	SIG_MAGICDWORD,
14713 	0x39, SIG_SELECTOR8(hide),          // pushi hide
14714 	0x81, 0x05,                         // lag 05
14715 	0x4a, SIG_UINT16(0x0006),           // send 06 [ cast eachElementDo: hide ]
14716 	0x78,                               // push1 [ kUpdatePlane param count ]
14717 	0x39, SIG_SELECTOR8(back),          // pushi back
14718 	0x78,                               // push1
14719 	0x76,                               // push0
14720 	0x39, SIG_ADDTOOFFSET(+1),          // pushi picture
14721 	0x78,                               // push1
14722 	0x39, 0xff,                         // pushi ff
14723 	0x38, SIG_ADDTOOFFSET(+2),          // pushi yourself [ returns self ]
14724 	0x76,                               // push0
14725 	0x76,                               // push0 [ plane ]
14726 	0x76,                               // push0
14727 	0x81, 0x02,                         // lag 02
14728 	0x4a, SIG_UINT16(0x0004),           // send 04 [ room plane? ]
14729 	0x4a, SIG_UINT16(0x0010),           // send 10 [ room:plane back: 0 picture: -1 yourself: ]
14730 	0x36,                               // push [ room:plane for kUpdatePlane ]
14731 	SIG_ADDTOOFFSET(+29),
14732 	0x34, SIG_UINT16(0x012c),           // ldi 012c
14733 	0x65, SIG_ADDTOOFFSET(+1),          // aTop cycles [ cycles = 300 ]
14734 	0x33, 0x1c,                         // jmp 1c [ end of method ]
14735 	0x3c,                               // dup
14736 	0x35, SIG_ADDTOOFFSET(+1),          // ldi 04 or 05
14737 	0x1a,                               // eq?
14738 	0x31, 0x16,                         // bnt 16 [ end of method ]
14739 	SIG_END
14740 };
14741 
14742 static const uint16 qfg4NecrotaurBlackoutPatch[] = {
14743 	0x31, 0x55,                         // bnt 55 [ next state ]
14744 	PATCH_ADDTOOFFSET(+11),
14745 	0x38, PATCH_SELECTOR16(dispose),    // pushi dispose
14746 	0x81, 0x05,                         // lag 05
14747 	0x4a, PATCH_UINT16(0x0006),         // send 06 [ cast eachElementDo: dispose ]
14748 	0x78,                               // push1 [ kUpdatePlane param count ]
14749 	0x76,                               // push0 [ plane ]
14750 	0x76,                               // push0
14751 	0x81, 0x02,                         // lag 02
14752 	0x4a, PATCH_UINT16(0x0004),         // send 04 [ room plane? ]
14753 	0x36,                               // push [ room:plane for kUpdatePlane ]
14754 	0x39, PATCH_SELECTOR8(back),        // pushi back
14755 	0x39, 0x01,                         // pushi 01
14756 	0x39, 0x00,                         // pushi 00
14757 	0x39, PATCH_GETORIGINALBYTE(+26),   // pushi picture
14758 	0x39, 0x01,                         // pushi 01
14759 	0x39, 0xff,                         // pushi ff
14760 	0x4a, PATCH_UINT16(0x000c),         // send 0c [ room:plane back: 0 picture: -1 ]
14761 	PATCH_ADDTOOFFSET(+29),
14762 	0x35, 0x04,                         // ldi 04
14763 	0x65, PATCH_GETORIGINALBYTEADJUST(+78, +2), // aTop seconds [ seconds = 4 ]
14764 	0x78,                               // push1
14765 	0x39, 0x23,                         // pushi 23
14766 	0x45, 0x03, PATCH_UINT16(0x0002),   // callb proc0_3 02 [ clear flag 35 ]
14767 	0x3a,                               // toss
14768 	0x48,                               // ret
14769 	PATCH_END
14770 };
14771 
14772 // When the necrotaurs catch hero in the woods to take him to the dungeon an
14773 //  effectively random crash from the Grooper class can occur in the CD version.
14774 //  See the inn door's script patch for details on these CD regressions.
14775 //
14776 // The error occurs when sBlackOut sets the motion of a necrotaur while it has
14777 //  no cycler. In this case the cycler should still be Walk but CD regressions
14778 //  can cause it to be cleared at unexpected times. We prevent this by setting
14779 //  necrotaur cyclers to Walk when setting their final PChase motions.
14780 //
14781 // Applies to: PC CD
14782 // Responsible method: sBlackOut:changeState(0)
14783 // Fixes bug: #11056
14784 static const uint16 qfg4NecrotaurCaptureSignature[] = {
14785 	SIG_MAGICDWORD,
14786 	0x18,                               // not
14787 	0x31, 0x38,                         // bnt 38
14788 	0x38, SIG_ADDTOOFFSET(+2),          // pushi distanceTo
14789 	0x78,                               // push1
14790 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa nec (nec1 or nec2 or nec3)
14791 	0x36,                               // push
14792 	0x81, 0x00,                         // lag 00
14793 	0x4a, SIG_UINT16(0x0006),           // send 06 [ hero distanceTo: nec ]
14794 	0x36,                               // push
14795 	0x35, 0x19,                         // ldi 19
14796 	0x1e,                               // gt?
14797 	0x31, 0x19,                         // bnt 19
14798 	0x38, SIG_SELECTOR16(setMotion),    // pushi setMotion
14799 	0x38, SIG_UINT16(0x0004),           // pushi 0004
14800 	0x51, 0x6c,                         // class PChase
14801 	0x36,                               // push
14802 	0x89, 0x00,                         // lsg 00
14803 	0x39, 0x19,                         // pushi 19
14804 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa nec
14805 	0x36,                               // push
14806 	0x72,                               // lofsa nec
14807 	SIG_END
14808 };
14809 
14810 static const uint16 qfg4NecrotaurCapturePatch[] = {
14811 	0x2f, 0x39,                         // bt 39
14812 	0x38, PATCH_GETORIGINALUINT16(+4),  // pushi distanceTo
14813 	0x78,                               // push1
14814 	0x74, PATCH_GETORIGINALUINT16(+8),  // lofss nec (nec1 or nec2 or nec3)
14815 	0x81, 0x00,                         // lag 00
14816 	0x4a, PATCH_UINT16(0x0006),         // send 06 [ hero distanceTo: nec ]
14817 	0x39, 0x19,                         // pushi 19
14818 	0x24,                               // le?
14819 	0x31, 0x1c,                         // bnt 1c
14820 	0x38, PATCH_SELECTOR16(setCycle),   // pushi setCycle
14821 	0x78,                               // push1
14822 	0x51, 0x17,                         // class Walk
14823 	0x36,                               // push
14824 	0x38, PATCH_SELECTOR16(setMotion),  // pushi setMotion
14825 	0x39, 0x04,                         // pushi 04
14826 	0x51, 0x6c,                         // class PChase
14827 	0x36,                               // push
14828 	0x89, 0x00,                         // lsg 00
14829 	0x39, 0x19,                         // pushi 19
14830 	0x72, PATCH_GETORIGINALUINT16(+8),  // lofsa nec
14831 	0x36,                               // push
14832 	PATCH_END
14833 };
14834 
14835 // When entering room 600 from the south at night, paladins receive a message
14836 //  about the two necrotaurs guarding the gate even when they're not there. The
14837 //  necrotaurs' appearance depends on flags, events, and if they've been killed,
14838 //  but the message code only tests if it's night.
14839 //
14840 // We fix this by only showing the message if there are at least 4 cast members
14841 //  in the room at night, which only occurs when the necrotaurs are present.
14842 //
14843 // Applies to: All versions
14844 // Responsible method: sFromSouth:changeState(1)
14845 // Fixes bug: #11057
14846 static const uint16 qfg4NecrotaurMessageSignature[] = {
14847 	0x30, SIG_UINT16(0x0052),           // bnt 0052 [ state 1 ]
14848 	SIG_ADDTOOFFSET(+0x4f),
14849 	SIG_MAGICDWORD,
14850 	0x32, SIG_UINT16(0x004b),           // jmp 004b [ end of method ]
14851 	0x3c,                               // dup
14852 	0x35, 0x01,                         // ldi 01
14853 	0x1a,                               // eq?
14854 	0x30, SIG_UINT16(0x0044),           // bnt 0044 [ end of method ]
14855 	0x81, 0x79,                         // lag 79 [ night ]
14856 	0x30, SIG_UINT16(0x0022),           // bnt 0022
14857 	0x89, 0x7d,                         // lsg 7d [ character type ]
14858 	0x35, 0x03,                         // ldi 03 [ paladin ]
14859 	0x1a,                               // eq?
14860 	0x30, SIG_UINT16(0x0011),           // bnt 0011 [ skip message ]
14861 	0x38, SIG_SELECTOR16(say),          // pushi say
14862 	0x38, SIG_UINT16(0x0003),           // pushi 0003
14863 	SIG_ADDTOOFFSET(+0x31),
14864 	0x3a,                               // toss
14865 	SIG_END
14866 };
14867 
14868 static const uint16 qfg4NecrotaurMessagePatch[] = {
14869 	0x3a,                               // toss
14870 	0x31, 0x50,                         // bnt 50 [ state 1 ]
14871 	PATCH_ADDTOOFFSET(+0x4f),
14872 	0x48,                               // ret
14873 	0x81, 0x79,                         // lag 79 [ night ]
14874 	0x31, 0x2c,                         // bnt 2c
14875 	0x7a,                               // push2
14876 	0x81, 0x7d,                         // lag 7d [ character type ]
14877 	0x22,                               // lt?
14878 	0x31, 0x1d,                         // bnt 1d [ skip message ]
14879 	0x39, 0x04,                         // pushi 04
14880 	0x39, PATCH_SELECTOR8(size),        // pushi size
14881 	0x76,                               // push0
14882 	0x81, 0x05,                         // lag 05
14883 	0x4a, PATCH_UINT16(0x0004),         // send 04 [ cast size? ]
14884 	0x24,                               // le?     [ at least 4 cast members? ]
14885 	0x31, 0x10,                         // bnt 10  [ skip message ]
14886 	0x38, PATCH_SELECTOR16(say),        // pushi say
14887 	0x39, 0x03,                         // pushi 03
14888 	PATCH_ADDTOOFFSET(+0x31),
14889 	0x48,                               // ret
14890 	PATCH_END
14891 };
14892 
14893 // When returning to the castle gate (room 600) from the dungeon (room 670) the
14894 //  gate options and messages can be out of sync with the gatekeeper's presence.
14895 //  Returning from the dungeon is room event 11, which causes rm600:init to
14896 //  advance the time to night and hide the gatekeeper, but this happens after
14897 //  rm600:init has initialized gateTeller based on whether it was day or night.
14898 //
14899 // We fix this by setting the night global before gateTeller is initialized
14900 //  during room event 11. Fortunately, the gateTeller code is preceded by a
14901 //  redundant condition which is always false and can be overwritten.
14902 //
14903 // Applies to: All versions
14904 // Responsible method: rm600:init
14905 // Fixes bug: #11044
14906 static const uint16 qfg4GateOptionsSignature[] = {
14907 	0x35, 0x0a,                         // ldi 0a [ event 10 ]
14908 	0x1a,                               // eq?    [ always false, tested earlier ]
14909 	0x31, SIG_ADDTOOFFSET(+1),          // bnt    [ gateTeller init ]
14910 	0x38, SIG_SELECTOR16(posn),         // pushi posn
14911 	0x7a,                               // push2
14912 	0x39, SIG_MAGICDWORD, 0xe2,         // pushi e2
14913 	0x39, 0x69,                         // pushi 69
14914 	0x72,                               // lofsa aGate
14915 	SIG_END
14916 };
14917 
14918 static const uint16 qfg4GateOptionsPatch[] = {
14919 	0x35, 0x0b,                         // ldi 0b [ event 11, came from dungeon ]
14920 	PATCH_ADDTOOFFSET(+3),
14921 	0x35, 0x01,                         // ldi 01
14922 	0xa1, 0x79,                         // sag 79 [ night = 1 ]
14923 	0x33, PATCH_GETORIGINALBYTEADJUST(+4, -6), // jmp [ gateTeller init ]
14924 	PATCH_END
14925 };
14926 
14927 // Six castle rooms contain a bug where oiling one door in the room effectively
14928 //  oils the rest when picking locks. sPickLock doesn't test which door is being
14929 //  opened, only their oil flags, and if any are set then it doesn't squeak.
14930 //
14931 // We fix this by adding code to test hero's position to determine which door is
14932 //  being opened and then test the correct flag. Ideally we should only need two
14933 //  versions of this patch: a two-door version and a three-door. Unfortunately,
14934 //  each two-door sPickLock is different. Script 634 adds an unnecessary room
14935 //  test, 643 and 644 transpose the left and right door flags, and script 661
14936 //  tests a flag lower than 128 which changes a key instruction size. Room 631's
14937 //  sPickLock is in script 634 and it is the only room that uses it.
14938 //
14939 // Applies to: All versions
14940 // Responsible method: sPickLock:changeState(1) in scripts 634, 640, 642, 643, 644, 661
14941 // Fixes bug: #10832
14942 static const uint16 qfg4Room631LockSqueakSignature[] = {
14943 	0x89, 0x0b,                         // lsg 0b
14944 	0x34, SIG_UINT16(0x0277),           // ldi 0277
14945 	0x1a,                               // eq? [ is room 631? (always true) ]
14946 	0x31, SIG_MAGICDWORD, 0x14,         // bnt 14 [ right flag test ]
14947 	0x78,                               // push1
14948 	0x38, SIG_UINT16(0x00d1),           // pushi 00d1 [ flag 209 ]
14949 	0x45, 0x04, SIG_UINT16(0x0002),     // callb proc0_4 02 [ is left door oiled? ]
14950 	0x31, 0x0a,                         // bnt 0a [ right flag test ]
14951 	0x38, SIG_SELECTOR16(cue),          // pushi cue
14952 	0x76,                               // push0
14953 	0x54, SIG_UINT16(0x0004),           // self 04 [ self cue: ]
14954 	SIG_ADDTOOFFSET(+21),
14955 	0x38, SIG_SELECTOR16(cue),          // pushi cue [ no squeak ]
14956 	SIG_ADDTOOFFSET(+7),
14957 	0x39, SIG_SELECTOR8(play),          // pushi play [ squeak ]
14958 	SIG_END
14959 };
14960 
14961 static const uint16 qfg4Room631LockSqueakPatch[] = {
14962 	0x38, PATCH_UINT16(0x00a0),         // pushi 00a0
14963 	0x78,                               // push1 [ x ]
14964 	0x76,                               // push0
14965 	0x81, 0x00,                         // lag 00
14966 	0x4a, PATCH_UINT16(0x0004),         // send 04 [ hero x? ]
14967 	0x1e,                               // gt? [ 160 > hero:x ]
14968 	0x31, 0x0f,                         // bnt 0f [ right flag test ]
14969 	0x78,                               // push1
14970 	0x38, PATCH_UINT16(0x00d1),         // pushi 00d1 [ flag 209 ]
14971 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb proc0_4 02 [ is left door oiled? ]
14972 	0x31, 0x21,                         // bnt 21 [ squeak ]
14973 	0x33, 0x15,                         // jmp 15 [ no squeak ]
14974 	PATCH_END
14975 };
14976 
14977 // rooms 640 and 642 have three doors
14978 static const uint16 qfg4Room640LockSqueakSignature[] = {
14979 	0x78,                               // push1
14980 	0x38, SIG_ADDTOOFFSET(+2),          // pushi middle flag
14981 	0x45, 0x04, SIG_UINT16(0x0002),     // callb proc0_4 02 [ is middle door oiled? ]
14982 	SIG_MAGICDWORD,
14983 	0x31, 0x0a,                         // bnt 0a [ left door flag test ]
14984 	0x38, SIG_SELECTOR16(cue),          // pushi cue
14985 	0x76,                               // push0
14986 	0x54, SIG_UINT16(0x0004),           // self 04 [ self cue: ]
14987 	0x32, SIG_ADDTOOFFSET(+2),          // jmp [ end of method ]
14988 	0x78,                               // push1
14989 	0x38, SIG_ADDTOOFFSET(+2),          // pushi left flag
14990 	0x45, 0x04, SIG_UINT16(0x0002),     // callb proc0_4 02 [ is left door oiled? ]
14991 	0x31, 0x0a,                         // bnt 0a [ right door flag test ]
14992 	0x38, SIG_SELECTOR16(cue),          // pushi cue
14993 	0x76,                               // push0
14994 	0x54, SIG_UINT16(0x0004),           // self 04 [ self cue: ]
14995 	0x32, SIG_ADDTOOFFSET(+2),          // jmp [ end of method ]
14996 	0x78,                               // push1
14997 	0x38, SIG_ADDTOOFFSET(+2),          // pushi right flag
14998 	0x45, 0x04, SIG_UINT16(0x0002),     // callb proc0_4 02 [ is right door oiled? ]
14999 	0x31, 0x0a,                         // bnt 0a [ squeak ]
15000 	0x38, SIG_SELECTOR16(cue),          // pushi cue
15001 	0x76,                               // push0
15002 	0x54, SIG_UINT16(0x0004),           // self 04 [ self cue: ]
15003 	SIG_ADDTOOFFSET(+3),
15004 	0x39, SIG_SELECTOR8(play),          // pushi play [ squeak ]
15005 	SIG_ADDTOOFFSET(+28),
15006 	0x38, SIG_SELECTOR16(cue),          // pushi cue [ no squeak ]
15007 	SIG_END
15008 };
15009 
15010 static const uint16 qfg4Room640LockSqueakPatch[] = {
15011 	0x39, 0x2e,                         // pushi 2e
15012 	0x78,                               // push1 [ x ]
15013 	0x76,                               // push0
15014 	0x81, 0x00,                         // lag 00
15015 	0x4a, PATCH_UINT16(0x0004),         // send 04 [ hero x? ]
15016 	0x1e,                               // gt? [ 46 > hero:x ]
15017 	0x31, 0x0c,                         // bnt 0c [ middle door test ]
15018 	0x78,                               // push1
15019 	0x38, PATCH_GETORIGINALUINT16(+22), // pushi left flag
15020 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb proc0_4 02 [ is left door oiled? ]
15021 	0x31, 0x26,                         // bnt 26 [ squeak ]
15022 	0x33, 0x42,                         // jmp 42 [ no squeak ]
15023 	0x60,                               // pprev [ hero:x ]
15024 	0x34, PATCH_UINT16(0x00e6),         // ldi 00e6
15025 	0x22,                               // lt? [ hero:x < 230 ]
15026 	0x31, 0x0c,                         // bnt 0c [ right flag test ]
15027 	0x78,                               // push1
15028 	0x38, PATCH_GETORIGINALUINT16(+2),  // pushi middle flag
15029 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb proc0_4 02 [ is middle door oiled? ]
15030 	0x31, 0x13,                         // bnt 13 [ squeak ]
15031 	0x33, 0x2f,                         // jmp 2f [ no squeak ]
15032 	0x78,                               // push1
15033 	0x38, PATCH_GETORIGINALUINT16(+42), // pushi right flag
15034 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb proc0_4 02 [ is right door oiled? ]
15035 	0x31, 0x07,                         // bnt 07 [ squeak ]
15036 	0x33, 0x23,                         // jmp 23 [ no squeak ]
15037 	PATCH_END
15038 };
15039 
15040 // room 643 has two doors but no room test as in script 634
15041 static const uint16 qfg4Room643LockSqueakSignature[] = {
15042 	0x78,                               // push1
15043 	0x38, SIG_ADDTOOFFSET(+2),          // pushi flag
15044 	0x45, 0x04, SIG_UINT16(0x0002),     // callb proc0_4 02 [ is door oiled? ]
15045 	SIG_MAGICDWORD,
15046 	0x31, 0x0a,                         // bnt 0a [ other door flag test ]
15047 	0x38, SIG_SELECTOR16(cue),          // pushi cue
15048 	0x76,                               // push0
15049 	0x54, SIG_UINT16(0x0004),           // self 04 [ self cue: ]
15050 	0x32, SIG_ADDTOOFFSET(+2),          // jmp [ end of method ]
15051 	0x78,                               // push1
15052 	0x38, SIG_ADDTOOFFSET(+2),          // pushi flag
15053 	0x45, 0x04, SIG_UINT16(0x0002),     // callb proc0_4 02 [ is other door oiled? ]
15054 	0x31, 0x0a,                         // bnt 0a [ squeak ]
15055 	0x38, SIG_SELECTOR16(cue),          // pushi cue
15056 	0x76,                               // push0
15057 	0x54, SIG_UINT16(0x0004),           // self 04 [ self cue: ]
15058 	SIG_ADDTOOFFSET(+3),
15059 	0x39, SIG_SELECTOR8(play),          // pushi play [ squeak ]
15060 	SIG_ADDTOOFFSET(+28),
15061 	0x38, SIG_SELECTOR16(cue),          // pushi cue [ no squeak ]
15062 	SIG_END
15063 };
15064 
15065 static const uint16 qfg4Room643LockSqueakPatch[] = {
15066 	0x38, PATCH_UINT16(0x00a0),         // pushi 00a0
15067 	0x78,                               // push1 [ x ]
15068 	0x76,                               // push0
15069 	0x81, 0x00,                         // lag 00
15070 	0x4a, PATCH_UINT16(0x0004),         // send 04 [ hero x? ]
15071 	0x1e,                               // gt? [ 160 > hero:x ]
15072 	0x31, 0x0c,                         // bnt 0c [ right flag test ]
15073 	0x78,                               // push1
15074 	0x38, PATCH_GETORIGINALUINT16(+22), // pushi left flag
15075 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb proc0_4 02 [ is left door oiled? ]
15076 	0x31, 0x11,                         // bnt 11 [ squeak ]
15077 	0x33, 0x2d,                         // jmp 2d [ no squeak ]
15078 	0x78,                               // push1
15079 	0x38, PATCH_GETORIGINALUINT16(+2),  // pushi right flag
15080 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb proc0_4 02 [ is right door oiled? ]
15081 	0x31, 0x05,                         // bnt 05 [ squeak ]
15082 	0x33, 0x21,                         // jmp 21 [ no squeak ]
15083 	PATCH_END
15084 };
15085 
15086 // room 644 is the same as 643 except the left and right door flags
15087 //  were transposed, so at least the 643 signature can be reused
15088 static const uint16 qfg4Room644LockSqueakPatch[] = {
15089 	0x38, PATCH_UINT16(0x00a0),         // pushi 00a0
15090 	0x78,                               // push1 [ x ]
15091 	0x76,                               // push0
15092 	0x81, 0x00,                         // lag 00
15093 	0x4a, PATCH_UINT16(0x0004),         // send 04 [ hero x? ]
15094 	0x1e,                               // gt? [ 160 > hero:x ]
15095 	0x31, 0x0c,                         // bnt 0c [ right flag test ]
15096 	0x78,                               // push1
15097 	0x38, PATCH_GETORIGINALUINT16(+2),  // pushi left flag
15098 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb proc0_4 02 [ is left door oiled? ]
15099 	0x31, 0x11,                         // bnt 11 [ squeak ]
15100 	0x33, 0x2d,                         // jmp 2d [ no squeak ]
15101 	0x78,                               // push1
15102 	0x38, PATCH_GETORIGINALUINT16(+22), // pushi right flag
15103 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb proc0_4 02 [ is right door oiled? ]
15104 	0x31, 0x05,                         // bnt 05 [ squeak ]
15105 	0x33, 0x21,                         // jmp 21 [ no squeak ]
15106 	PATCH_END
15107 };
15108 
15109 // room 661 is the same as 644 but has a different instruction size in the middle
15110 static const uint16 qfg4Room661LockSqueakSignature[] = {
15111 	0x78,                               // push1
15112 	0x38, SIG_UINT16(0x00e0),           // pushi 00e0 [ left flag ]
15113 	0x45, 0x04, SIG_UINT16(0x0002),     // callb proc0_4 02 [ is left door oiled? ]
15114 	SIG_MAGICDWORD,
15115 	0x31, 0x0a,                         // bnt 0a [ other door flag test ]
15116 	0x38, SIG_SELECTOR16(cue),          // pushi cue
15117 	0x76,                               // push0
15118 	0x54, SIG_UINT16(0x0004),           // self 04 [ self cue: ]
15119 	0x32, SIG_ADDTOOFFSET(+2),          // jmp [ end of method ]
15120 	0x78,                               // push1
15121 	0x39, 0x76,                         // pushi 76 [ right flag ]
15122 	0x45, 0x04, SIG_UINT16(0x0002),     // callb proc0_4 02 [ is right door oiled? ]
15123 	0x31, 0x0a,                         // bnt 0a [ squeak ]
15124 	0x38, SIG_SELECTOR16(cue),          // pushi cue
15125 	0x76,                               // push0
15126 	0x54, SIG_UINT16(0x0004),           // self 04 [ self cue: ]
15127 	SIG_ADDTOOFFSET(+3),
15128 	0x39, SIG_SELECTOR8(play),          // pushi play [ squeak ]
15129 	SIG_ADDTOOFFSET(+28),
15130 	0x38, SIG_SELECTOR16(cue),          // pushi cue [ no squeak ]
15131 	SIG_END
15132 };
15133 
15134 static const uint16 qfg4Room661LockSqueakPatch[] = {
15135 	0x38, PATCH_UINT16(0x00a0),         // pushi 00a0
15136 	0x78,                               // push1 [ x ]
15137 	0x76,                               // push0
15138 	0x81, 0x00,                         // lag 00
15139 	0x4a, PATCH_UINT16(0x0004),         // send 04 [ hero x? ]
15140 	0x1e,                               // gt? [ 160 > hero:x ]
15141 	0x31, 0x0c,                         // bnt 0c [ right flag test ]
15142 	0x78,                               // push1
15143 	0x38, PATCH_UINT16(0x00e0),         // pushi 00e0 [ left flag ]
15144 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb proc0_4 02 [ is left door oiled? ]
15145 	0x31, 0x10,                         // bnt 10 [ squeak ]
15146 	0x33, 0x2c,                         // jmp 2c [ no squeak ]
15147 	0x78,                               // push1
15148 	0x39, 0x76,                         // pushi 76 [ right flag ]
15149 	0x45, 0x04, PATCH_UINT16(0x0002),   // callb proc0_4 02 [ is right door oiled? ]
15150 	0x31, 0x05,                         // bnt 05 [ squeak ]
15151 	0x33, 0x21,                         // jmp 21 [ no squeak ]
15152 	PATCH_END
15153 };
15154 
15155 // When exiting the Thieves' Guild secret passage (room 340) to the town bridge
15156 //  (room 290), hero appears in an out of bounds area on the far right of the
15157 //  screen for 120 ticks and then abruptly teleports to the bridge secret exit.
15158 //  This is due to not initializing hero until after the 120 tick delay, causing
15159 //  him to be initially visible in his position from the previous room.
15160 //
15161 // We fix this by hiding hero initially so that he appears when emerging from
15162 //  the secret exit under the bridge after the delay.
15163 //
15164 // Applies to: All versions
15165 // Responsible method: sThiefEnter:changeState
15166 // Fixes bug: #10774
15167 static const uint16 qfg4BridgeSecretExitSignature[] = {
15168 	0x3c,                               // dup
15169 	0x35, 0x00,                         // ldi 00
15170 	0x1a,                               // eq?
15171 	SIG_MAGICDWORD,
15172 	0x31, 0x07,                         // bnt 07 [ state 1 ]
15173 	0x35, 0x78,                         // ldi 78
15174 	0x65, SIG_ADDTOOFFSET(+1),          // aTop ticks [ ticks = 120 ]
15175 	0x32, SIG_UINT16(0x00dd),           // jmp 00dd [ end of method ]
15176 	0x3c,                               // dup
15177 	0x35, 0x01,                         // ldi 01
15178 	0x1a,                               // eq?
15179 	0x30, SIG_UINT16(0x0020),           // bnt 0020 [ state 2 ]
15180 	SIG_END
15181 };
15182 
15183 static const uint16 qfg4BridgeSecretExitPatch[] = {
15184 	0x2f, 0x0c,                         // bt 0c [ state 1 ]
15185 	0x39, PATCH_SELECTOR8(hide),        // pushi hide
15186 	0x76,                               // push0
15187 	0x81, 0x00,                         // lag 00
15188 	0x4a, PATCH_UINT16(0x0004),         // send 04 [ hero hide: ]
15189 	0x35, 0x78,                         // ldi 78
15190 	0x65, PATCH_GETORIGINALBYTE(+9),    // aTop ticks [ ticks = 120 ]
15191 	0x3c,                               // dup
15192 	0x35, 0x01,                         // ldi 01
15193 	0x1a,                               // eq?
15194 	0x31, 0x20,                         // bnt 20 [ state 2 ]
15195 	PATCH_END
15196 };
15197 
15198 // The bone cage in room 770 has two problems. Clicking Do and selecting Jump
15199 //  Out of Cage as a thief leaves the cursor stuck. Selecting Break Cage as a
15200 //  fighter allows the player to click during what's supposed to be a handsOff
15201 //  script and break the game. Both bugs are due to egoTeller:sayMessage. Its
15202 //  Jump handler is missing a call to self:clean and its Break Cage handler
15203 //  incorrectly calls self:clean after running sBreakBones instead of before.
15204 //
15205 // We fix this by calling self:clean before running sBreakBones or sJumpOut.
15206 //
15207 // Applies to: All versions
15208 // Responsible method: egoTeller:sayMessage
15209 // Fixes bug: #11238
15210 static const uint16 qfg4BoneCageTellerSignature[] = {
15211 	0x30, SIG_UINT16(0x001b),           // bnt 001b
15212 	0x38, SIG_SELECTOR16(setScript),    // pushi setScript
15213 	0x38, SIG_UINT16(0x0003),           // pushi 0003
15214 	0x72, SIG_ADDTOOFFSET(+2),          // lofsa sBreakBones
15215 	0x36,                               // push
15216 	0x76,                               // push0
15217 	0x76,                               // push0
15218 	0x81, 0x02,                         // lag 02
15219 	0x4a, SIG_UINT16(0x000a),           // send 0a [ rm770 setScript: sBreakBones 0 0 ]
15220 	0x38, SIG_ADDTOOFFSET(+2),          // pushi clean
15221 	0x76,                               // push0
15222 	0x54, SIG_UINT16(0x0004),           // self 04 [ self clean: ]
15223 	0x32, SIG_UINT16(0x0021),           // jmp 0021 [ end of method ]
15224 	SIG_MAGICDWORD,
15225 	0x3c,                               // dup
15226 	0x35, 0x23,                         // ldi 23 [ "Jump Out of Cage" ]
15227 	0x1a,                               // eq?
15228 	0x30, SIG_UINT16(0x0010),           // bnt 0010
15229 	SIG_END
15230 };
15231 
15232 static const uint16 qfg4BoneCageTellerPatch[] = {
15233 	0x31, 0x15,                         // bnt 15
15234 	0x38, PATCH_GETORIGINALUINT16(+21), // pushi clean
15235 	0x76,                               // push0
15236 	0x54, PATCH_UINT16(0x0004),         // self 0004 [ self clean: ]
15237 	0x38, PATCH_SELECTOR16(setScript),  // pushi setScript
15238 	0x78,                               // push1
15239 	0x74, PATCH_GETORIGINALUINT16(+10), // lofss sBreakBones
15240 	0x81, 0x02,                         // lag 02
15241 	0x4a, PATCH_UINT16(0x0006),         // send 06 [ rm770 setScript: sBreakBones ]
15242 	0x3a,                               // toss
15243 	0x48,                               // ret
15244 	0x3c,                               // dup
15245 	0x35, 0x23,                         // ldi 23 [ "Jump Out of Cage" ]
15246 	0x1a,                               // eq?
15247 	0x30, PATCH_UINT16(0x0017),         // bnt 0017
15248 	0x38, PATCH_GETORIGINALUINT16(+21), // pushi clean
15249 	0x76,                               // push0
15250 	0x54, PATCH_UINT16(0x0004),         // self 04 [ self clean: ]
15251 	PATCH_END
15252 };
15253 
15254 //          script, description,                                     signature                      patch
15255 static const SciScriptPatcherEntry qfg4Signatures[] = {
15256 	{  true,     0, "prevent autosave from deleting save games",   1, qfg4AutosaveSignature,         qfg4AutosavePatch },
15257 	{  true,     0, "fix inventory leaks across restarts",         1, qfg4RestartSignature,          qfg4RestartPatch },
15258 	{  true,     1, "disable volume reset on startup",             1, sci2VolumeResetSignature,      sci2VolumeResetPatch },
15259 	{  true,     1, "disable video benchmarking",                  1, qfg4BenchmarkSignature,        qfg4BenchmarkPatch },
15260 	{  true,     7, "fix consecutive moonrises",                   1, qfg4MoonriseSignature,         qfg4MoonrisePatch },
15261 	{  true,    10, "fix setLooper calls (2/2)",                   2, qfg4SetLooperSignature2,       qfg4SetLooperPatch2 },
15262 	{  true,    11, "fix spell effect disposal",                   1, qfg4EffectDisposalSignature,   qfg4EffectDisposalPatch },
15263 	{  true,    11, "fix trigger after summon staff (1/2)",        1, qfg4TriggerStaffSignature1,    qfg4TriggerStaffPatch1 },
15264 	{  true,    11, "fix trigger after summon staff (2/2)",        1, qfg4TriggerStaffSignature2,    qfg4TriggerStaffPatch2 },
15265 	{  true,    13, "fix spell effect disposal",                   1, qfg4EffectDisposalSignature,   qfg4EffectDisposalPatch },
15266 	{  true,    28, "fix lingering rations icon after eating",     1, qfg4LeftoversSignature,        qfg4LeftoversPatch },
15267 	{  true,    31, "fix setScaler calls",                         1, qfg4SetScalerSignature,        qfg4SetScalerPatch },
15268 	{  true,    41, "fix conditional void calls",                  3, qfg4ConditionalVoidSignature,  qfg4ConditionalVoidPatch },
15269 	{  true,    50, "fix random revenant kopeks",                  1, qfg4SearchRevenantSignature,   qfg4SearchRevenantPatch },
15270 	{  true,    51, "Floppy: fix ad avis capture lockup",          1, qfg4AdAvisCaptureSignature,    qfg4AdAvisCapturePatch },
15271 	{  true,    51, "fix necrotaur blackout",                      1, qfg4NecrotaurBlackoutSignature,qfg4NecrotaurBlackoutPatch },
15272 	{  true,    51, "CD: fix necrotaur capture",                   3, qfg4NecrotaurCaptureSignature, qfg4NecrotaurCapturePatch },
15273 	{  true,    53, "NRS: fix wraith lockup",                      1, qfg4WraithLockupNrsSignature,  qfg4WraithLockupNrsPatch },
15274 	{  true,    83, "fix incorrect array type",                    1, qfg4TrapArrayTypeSignature,    qfg4TrapArrayTypePatch },
15275 	{  true,   140, "fix character selection",                     1, qfg4CharacterSelectSignature,  qfg4CharacterSelectPatch },
15276 	{  true,   250, "fix hectapus death lockup",                   1, qfg4HectapusDeathSignature,    qfg4HectapusDeathPatch },
15277 	{  true,   260, "CD: fix inn door crash",                      1, qfg4InnDoorCDSignature,        qfg4InnDoorCDPatch },
15278 	{  true,   270, "fix town gate after a staff dream",           1, qfg4DreamGateSignature,        qfg4DreamGatePatch },
15279 	{  true,   270, "fix town gate doormat at night",              1, qfg4TownGateDoormatSignature,  qfg4TownGateDoormatPatch },
15280 	{  true,   290, "fix chase repeating",                         1, qfg4ChaseRepeatsSignature,     qfg4ChaseRepeatsPatch },
15281 	{  true,   290, "fix bridge secret exit",                      1, qfg4BridgeSecretExitSignature, qfg4BridgeSecretExitPatch },
15282 	{  true,   300, "fix empty burgomeister room teller",          1, qfg4EmptyBurgoRoomSignature,   qfg4EmptyBurgoRoomPatch },
15283 	{  true,   320, "fix pathfinding at the inn",                  1, qfg4InnPathfindingSignature,   qfg4InnPathfindingPatch },
15284 	{  true,   320, "fix talking to absent innkeeper",             1, qfg4AbsentInnkeeperSignature,  qfg4AbsentInnkeeperPatch },
15285 	{  true,   320, "CD: fix domovoi never appearing",             1, qfg4DomovoiInnSignature,       qfg4DomovoiInnPatch },
15286 	{  true,   324, "CD: fix domovoi never appearing",             1, qfg4DomovoiInnSignature,       qfg4DomovoiInnPatch },
15287 	{  true,   340, "CD/Floppy: fix guild tunnel access (1/3)",    1, qfg4GuildWalkSignature1,       qfg4GuildWalkPatch1 },
15288 	{  true,   340, "CD/Floppy: fix guild tunnel access (2/3)",    1, qfg4GuildWalkSignature2,       qfg4GuildWalkPatch2 },
15289 	{  false,  340, "CD: fix guild tunnel access (3/3)",           1, qfg4GuildWalkCDSignature3,     qfg4GuildWalkCDPatch3 },
15290 	{  false,  340, "Floppy: fix guild tunnel access (3/3)",       1, qfg4GuildWalkFloppySignature3, qfg4GuildWalkFloppyPatch3 },
15291 	{  true,   440, "fix setLooper calls (1/2)",                   1, qfg4SetLooperSignature1,       qfg4SetLooperPatch1 },
15292 	{  true,   475, "fix tarot 3 queen card",                      1, qfg4Tarot3QueenSignature,      qfg4Tarot3QueenPatch },
15293 	{  true,   475, "fix tarot 3 death card",                      1, qfg4Tarot3DeathSignature,      qfg4Tarot3DeathPatch },
15294 	{  true,   475, "fix tarot 3 two of cups placement",           1, qfg4Tarot3TwoOfCupsSignature,  qfg4Tarot3TwoOfCupsPatch },
15295 	{  true,   475, "fix tarot 3 card priority",                   1, qfg4Tarot3PrioritySignature,   qfg4Tarot3PriorityPatch },
15296 	{  true,   475, "fix tarot 5 card priority",                   1, qfg4Tarot5PrioritySignature,   qfg4Tarot5PriorityPatch },
15297 	{  false,  500, "CD: fix rope during Igor rescue (1/2)",       1, qfg4GraveyardRopeSignature1,   qfg4GraveyardRopePatch1 },
15298 	{  false,  500, "CD: fix rope during Igor rescue (2/2)",       1, qfg4GraveyardRopeSignature2,   qfg4GraveyardRopePatch2 },
15299 	{  true,   530, "fix setLooper calls (1/2)",                   4, qfg4SetLooperSignature1,       qfg4SetLooperPatch1 },
15300 	{  true,   535, "fix setLooper calls (1/2)",                   4, qfg4SetLooperSignature1,       qfg4SetLooperPatch1 },
15301 	{  true,   541, "fix setLooper calls (1/2)",                   5, qfg4SetLooperSignature1,       qfg4SetLooperPatch1 },
15302 	{  true,   542, "fix setLooper calls (1/2)",                   5, qfg4SetLooperSignature1,       qfg4SetLooperPatch1 },
15303 	{  true,   543, "fix setLooper calls (1/2)",                   5, qfg4SetLooperSignature1,       qfg4SetLooperPatch1 },
15304 	{  true,   545, "fix setLooper calls (1/2)",                   5, qfg4SetLooperSignature1,       qfg4SetLooperPatch1 },
15305 	{  true,   557, "fix forest 557 entry from east",              1, qfg4Forest557PathfindingSignature, qfg4Forest557PathfindingPatch },
15306 	{  true,   600, "fix passable closed gate after geas",         1, qfg4DungeonGateSignature,      qfg4DungeonGatePatch },
15307 	{  true,   600, "fix gate options after geas",                 1, qfg4GateOptionsSignature,      qfg4GateOptionsPatch },
15308 	{  true,   600, "fix paladin's necrotaur message",             1, qfg4NecrotaurMessageSignature, qfg4NecrotaurMessagePatch },
15309 	{  true,   630, "fix great hall entry from barrel room",       1, qfg4GreatHallEntrySignature,   qfg4GreatHallEntryPatch },
15310 	{  true,   633, "fix stairway pathfinding",                    1, qfg4StairwayPathfindingSignature, qfg4StairwayPathfindingPatch },
15311 	{  true,   633, "Floppy: fix argument message",                1, qfg4ArgumentMessageFloppySignature,  qfg4ArgumentMessageFloppyPatch },
15312 	{  true,   633, "fix room 627 door options",                   1, qfg4Room627DoorOptionsSignature, qfg4Room627DoorOptionsPatch },
15313 	{  true,   633, "fix room 627 door responses (1/2)",           1, qfg4Room627DoorResponsesSignature1, qfg4Room627DoorResponsesPatch1 },
15314 	{  true,   633, "Floppy: fix room 627 door responses (2/2)",   1, qfg4Room627DoorResponsesFloppySignature2, qfg4Room627DoorResponsesFloppyPatch2 },
15315 	{  true,   633, "CD: fix room 627 door responses (2/2)",       1, qfg4Room627DoorResponsesCDSignature2, qfg4Room627DoorResponsesCDPatch2 },
15316 	{  true,   633, "Floppy: fix room 627 door squeak",            1, qfg4Room627SqueakFloppySignature,  qfg4Room627SqueakFloppyPatch },
15317 	{  true,   633, "CD: fix room 627 door squeak",                2, qfg4Room627SqueakCDSignature,  qfg4Room627SqueakCDPatch },
15318 	{  true,   633, "fix great hall keyhole message",              1, qfg4GreatHallKeyholeSignature, qfg4GreatHallKeyholePatch },
15319 	{  true,   634, "fix room 631 pick lock squeak",               1, qfg4Room631LockSqueakSignature,qfg4Room631LockSqueakPatch },
15320 	{  true,   640, "fix room 640 pick lock squeak",               1, qfg4Room640LockSqueakSignature,qfg4Room640LockSqueakPatch },
15321 	{  true,   642, "fix room 642 pick lock squeak",               1, qfg4Room640LockSqueakSignature,qfg4Room640LockSqueakPatch },
15322 	{  true,   643, "fix room 643 pick lock squeak",               1, qfg4Room643LockSqueakSignature,qfg4Room643LockSqueakPatch },
15323 	{  true,   643, "fix iron safe's east door sending hero west", 1, qfg4SafeDoorEastSignature,     qfg4SafeDoorEastPatch },
15324 	{  true,   643, "fix iron safe's door oil flags",              1, qfg4SafeDoorOilSignature,      qfg4SafeDoorOilPatch },
15325 	{  true,   644, "fix castle door open message for rogue",      2, qfg4StuckDoorSignature,        qfg4StuckDoorPatch },
15326 	{  true,   644, "fix peer bats, lower door",                   1, qfg4LowerPeerBatsSignature,    qfg4LowerPeerBatsPatch },
15327 	{  true,   644, "fix room 644 pick lock squeak",               1, qfg4Room643LockSqueakSignature,qfg4Room644LockSqueakPatch },
15328 	{  true,   645, "fix extraneous door sound in the castle",     1, qfg4DoubleDoorSoundSignature,  qfg4DoubleDoorSoundPatch },
15329 	{  true,   661, "fix room 661 pick lock squeak",               1, qfg4Room661LockSqueakSignature,qfg4Room661LockSqueakPatch },
15330 	{  false,  663, "CD: fix crest bookshelf",                     1, qfg4CrestBookshelfCDSignature,     qfg4CrestBookshelfCDPatch },
15331 	{  false,  663, "Floppy: fix crest bookshelf",                 1, qfg4CrestBookshelfFloppySignature, qfg4CrestBookshelfFloppyPatch },
15332 	{  true,   663, "CD/Floppy: fix crest bookshelf motion",       1, qfg4CrestBookshelfMotionSignature, qfg4CrestBookshelfMotionPatch },
15333 	{  true,   663, "CD/Floppy: fix peer bats, upper door (1/2)",  1, qfg4UpperPeerBatsSignature1,       qfg4UpperPeerBatsPatch1 },
15334 	{  false,  663, "CD: fix peer bats, upper door (2/2)",         1, qfg4UpperPeerBatsCDSignature2,     qfg4UpperPeerBatsCDPatch2 },
15335 	{  false,  663, "Floppy: fix peer bats, upper door (2/2)",     1, qfg4UpperPeerBatsFloppySignature2, qfg4UpperPeerBatsFloppyPatch2 },
15336 	{  true,   670, "fix look dungeon message",                    1, qfg4LookDungeonSignature,      qfg4LookDungeonPatch },
15337 	{  true,   710, "fix tentacle wriggle cycler",                 1, qfg4TentacleWriggleSignature,  qfg4TentacleWrigglePatch },
15338 	{  true,   710, "fix tentacle retraction for fighter",         1, qfg4PitRopeFighterSignature,   qfg4PitRopeFighterPatch },
15339 	{  true,   710, "fix tentacle retraction for mage (1/2)",      1, qfg4PitRopeMageSignature1,     qfg4PitRopeMagePatch1 },
15340 	{  true,   710, "NRS: fix tentacle retraction for mage (1/2)", 1, qfg4PitRopeMageNrsSignature1,  qfg4PitRopeMageNrsPatch1 },
15341 	{  true,   710, "fix tentacle retraction for mage (2/2)",      1, qfg4PitRopeMageSignature2,     qfg4PitRopeMagePatch2 },
15342 	{  true,   730, "fix ad avis timeout",                         1, qfg4AdAvisTimeoutSignature,    qfg4AdAvisTimeoutPatch },
15343 	{  true,   730, "Floppy: fix casting spells at ad avis",       1, qfg4AdAvisSpellsFloppySignature, qfg4AdAvisSpellsFloppyPatch },
15344 	{  true,   730, "CD: fix casting spells at ad avis",           2, qfg4AdAvisSpellsCDSignature,   qfg4AdAvisSpellsCDPatch },
15345 	{  true,   730, "NRS: fix casting spells at ad avis",          2, qfg4AdAvisSpellsNrsSignature,  qfg4AdAvisSpellsNrsPatch },
15346 	{  true,   730, "fix casting last spell at ad avis",           1, qfg4AdAdvisLastSpellSignature, qfg4AdAdvisLastSpellPatch },
15347 	{  true,   730, "fix ad avis projectile message",              1, qfg4AdAvisMessageSignature,    qfg4AdAvisMessagePatch },
15348 	{  true,   730, "fix throwing weapons at ad avis",             1, qfg4AdAvisThrowWeaponSignature,qfg4AdAvisThrowWeaponPatch },
15349 	{  true,   730, "fix fighter's spear animation",               1, qfg4FighterSpearSignature,     qfg4FighterSpearPatch },
15350 	{  true,   770, "fix bone cage teller",                        1, qfg4BoneCageTellerSignature,   qfg4BoneCageTellerPatch },
15351 	{  true,   800, "fix setScaler calls",                         1, qfg4SetScalerSignature,        qfg4SetScalerPatch },
15352 	{  true,   800, "fix grapnel removing hero's scaler",          1, qfg4RopeScalerSignature,       qfg4RopeScalerPatch },
15353 	{  true,   801, "fix runes puzzle (1/2)",                      1, qfg4RunesPuzzleSignature1,     qfg4RunesPuzzlePatch1 },
15354 	{  true,   801, "fix runes puzzle (2/2)",                      1, qfg4RunesPuzzleSignature2,     qfg4RunesPuzzlePatch2 },
15355 	{  true,   803, "CD: fix sliding down slope",                  1, qfg4SlidingDownSlopeCDSignature, qfg4SlidingDownSlopeCDPatch },
15356 	{  true,   803, "CD: fix walking up slippery slope",           1, qfg4WalkUpSlopeCDSignature,    qfg4WalkUpSlopeCDPatch },
15357 	{  true,   803, "CD: fix walking down slippery slope",         1, qfg4WalkDownSlopeCDSignature,  qfg4WalkDownSlopeCDPatch },
15358 	{  true,   803, "NRS: fix walking down slippery slope",        1, qfg4WalkDownSlopeNrsSignature, qfg4WalkDownSlopeNrsPatch },
15359 	{  true,   820, "fix rabbit combat",                           1, qfg4RabbitCombatSignature,     qfg4RabbitCombatPatch },
15360 	{  true,   810, "fix conditional void calls",                  1, qfg4ConditionalVoidSignature,  qfg4ConditionalVoidPatch },
15361 	{  true,   830, "fix conditional void calls",                  2, qfg4ConditionalVoidSignature,  qfg4ConditionalVoidPatch },
15362 	{  true,   835, "fix conditional void calls",                  3, qfg4ConditionalVoidSignature,  qfg4ConditionalVoidPatch },
15363 	{  true,   840, "fix conditional void calls",                  2, qfg4ConditionalVoidSignature,  qfg4ConditionalVoidPatch },
15364 	{  true,   855, "fix conditional void calls",                  1, qfg4ConditionalVoidSignature,  qfg4ConditionalVoidPatch },
15365 	{  true,   870, "fix conditional void calls",                  5, qfg4ConditionalVoidSignature,  qfg4ConditionalVoidPatch },
15366 	{  true, 64990, "increase number of save games (1/2)",         1, sci2NumSavesSignature1,        sci2NumSavesPatch1 },
15367 	{  true, 64990, "increase number of save games (2/2)",         1, sci2NumSavesSignature2,        sci2NumSavesPatch2 },
15368 	{  true, 64990, "disable change directory button",             1, sci2ChangeDirSignature,        sci2ChangeDirPatch },
15369 	SCI_SIGNATUREENTRY_TERMINATOR
15370 };
15371 
15372 #endif
15373 
15374 // ===========================================================================
15375 //  script 298 of sq4/floppy has an issue. object "nest" uses another property
15376 //   which isn't included in property count. We return 0 in that case, so that
15377 //   the game does not increment nest::x. The problem is that the script also
15378 //   checks if x exceeds nest::x. We never reach that of course, so the
15379 //   incorrect property means that the pterodactyl flight will continue
15380 //	 endlessly. We could either calculate the property count differently,
15381 //   thereby fixing this bug, but I think that just patching it out is cleaner.
15382 // Fixes bug: #5093
15383 static const uint16 sq4FloppySignatureEndlessFlight[] = {
15384 	0x39, 0x04,                         // pushi 04 (selector x)
15385 	SIG_MAGICDWORD,
15386 	0x78,                               // push1
15387 	0x67, 0x08,                         // pTos 08 (property x)
15388 	0x63, SIG_ADDTOOFFSET(+1),          // pToa (invalid property) - 44h for English floppy, 4ch for German floppy
15389 	0x02,                               // add
15390 	SIG_END
15391 };
15392 
15393 static const uint16 sq4FloppyPatchEndlessFlight[] = {
15394 	PATCH_ADDTOOFFSET(+5),
15395 	0x35, 0x03,                         // ldi 03 (which would be the content of the property)
15396 	PATCH_END
15397 };
15398 
15399 // Floppy-only: When the player tries to throw something at the sequel police
15400 //   in Space Quest X (zero g zone), the game will first show a textbox and
15401 //   then cause a signature mismatch in ScummVM. In Sierra SCI, it'd crash the
15402 //   whole game - or, when the Sierra "patch" got applied, display garbage.
15403 //
15404 // All of this is caused by a typo in the script. Right after the code for
15405 //  showing the textbox, there is similar code for showing another textbox, but
15406 //  without a pointer to the text. This has to be a typo, because there is no
15407 //  unused text to be found within that script.
15408 //
15409 // Sierra's "patch" didn't include a proper fix (as in a modified script).
15410 //  Instead they shipped a dummy text resource, which somewhat "solved" the
15411 //  issue in Sierra SCI, but it still showed another textbox with garbage in
15412 //  it. Funnily Sierra must have known that, because that new text resource
15413 //  contains: "Hi! This is a kludge!"
15414 //
15415 // A copy of this script exists in the arcade when the sequel police arrive, but
15416 //  it has an additional typo that adds another broken function call to the ATM
15417 //  card message. Originally these arcade bugs couldn't be triggered because the
15418 //  police didn't respond to clicks due to their description property not being
15419 //  set. This Feature behavior was changed in later versions, exposing the bug
15420 //  on Mac and Amiga until the script was eventually rewritten for localization.
15421 //
15422 // We properly fix it by removing the faulty code from both scripts, preventing
15423 //  crashes in all versions, and by setting an arbitrary sp2:description in
15424 //  English PC floppy so that the arcade police respond to clicks as intended.
15425 //
15426 // Applies to: English PC Floppy, English Mac Floppy, English Amiga Floppy
15427 // Responsible methods: sp1::doVerb, heap in script 376
15428 // Fixes bug: found by SCI developer
15429 static const uint16 sq4FloppySignatureThrowStuffAtSequelPolice[] = {
15430 	0x47, 0xff, 0x00, 0x02,             // calle [export 0 of script 255], 2
15431 	0x3a,                               // toss
15432 	SIG_MAGICDWORD,
15433 	0x36,                               // push
15434 	0x47, 0xff, 0x00, 0x02,             // calle [export 0 of script 255], 2
15435 	SIG_END
15436 };
15437 
15438 static const uint16 sq4FloppyPatchThrowStuffAtSequelPolice[] = {
15439 	PATCH_ADDTOOFFSET(+5),
15440 	0x32, PATCH_UINT16(0x0002),         // jmp 0002
15441 	PATCH_END
15442 };
15443 
15444 static const uint16 sq4FloppySignatureClickAtmCardOnSequelPolice[] = {
15445 	0x47, 0xff, 0x00, 0x02,             // calle [export 0 of script 255], 2
15446 	SIG_MAGICDWORD,
15447 	0x36,                               // push
15448 	0x47, 0xff, 0x00, 0x02,             // calle [export 0 of script 255], 2
15449 	SIG_END
15450 };
15451 
15452 static const uint16 sq4FloppyPatchClickAtmCardOnSequelPolice[] = {
15453 	PATCH_ADDTOOFFSET(+4),
15454 	0x32, PATCH_UINT16(0x0002),         // jmp 0002
15455 	PATCH_END
15456 };
15457 
15458 // set an arbitrary sp2:description so that sp2:doVerb can run.
15459 //  the description isn't used, it just has to be non-zero.
15460 static const uint16 sq4FloppySignatureSequelPoliceDescription[] = {
15461 	SIG_UINT16(0x0000),                 // description = 0
15462 	SIG_UINT16(0x005a),                 // sighAngle = 90
15463 	SIG_UINT16(0x6789),                 // actions = 26505
15464 	SIG_UINT16(0x6789),                 // onMeCheck = 26505
15465 	SIG_UINT16(0x0000),                 // lookStr = 0
15466 	SIG_UINT16(0x0002),                 // yStep = 2
15467 	SIG_MAGICDWORD,
15468 	SIG_UINT16(0x0179),                 // view = 377
15469 	SIG_UINT16(0x0004),                 // view = 4
15470 	SIG_END
15471 };
15472 
15473 static const uint16 sq4FloppyPatchSequelPoliceDescription[] = {
15474 	PATCH_UINT16(0x2b21),               // description = "It's one of Vohaul's Sequel Policemen!"
15475 	PATCH_END
15476 };
15477 
15478 // Right at the start of Space Quest 4 CD, when walking up in the first room, ego will
15479 //  immediately walk down just after entering the upper room.
15480 //
15481 // This is caused by the scripts setting ego's vertical coordinate to 189 (BDh), which is the
15482 //  trigger in rooms to walk to the room below it. Sometimes this isn't triggered, because
15483 //  the scripts also initiate a motion to vertical coordinate 188 (BCh). When you lower the game's speed,
15484 //  this bug normally always triggers. And it triggers of course also in the original interpreter.
15485 //
15486 // It doesn't happen in PC floppy, because nsRect is not the same as in CD.
15487 //
15488 // We fix it by setting ego's vertical coordinate to 188 and we also initiate a motion to 187.
15489 //
15490 // Applies to at least: English PC CD
15491 // Responsible method: rm045::doit
15492 // Fixes bug: #5468
15493 static const uint16 sq4CdSignatureWalkInFromBelowRoom45[] = {
15494 	0x76,                               // push0
15495 	SIG_MAGICDWORD,
15496 	0x78,                               // push1
15497 	0x38, SIG_UINT16(0x00bd),           // pushi 00BDh
15498 	0x38, SIG_ADDTOOFFSET(+2),          // pushi [setMotion selector]
15499 	0x39, 0x03,                         // pushi 3
15500 	0x51, SIG_ADDTOOFFSET(+1),          // class MoveTo
15501 	0x36,                               // push
15502 	0x78,                               // push1
15503 	0x76,                               // push0
15504 	0x81, 0x00,                         // lag global[0]
15505 	0x4a, 0x04,                         // send 04 -> get ego::x
15506 	0x36,                               // push
15507 	0x38, SIG_UINT16(0x00bc),           // pushi 00BCh
15508 	SIG_END
15509 };
15510 
15511 static const uint16 sq4CdPatchWalkInFromBelowRoom45[] = {
15512 	PATCH_ADDTOOFFSET(+2),
15513 	0x38, PATCH_UINT16(0x00bc),         // pushi 00BCh
15514 	PATCH_ADDTOOFFSET(+15),
15515 	0x38, PATCH_UINT16(0x00bb),         // pushi 00BBh
15516 	PATCH_END
15517 };
15518 
15519 // Sierra seems to have forgotten to include code to play the audio
15520 // describing the "Universal Remote Control" inside the "Hz So good" store.
15521 //
15522 // We add it, so that the audio is now playing.
15523 //
15524 // Applies to at least: English PC CD
15525 // Responsible method: doCatalog::changeState(1Ch) in script 391
15526 // Implements enhancement: #10227
15527 static const uint16 sq4CdSignatureMissingAudioUniversalRemote[] = {
15528 	SIG_MAGICDWORD,
15529 	0x30, SIG_UINT16(0x00b1),           // bnt [skip over state 1Ch code]
15530 	0x35, 0x1c,                         // ldi 1Ch
15531 	0x39, 0x0c,                         // pushi 0Ch
15532 	SIG_ADDTOOFFSET(+127),              // skip over to substate 1 code of state 1Ch code
15533 	0x32, SIG_UINT16(0x0028),           // jmp [to toss/ret, substate 0-code]
15534 	// substate 1 code
15535 	0x3c,                               // dup
15536 	0x35, 0x01,                         // ldi 01
15537 	0x1a,                               // eq?
15538 	0x30, SIG_UINT16(0x0021),           // bnt [skip over substate 1 code]
15539 	0x39, SIG_SELECTOR8(number),        // pushi number
15540 	0x78,                               // push1
15541 	0x7a,                               // push2
15542 	0x38, SIG_UINT16(0x0188),           // pushi 188h
15543 	0x38, SIG_UINT16(0x018b),           // pushi 18Bh
15544 	0x43, 0x3c, 0x04,                   // callk Random, 4
15545 	0x36,                               // push
15546 	0x39, SIG_SELECTOR8(play),          // pushi play
15547 	0x76,                               // push0
15548 	0x81, 0x64,                         // lag global[64h]
15549 	0x4a, 0x0a,                         // send 0Ah
15550 	0x38, SIG_SELECTOR16(setScript),    // pushi setScript
15551 	0x78,                               // push1
15552 	0x72, SIG_UINT16(0x0488),           // lofsa startTerminal
15553 	0x36,                               // push
15554 	0x63, 0x12,                         // pToa client
15555 	0x4a, 0x06,                         // send 06
15556 	0x3a,                               // toss
15557 	0x33, 0x14,                         // jmp [to toss/ret]
15558 	// state 1Dh, called when returning back to main menu from catalog sub menu
15559 	0x3c,                               // dup
15560 	0x35, 0x1d,                         // ldi 1Dh
15561 	0x1a,                               // eq?
15562 	0x31, 0x0e,                         // bnt [skip last state and toss/ret]
15563 	0x35, 0x1d,                         // ldi 1Dh
15564 	0x38, SIG_SELECTOR16(setScript),    // pushi setScript
15565 	0x78,                               // push1
15566 	0x72, SIG_UINT16(0x0488),           // lofsa startTerminal
15567 	0x36,                               // push
15568 	0x63, 0x12,                         // pToa client
15569 	0x4a, 0x06,                         // send 06
15570 	0x3a,                               // toss
15571 	0x48,                               // ret
15572 	SIG_END
15573 };
15574 
15575 static const uint16 sq4CdPatchMissingAudioUniversalRemote[] = {
15576 	0x30, PATCH_UINT16(0x00b7),         // bnt [directly to last state code, saving 6 bytes]
15577 	0x32, PATCH_UINT16(0x009a),         // jmp 154d [to our new code]
15578 	// 1 not used byte here
15579 	PATCH_ADDTOOFFSET(+128),            // skip over to substate 1 code of state 1Ch code
15580 	0x32, PATCH_UINT16(0x003f),         // substate 0 code, jumping to toss/ret
15581 	// directly start with substate 1 code, saving 7 bytes
15582 	0x39, PATCH_SELECTOR8(number),      // pushi number
15583 	0x78,                               // push1
15584 	0x7a,                               // push2
15585 	0x38, PATCH_UINT16(0x0188),         // pushi 188h
15586 	0x38, PATCH_UINT16(0x018b),         // pushi 18Bh
15587 	0x43, 0x3c, 0x04,                   // callk Random, 4
15588 	0x36,                               // push
15589 	0x39, PATCH_SELECTOR8(play),        // pushi play
15590 	0x76,                               // push0
15591 	0x81, 0x64,                         // lag global[64h]
15592 	0x4a, 0x0a,                         // send 0Ah
15593 	0x33, 0x1a,                         // jmp [state 1Dh directly, saving 12 bytes]
15594 	// additional code for playing missing audio (18 bytes w/o jmp back)
15595 	0x89, 0x5a,                         // lsg global[5Ah]
15596 	0x35, 0x01,                         // ldi 1
15597 	0x1c,                               // ne?
15598 	0x31, 0x0b,                         // bnt [skip play audio]
15599 	0x38, PATCH_SELECTOR16(say),        // pushi say (0123h)
15600 	0x78,                               // push1
15601 	0x39, 0x14,                         // pushi 14h
15602 	0x72, PATCH_UINT16(0x0850),         // lofsa newRob
15603 	0x4a, 0x06,                         // send 06
15604 	// now get back
15605 	0x39, 0x0c,                         // pushi 0Ch
15606 	0x32, PATCH_UINT16(0xff50),         // jmp back
15607 	PATCH_END
15608 };
15609 
15610 // It seems that Sierra forgot to set a script flag when cleaning out the bank
15611 // account in Space Quest 4 CD. This was probably caused by the whole bank
15612 // account interaction getting a rewrite and polish in the CD version.
15613 //
15614 // Because of this bug, points for changing back clothes will not get awarded,
15615 // which makes it impossible to get a perfect point score in the CD version of
15616 // the game. The points are awarded by rm371::doit in script 371.
15617 //
15618 // We fix this. PC floppy does not have this bug.
15619 //
15620 // Note: Some Let's Plays on YouTube show points are in fact awarded. But those
15621 //  Let's Plays were of a hacked Space Quest 4 version. It was part Floppy,
15622 //  part CD version. We consider it to be effectively pirated and not a
15623 //  canonical CD version of Space Quest 4. It's easy to identify for having
15624 //  both voices and a store called "Radio Shock" instead of "Hz. So Good".
15625 //
15626 // Applies to at least: English PC CD
15627 // Responsible method: but2Script::changeState(2)
15628 // Fixes bug: #6866
15629 static const uint16 sq4CdSignatureGetPointsForChangingBackClothes[] = {
15630 	0x35, 0x02,                         // ldi 02
15631 	SIG_MAGICDWORD,
15632 	0x1a,                               // eq?
15633 	0x30, SIG_UINT16(0x006a),           // bnt [state 3]
15634 	0x76,
15635 	SIG_ADDTOOFFSET(+46),               // jump over "withdraw funds" code
15636 	0x33, 0x33,                         // jmp [end of state 2, set cycles code]
15637 	SIG_ADDTOOFFSET(+51),               // jump over "clean bank account" code
15638 	0x35, 0x02,                         // ldi 02
15639 	0x65, 0x1a,                         // aTop cycles
15640 	0x33, 0x0b,                         // jmp [toss/ret]
15641 	0x3c,                               // dup
15642 	0x35, 0x03,                         // ldi 03
15643 	0x1a,                               // eq?
15644 	0x31, 0x05,                         // bnt [toss/ret]
15645 	SIG_END
15646 };
15647 
15648 static const uint16 sq4CdPatchGetPointsForChangingBackClothes[] = {
15649 	PATCH_ADDTOOFFSET(+3),
15650 	0x30, PATCH_UINT16(0x0070),         // bnt [state 3]
15651 	PATCH_ADDTOOFFSET(+47),             // "withdraw funds" code
15652 	0x33, 0x39,                         // jmp [end of state 2, set cycles code]
15653 	PATCH_ADDTOOFFSET(+51),
15654 	0x78,                               // push1
15655 	0x39, 0x1d,                         // pushi 1Dh
15656 	0x45, 0x07, 0x02,                   // callb [export 7 of script 0], 02 (set flag 1Dh - located at global[73h] bit 2)
15657 	0x35, 0x02,                         // ldi 02
15658 	0x65, 0x1c,                         // aTop cycles
15659 	0x33, 0x05,                         // jmp [toss/ret]
15660 	// check for state 3 code removed to save 6 bytes
15661 	PATCH_END
15662 };
15663 
15664 // The English Amiga version contains curious changes to the dress logic in
15665 //  Sock's which break the game and weren't included in later versions:
15666 //
15667 // 1. Purchasing the dress is recorded in flag 90 instead of mall:rFlag3
15668 // 2. Flag 90 is cleared when changing clothes after clearing out the ATM
15669 //
15670 // Game flags are global while mall flags are reset upon leaving the mall, which
15671 //  makes this look like a bug fix, but Sock's is closed when returning so this
15672 //  shouldn't change game logic. Unfortunately Sierra forgot to update the other
15673 //  scripts which query mall:rFlag3 and so they never see the dress purchase.
15674 //  This creates scenarios where exiting Sock's (room 371) after paying causes
15675 //  room 370 to kick Roger out for not paying, preventing game completion.
15676 //
15677 // Clearing the dress-purchase flag has no effect other than to allow purchasing
15678 //  the dress a second time as if it never happened, leaving the player without
15679 //  enough money to complete the game, having paid for the dress twice.
15680 //
15681 // We fix both bugs by updating the mall scripts so that they all test flag 90
15682 //  and never clear it. It's possible the flag change was an optimization, which
15683 //  many Amiga tweaks are, since it eliminated a message send in rm371:doit.
15684 //
15685 // Applies to: English Amiga Floppy
15686 // Responsible methods: rm371:doit, rm370:init, warningScript:changeState(1)
15687 // Fixes bug #11004
15688 static const uint16 sq4AmigaSignatureDressPurchaseFlagClear[] = {
15689 	SIG_MAGICDWORD,
15690 	0x78,                               // push1
15691 	0x39, 0x5a,                         // pushi 5a
15692 	0x45, 0x08, 0x02,                   // callb proc0_8 02 [ clear flag 90 ]
15693 	SIG_END
15694 };
15695 
15696 static const uint16 sq4AmigaPatchDressPurchaseFlagClear[] = {
15697 	0x32, PATCH_UINT16(0x0003),         // jmp 0003 [ don't clear flag 90 ]
15698 	PATCH_END
15699 };
15700 
15701 static const uint16 sq4AmigaSignatureDressPurchaseFlagCheck[] = {
15702 	SIG_MAGICDWORD,
15703 	0x36,                               // push [ mall ]
15704 	0x38, SIG_UINT16(0x0228),           // pushi rFlag3
15705 	0x39, 0x04,                         // pushi 04
15706 	0x46, SIG_UINT16(0x02bc),           // calle proc700_3 06 [ is mall:rFlag3 flag 4 set? ]
15707 	      SIG_UINT16(0x0003), 0x06,
15708 	SIG_END
15709 };
15710 
15711 static const uint16 sq4AmigaPatchDressPurchaseFlagCheck[] = {
15712 	0x78,                               // push1
15713 	0x39, 0x5a,                         // pushi 5a
15714 	0x45, 0x06, 0x02,                   // callb proc0_6 02 [ is flag 90 set? ]
15715 	0x32, PATCH_UINT16(0x0003),         // jmp 0003
15716 	PATCH_END
15717 };
15718 
15719 // The Big And Tall store (room 381) doesn't display its Look message in the CD
15720 //  version. We add the missing super:doVerb call to theStore:doVerb.
15721 //
15722 // Applies to: English PC CD
15723 // Responsible method: theStore:doVerb
15724 static const uint16 sq4CdSignatureBigAndTallDescription[] = {
15725 	0x3c,                               // dup
15726 	0x35, 0x06,                         // ldi 06
15727 	0x1a,                               // eq? [ verb == smell ]
15728 	0x30, SIG_UINT16(0x0013),           // bnt 0013
15729 	0x38, SIG_SELECTOR16(modNum),       // pushi modNum [ redundant when set to room number ]
15730 	0x78,                               // push1
15731 	0x38, SIG_UINT16(0x017d),           // pushi 017d
15732 	0x38, SIG_SELECTOR16(say),          // pushi say
15733 	0x78,                               // push1
15734 	0x39, 0x0a,                         // pushi 0a
15735 	0x81, 0x59,                         // lag 59
15736 	0x4a, 0x0c,                         // send 0c [ sq4GlobalNarrator modNum: 381 say: 10 ]
15737 	SIG_MAGICDWORD,
15738 	0x33, 0x02,                         // jmp 02
15739 	0x35, 0x00,                         // ldi 00
15740 	0x3a,                               // toss
15741 	SIG_END
15742 };
15743 
15744 static const uint16 sq4CdPatchBigAndTallDescription[] = {
15745 	0x35, 0x06,                         // ldi 06
15746 	0x1a,                               // eq? [ verb == smell ]
15747 	0x31, 0x0a,                         // bnt 0a
15748 	0x38, PATCH_SELECTOR16(say),        // pushi say
15749 	0x78,                               // push1
15750 	0x39, 0x0a,                         // pushi 0a
15751 	0x81, 0x59,                         // lag 59
15752 	0x4a, 0x06,                         // send 06 [ sq4GlobalNarrator say: 10 ]
15753 	0x87, 0x01,                         // lap 01
15754 	0x78,                               // push1
15755 	0x1a,                               // eq? [ verb == look ]
15756 	0x31, 0x08,                         // bnt 08
15757 	0x38, PATCH_SELECTOR16(doVerb),     // pushi doVerb
15758 	0x78,                               // push1
15759 	0x78,                               // push1
15760 	0x57, 0x7a, 0x06,                   // super Sq4Feature 06 [ super doVerb: 1 ]
15761 	PATCH_END
15762 };
15763 
15764 // Clicking Do on the Monolith Burger door responds with the message for looking
15765 //  at the boss and clicking Taste responds with the taste message for the bush.
15766 //  The verb handler is missing the modNum for both and also sets the wrong cond
15767 //  for the second.
15768 //
15769 // Applies to: English PC CD
15770 // Responsible method: door:doVerb(4)
15771 // Fixes bug #10976
15772 static const uint16 sq4CdSignatureMonolithBurgerDoor[] = {
15773 	SIG_MAGICDWORD,
15774 	0x31, 0x13,                         // bnt 13
15775 	0x38, SIG_SELECTOR16(modNum),       // pushi modNum
15776 	0x78,                               // push1
15777 	0x38, SIG_UINT16(0x0177),           // pushi 0177
15778 	0x38, SIG_SELECTOR16(say),          // pushi say
15779 	0x78,                               // push1
15780 	0x78,                               // push1
15781 	0x81, 0x59,                         // lag 59
15782 	0x4a, 0x0c,                         // send 0c [ Sq4GlobalNarrator modNum 375: say: 1 ]
15783 	SIG_ADDTOOFFSET(+9),
15784 	0x38, SIG_SELECTOR16(say),          // pushi say
15785 	0x78,                               // push1
15786 	0x7a,                               // push2
15787 	0x81, 0x59,                         // lag 59
15788 	0x4a, 0x06,                         // send 06 [ Sq4GlobalNarrator say: 2 ]
15789 	SIG_ADDTOOFFSET(+25),
15790 	0x38, SIG_SELECTOR16(say),          // pushi say
15791 	0x78,                               // push1
15792 	0x39, 0x0b,                         // pushi 0b
15793 	0x81, 0x59,                         // lag 59
15794 	0x4a, 0x06,                         // send 06 [ Sq4GlobalNarrator say: 11 ]
15795 	SIG_END
15796 };
15797 
15798 static const uint16 sq4CdPatchMonolithBurgerDoor[] = {
15799 	PATCH_ADDTOOFFSET(+13),
15800 	0x36,                               // push
15801 	PATCH_ADDTOOFFSET(+13),
15802 	0x35, 0x02,                         // ldi 02
15803 	0x33, 0xe3,                         // jmp e3 [ Sq4GlobalNarrator modNum 375: say: 2 ]
15804 	PATCH_ADDTOOFFSET(+30),
15805 	0x35, 0x04,                         // ldi 04
15806 	0x33, 0xc1,                         // jmp c1 [ Sq4GlobalNarrator modNum 375: say: 4 ]
15807 	PATCH_END
15808 };
15809 
15810 // For Space Quest 4 CD, Sierra added a pick up animation for Roger when he
15811 // picks up the rope.
15812 //
15813 // When the player is detected by the zombie right at the start of the game,
15814 // while picking up the rope, scripts bomb out.
15815 //
15816 // This is caused by code intended to make Roger face the arriving drone. We
15817 // fix it by checking if ego::cycler is actually set before calling that code.
15818 //
15819 // Applies to at least: English PC CD
15820 // Responsible method: droidShoots::changeState(3)
15821 // Fixes bug: #6076
15822 static const uint16 sq4CdSignatureGettingShotWhileGettingRope[] = {
15823 	0x35, 0x02,                         // ldi 02
15824 	0x65, 0x1a,                         // aTop cycles
15825 	0x32, SIG_UINT16(0x02fa),           // jmp [end]
15826 	SIG_MAGICDWORD,
15827 	0x3c,                               // dup
15828 	0x35, 0x02,                         // ldi 02
15829 	0x1a,                               // eq?
15830 	0x31, 0x0b,                         // bnt [state 3 check]
15831 	0x76,                               // push0
15832 	0x45, 0x02, 0x00,                   // callb [export 2 of script 0], 00 (disable controls)
15833 	0x35, 0x02,                         // ldi 02
15834 	0x65, 0x1a,                         // aTop cycles
15835 	0x32, SIG_UINT16(0x02e9),           // jmp [end]
15836 	0x3c,                               // dup
15837 	0x35, 0x03,                         // ldi 03
15838 	0x1a,                               // eq?
15839 	0x31, 0x1e,                         // bnt [state 4 check]
15840 	0x76,                               // push0
15841 	0x45, 0x02, 0x00,                   // callb [export 2 of script 0], 00 (disable controls again??)
15842 	0x7a,                               // push2
15843 	0x89, 0x00,                         // lsg global[0]
15844 	0x72, SIG_UINT16(0x0242),           // lofsa deathDroid
15845 	0x36,                               // push
15846 	0x45, 0x0d, 0x04,                   // callb [export 13 of script 0], 04 (set heading of ego to face droid)
15847 	SIG_END
15848 };
15849 
15850 static const uint16 sq4CdPatchGettingShotWhileGettingRope[] = {
15851 	PATCH_ADDTOOFFSET(+11),
15852 	// this makes state 2 only do the 2 cycles wait, controls should always be disabled already at this point
15853 	0x2f, 0xf3,                         // bt [previous state aTop cycles code]
15854 	// Now we check for state 3, this change saves us 11 bytes
15855 	0x3c,                               // dup
15856 	0x35, 0x03,                         // ldi 03
15857 	0x1a,                               // eq?
15858 	0x31, 0x29,                         // bnt [state 4 check]
15859 	// new state 3 code
15860 	0x76,                               // push0
15861 	0x45, 0x02, 0x00,                   // callb [export 2 of script 0], 00 (disable controls, actually not needed)
15862 	0x38, PATCH_SELECTOR16(cycler),     // pushi cycler
15863 	0x76,                               // push0
15864 	0x81, 0x00,                         // lag global[0]
15865 	0x4a, 0x04,                         // send 04 (get ego::cycler)
15866 	0x30, PATCH_UINT16(0x000a),         // bnt [skip the heading call]
15867 	PATCH_END
15868 };
15869 
15870 // During the SQ4 introduction logo, EGA versions increase the number of calls
15871 //  to kPaletteAnimate by 40x. This was probably to achieve the same delay as
15872 //  VGA even though no palette animation occurred. This adjustment interferes
15873 //  with our kPaletteAnimate speed throttling for SQ4 scripts such as this, bug
15874 //  #6057. We remove the EGA delay, making all versions consistent, otherwise
15875 //  the logo is displayed for over 3 minutes instead of 5 seconds.
15876 //
15877 // Applies to: English PC EGA Floppy, Japanese PC-98
15878 // Responsible method: rmScript:changeState
15879 // Fixes bug #6193
15880 static const uint16 sq4SignatureEgaIntroDelay[] = {
15881 	SIG_MAGICDWORD,
15882 	0x89, 0x69,                         // lsg 69 [ system colors ]
15883 	0x35, 0x10,                         // ldi 10
15884 	0x1e,                               // gt?    [ system colors > 16 ]
15885 	0x30,                               // bnt    [ use EGA delay ]
15886 	SIG_END
15887 };
15888 
15889 static const uint16 sq4PatchEgaIntroDelay[] = {
15890 	0x33, 0x06,                         // jmp 06 [ don't use EGA delay ]
15891 	PATCH_END
15892 };
15893 
15894 // Talking to the red shopper in the mall has a 5% chance of a funny message but
15895 //  this script is broken in the CD version. After the first time the wrong
15896 //  message tuple is attempted by the narrator as its modNum value is cleared.
15897 //  The message text is also accidentally repeated in a message box.
15898 //
15899 // We fix this by specifying the modNum when saying the message and removing
15900 //  the erroneous message box call.
15901 //
15902 // Applies to: English PC CD
15903 // Responsible method: shopper3:doVerb(2)
15904 // Fixes bug #10911
15905 static const uint16 sq4CdSignatureRedShopperMessageFix[] = {
15906 	0x38, SIG_SELECTOR16(say),          // pushi say
15907 	0x78,                               // push1
15908 	SIG_MAGICDWORD,
15909 	0x78,                               // push1
15910 	0x72, SIG_UINT16(0x057a),           // lofsa wierdNar
15911 	0x4a, 0x06,                         // send 06 [ wierdNar say: 1 ]
15912 	0x78,                               // push1
15913 	0x72, SIG_UINT16(0x0660),           // lofsa "Mr. Carlos sent me..."
15914 	0x36,                               // push
15915 	0x46, SIG_UINT16(0x0399),           // calle proc921_0 [ message box ]
15916 	      SIG_UINT16(0x0000), 0x02,
15917 	SIG_END
15918 };
15919 
15920 static const uint16 sq4CdPatchRedShopperMessageFix[] = {
15921 	0x38, PATCH_SELECTOR16(modNum),     // pushi modNum
15922 	0x38, PATCH_UINT16(0x0001),         // pushi 0001
15923 	0x38, PATCH_UINT16(0x02bc),         // pushi 02bc
15924 	0x38, PATCH_SELECTOR16(say),        // pushi say
15925 	0x39, 0x01,                         // pushi 01
15926 	0x39, 0x01,                         // pushi 01
15927 	0x72, PATCH_UINT16(0x057a),         // lofsa wierdNar
15928 	0x4a, 0x0c,                         // send 0c [ wierdNar modNum: 700 say: 1 ]
15929 	PATCH_END
15930 };
15931 
15932 // When swimming in zero gravity in the mall, the game can lock up if the Sequel
15933 //  Police shoot while ego swims past the edge of the screen.
15934 //
15935 // When the Sequel Police shoot, the object "blast" animates near ego. blast is
15936 //  an obstacle that ego can collide with. If ego is shot at while going beyond
15937 //  the edge of the screen and blast is in the right position then stayInScript
15938 //  can lock up due to ego getting stuck on the invisible blast object and never
15939 //  reaching his destination.
15940 //
15941 // We fix this by setting blast's ignore-actors flag so that ego can't collide
15942 //  with it and get stuck. This does not affect whether or not ego gets shot.
15943 //
15944 // Applies to at least: English PC Floppy, English PC CD, probably all versions
15945 // Responsible method: Heap in scripts 405, 406, 410, and 411
15946 // Fixes bug #10912
15947 static const uint16 sq4SignatureZeroGravityBlast[] = {
15948 	SIG_MAGICDWORD,                     // blast
15949 	SIG_UINT16(0x0002),                 // yStep = 2
15950 	SIG_UINT16(0x001c),                 // view = 128
15951 	SIG_UINT16(0x0000),                 // loop = 0
15952 	SIG_UINT16(0x0000),                 // cel = 0
15953 	SIG_UINT16(0x0000),                 // priority = 0
15954 	SIG_UINT16(0x0000),                 // underBits = 0
15955 	SIG_UINT16(0x0000),                 // signal = 0
15956 	SIG_END
15957 };
15958 
15959 static const uint16 sq4PatchZeroGravityBlast[] = {
15960 	PATCH_ADDTOOFFSET(+12),
15961 	PATCH_UINT16(0x4000),               // signal = $4000 [ set ignore-actors flag ]
15962 	PATCH_END
15963 };
15964 
15965 // Cedric the owl from KQ5 appears in the CD version of Ms. Astro Chicken, but a
15966 //  bug in this easter egg makes it a much rarer occurrence than intended.
15967 //
15968 // Every 50 ticks there's a 1 in 21 chance of Cedric appearing if there's no
15969 //  rock on screen and he hasn't already been killed by colliding with the
15970 //  player or getting shot by the farmer. The problem is that unless Cedric
15971 //  appears before the farmer, which is very unlikely, then the farmer's first
15972 //  bullet will kill Cedric off-screen due to incorrect collision testing.
15973 //  buckShot:doit tests for collision by calling cedric:onMe, but Cedric isn't
15974 //  initialized until he first appears, and View:onMe always returns true no
15975 //  matter what coordinates are being tested if its rectangle isn't initialized.
15976 //
15977 // We fix this by initializing Cedric's actor-hidden signal flag on the heap.
15978 //  This prevents View:onMe from returning true before Cedric is initialized.
15979 //  The flag is later cleared by cedric:init when he is placed on screen.
15980 //
15981 // Applies to: English PC CD
15982 // Responsible method: Heap in script 290
15983 // Fixes bug #10920
15984 static const uint16 sq4CdSignatureCedricEasterEgg[] = {
15985 	SIG_MAGICDWORD,                     // cedric
15986 	SIG_UINT16(0x0110),                 // view = 272
15987 	SIG_UINT16(0x0000),                 // loop = 0
15988 	SIG_UINT16(0x0000),                 // cel = 0
15989 	SIG_UINT16(0x000d),                 // priority = 13
15990 	SIG_UINT16(0x0000),                 // underBits = 0
15991 	SIG_UINT16(0x0810),                 // signal = $0810
15992 	SIG_END
15993 };
15994 
15995 static const uint16 sq4CdPatchCedricEasterEgg[] = {
15996 	PATCH_ADDTOOFFSET(+10),
15997 	PATCH_UINT16(0x0890),               // signal = $0890 [ set actor-hidden flag ]
15998 	PATCH_END
15999 };
16000 
16001 // Colliding with Cedric in Ms. Astro Chicken after colliding with an obstacle
16002 //  locks up the game. cedric:doit doesn't check if the player has been hit
16003 //  before running killCedricScript. This interferes with the collision scripts'
16004 //  animations and prevents them from continuing.
16005 //
16006 // We fix this by not running killCedricScript if the player has been hit.
16007 //  Unfortunately there's no single property that can be tested as each
16008 //  collision script does things differently, so this is a two-part patch.
16009 //  First, ScrollActor:doChicken is patched to set User:canControl to 0, making
16010 //  it consistent with the other collision scripts. Second, cedric:doit is
16011 //  patched to test this value. To make room for this we replace testing
16012 //  killCedricScript with testing the flag that killCedricScript sets.
16013 //
16014 // Applies to: English PC CD
16015 // Responsible methods: ScrollActor:doChicken, cedric:doit
16016 // Fixes bug #10920
16017 static const uint16 sq4CdSignatureCedricLockup1[] = {
16018 	SIG_MAGICDWORD,
16019 	0x18,                               // not
16020 	0x30, SIG_UINT16(0x0049),           // bnt 0049 [ end of method ]
16021 	0x63, 0x84,                         // pToa deathLoop
16022 	0x30, SIG_UINT16(0x0044),           // bnt 0044 [ end of method ]
16023 	0x38, SIG_SELECTOR16(stop),         // pushi stop
16024 	0x76,                               // push0
16025 	0x81, 0x64,                         // lag 64
16026 	0x4a, 0x04,                         // send 04 [ longSong2 stop: ]
16027 	0x38, SIG_SELECTOR16(stop),         // pushi stop
16028 	0x76,                               // push0
16029 	0x72, SIG_UINT16(0x0108),           // lofsa eggSplatting
16030 	0x4a, 0x04,                         // send 04 [ eggSplatting stop: ]
16031 	0x39, SIG_SELECTOR8(number),        // pushi number
16032 	0x78,                               // push1
16033 	0x67, 0x86,                         // pTos deathMusic
16034 	0x39, SIG_SELECTOR8(loop),          // pushi loop
16035 	0x78,                               // push1
16036 	0x78,                               // push1 [ unnecessary, loop is initialized to 1 on heap ]
16037 	SIG_ADDTOOFFSET(+7),
16038 	0x4a, 0x12,                         // send 12 [ theSound number: deathMusic loop: 1 play: self ]
16039 	SIG_END
16040 };
16041 
16042 static const uint16 sq4CdPatchCedricLockup1[] = {
16043 	0x2f, 0x4b,                         // bt 4b [ end of method ]
16044 	0x63, 0x84,                         // pToa deathLoop
16045 	0x31, 0x47,                         // bnt 47 [ end of method ]
16046 	0x38, PATCH_SELECTOR16(stop),       // pushi stop
16047 	0x3c,                               // dup
16048 	0x76,                               // push0
16049 	0x81, 0x64,                         // lag 64
16050 	0x4a, 0x04,                         // send 04 [ longSong2 stop: ]
16051 	0x76,                               // push0
16052 	0x72, PATCH_UINT16(0x0108),         // lofsa eggSplatting
16053 	0x4a, 0x04,                         // send 04 [ eggSplatting stop: ]
16054 	0x39, PATCH_SELECTOR8(number),      // pushi number
16055 	0x78,                               // push1
16056 	0x67, 0x86,                         // pTos deathMusic
16057 	0x38, PATCH_SELECTOR16(canControl), // pushi canControl
16058 	0x78,                               // push1
16059 	0x76,                               // push0
16060 	0x81, 0x50,                         // lag 50
16061 	0x4a, 0x06,                         // send 06 [ User canControl: 0 ]
16062 	PATCH_ADDTOOFFSET(+7),
16063 	0x4a, 0x0c,                         // send 0c [ theSound number: deathMusic play: self ]
16064 	PATCH_END
16065 };
16066 
16067 static const uint16 sq4CdSignatureCedricLockup2[] = {
16068 	SIG_MAGICDWORD,
16069 	0x31, 0x17,                         // bnt 17 [ end of method ]
16070 	0x38, SIG_SELECTOR16(script),       // pushi script
16071 	0x76,                               // push0
16072 	0x51, 0x9c,                         // class astroChicken
16073 	0x4a, 0x04,                         // send 04 [ astroChicken script? ]
16074 	0x18,                               // not     [ acc = 1 if killCedricScript not running ]
16075 	0x31, 0x0c,                         // bnt 0c  [ end of method ]
16076 	0x38, SIG_SELECTOR16(setScript),    // pushi setScript
16077 	0x78,                               // push1
16078 	0x72, SIG_UINT16(0x0f7c),           // lofsa killCedricScript
16079 	0x36,                               // push
16080 	0x51, 0x9c,                         // class astroChicken
16081 	0x4a, 0x06,                         // send 06 [ astroChicken setScript: killCedricScript ]
16082 	0x48,                               // ret
16083 	0x48,                               // ret
16084 	SIG_END
16085 };
16086 
16087 static const uint16 sq4CdPatchCedricLockup2[] = {
16088 	0x31, 0x18,                         // bnt 18 [ end of method ]
16089 	0x38, PATCH_SELECTOR16(canControl), // pushi canControl
16090 	0x76,                               // push0
16091 	0x81, 0x50,                         // lag 50
16092 	0x4a, 0x04,                         // send 04 [ User canControl? ]
16093 	0x8b, 0x21,                         // lsl 21  [ local33 = 0 if cedric is alive, 1 if dead ]
16094 	0x22,                               // lt?     [ acc = 1 if cedric is alive and user has control ]
16095 	0x31, 0x0b,                         // bnt 0b  [ end of method ]
16096 	0x38, PATCH_SELECTOR16(setScript),  // pushi setScript
16097 	0x78,                               // push1
16098 	0x74, PATCH_UINT16(0x0f7c),         // lofss killCedricScript
16099 	0x51, 0x9c,                         // class astroChicken
16100 	0x4a, 0x06,                         // send 06 [ astroChicken setScript: killCedricScript ]
16101 	PATCH_END
16102 };
16103 
16104 // Dodging a biker in Ulence Flats before they've reached their first checkpoint
16105 //  causes the player to prematurely regain control, allowing them to break the
16106 //  game by walking to another room before the dodge sequence completes. This is
16107 //  due to the biker scripts in each room having an unnecessary handsOn call, so
16108 //  we remove it. This does not reduce the time that the player has to react,
16109 //  the scripts theDodgeL and theDodgeR call handsOn when ego crouches.
16110 //
16111 // Biker bugs like this only occur in the CD version due to its slower bikes.
16112 //
16113 // Applies to: English PC CD
16114 // Responsible methods: runOverScript1-3:changeState, runOver:changeState,
16115 //                      runOver2:changeState, runOverScript:changeState
16116 // Fixes bug #9806
16117 static const uint16 sq4CdSignatureBikerHandsOn[] = {
16118 	0x38, SIG_UINT16(0x02bb),           // pushi status [ hard-coded for CD ]
16119 	0x78,                               // push1
16120 	SIG_MAGICDWORD,
16121 	0x39, 0x04,                         // pushi 04
16122 	0x51, 0x98,                         // class ulence
16123 	0x4a, 0x06,                         // send 06 [ ulence status: 4 ]
16124 	0x76,                               // push0
16125 	0x45, 0x03, 0x00,                   // callb proc0_3 [ handsOn ]
16126 	SIG_END
16127 };
16128 
16129 static const uint16 sq4CdPatchBikerHandsOn[] = {
16130 	PATCH_ADDTOOFFSET(+10),
16131 	0x33, 0x02,                         // jmp 02 [ skip handsOn ]
16132 	PATCH_END
16133 };
16134 
16135 // Clicking Look/Do/etc on an object in Ulence Flats while a biker approaches
16136 //  can bring ego out of crouch-mode and allow the player to break the game by
16137 //  walking to another room before the dodge sequence completes. This occurs if
16138 //  ego isn't facing the object. Ego's heading will be changed and ultimately
16139 //  stopGroop:doit will reset ego's view to walking.
16140 //
16141 // We fix this by clearing ego:looper when placing ego into crouch mode. This
16142 //  causes Actor:setHeading to instead use kDirLoop which respects the flag
16143 //  kSignalDoesntTurn. ego:looper is automatically restored after dodging.
16144 //
16145 // Applies to: English PC CD
16146 // Responsible methods: theDodgeR:changeState, theDodgeL:changeState
16147 // Fixes bug #9806
16148 static const uint16 sq4CdSignatureBikerCrouchVerb[] = {
16149 	0x35, 0x01,                         // ldi 01
16150 	0x1a,                               // eq?
16151 	0x31, 0x1a,                         // bnt 1a [ state 2 ]
16152 	0x76,                               // push0
16153 	SIG_MAGICDWORD,
16154 	0x45, 0x03, 0x00,                   // callb proc0_3 [ handsOn ]
16155 	0x7a,                               // push2 [ view ]
16156 	0x78,                               // push1
16157 	0x38, SIG_UINT16(0x027b),           // pushi 027b [ crouch ]
16158 	0x38, SIG_SELECTOR16(setLoop),      // pushi setLoop
16159 	0x78,                               // push1
16160 	SIG_ADDTOOFFSET(+1),                // push0 in theDodgeR, push1 in theDodgeL
16161 	0x38, SIG_SELECTOR16(setCel),       // pushi setCel
16162 	0x78,                               // push1
16163 	0x76,                               // push0
16164 	0x81, 0x00,                         // lag 00
16165 	0x4a, 0x12,                         // send 12 [ ego view: 635 setLoop: * setCel: 0 ]
16166 	0x32,                               // jmp [ end of method ]
16167 	SIG_END
16168 };
16169 
16170 static const uint16 sq4CdPatchBikerCrouchVerb[] = {
16171 	0x18,                               // not [ save a byte ]
16172 	0x1a,                               // eq?
16173 	0x31, 0x1b,                         // bnt 1b [ state 2 ]
16174 	0x76,                               // push0
16175 	0x39, PATCH_SELECTOR8(looper),      // pushi looper
16176 	0x78,                               // push1
16177 	0x76,                               // push0
16178 	PATCH_ADDTOOFFSET(+17),
16179 	0x4a, 0x18,                         // send 18 [ ego looper: 0 view: 635 setLoop: * setCel: 0 ]
16180 	0x45, 0x03, 0x00,                   // callb proc0_3 [ handsOn ]
16181 	PATCH_END
16182 };
16183 
16184 // Clicking Do on the Ulence Flats bar door in room 610 while a biker approaches
16185 //  causes ego to enter the bar before the dodge sequence completes, breaking
16186 //  the game. Sierra forgot to test ulence:egoBusy as they did when clicking on
16187 //  the timepod in room 613.
16188 //
16189 // We fix this by testing ulence:egoBusy before running enterBar in door:doVerb.
16190 //  Fortunately there is already an unnecessary script test we can overwrite.
16191 //  This script test was copied from rm610:doit where it is necessary.
16192 //
16193 // Applies to: English PC CD
16194 // Responsible method: door:doVerb(4)
16195 // Fixes bug #9806
16196 static const uint16 sq4CdSignatureBikerBarDoor[] = {
16197 	0x38, SIG_SELECTOR16(script),       // pushi script
16198 	0x76,                               // push0
16199 	0x81, 0x02,                         // lag 02
16200 	0x4a, 0x04,                         // send 04 [ rm610 script? ]
16201 	0x36,                               // push
16202 	0x72, SIG_UINT16(0x014a),           // lofsa enterBar
16203 	SIG_MAGICDWORD,
16204 	0x1a,                               // eq? [ rm610:script == enterBar ]
16205 	0x18,                               // not
16206 	0x30, SIG_UINT16(0x0019),           // bnt 0019 [ don't run enterBar ]
16207 	SIG_END
16208 };
16209 
16210 static const uint16 sq4CdPatchBikerBarDoor[] = {
16211 	0x38, PATCH_UINT16(0x02cc),         // pushi egoBusy [ hard-coded for CD ]
16212 	0x76,                               // push0
16213 	0x51, 0x98,                         // class ulence
16214 	0x4a, 0x04,                         // send 04 [ ulence egoBusy? ]
16215 	0x18,                               // not
16216 	0x32, PATCH_UINT16(0x0002),         // jmp 0002
16217 	PATCH_END
16218 };
16219 
16220 // Clicking Do on the timepod in room 613 while a biker approaches always shows
16221 //  "Not now!" in a message box, even in speech mode. It seems that Sierra
16222 //  thought they didn't have audio for this message and created a text resource
16223 //  just for it, but it also appears in room 90 with audio, so we use that.
16224 //
16225 // Applies to: English PC CD
16226 // Responsible method: ship:doVerb(4)
16227 // Fixes bug #10922
16228 static const uint16 sq4CdSignatureBikerTimepodMessage[] = {
16229 	0x36,                               // push
16230 	0x35, 0x01,                         // ldi 01
16231 	0x1a,                               // eq?
16232 	0x31, 0x0d,                         // bnt 0d [ skip if ulence:egoBusy != 1 ]
16233 	0x7a,                               // push2
16234 	0x38, SIG_UINT16(0x0265),           // pushi 0265
16235 	0x76,                               // push0
16236 	SIG_MAGICDWORD,
16237 	0x46, SIG_UINT16(0x0330),           // calle proc816_1 04 [ message box ]
16238 	      SIG_UINT16(0x0001), 0x04,
16239 	SIG_END
16240 };
16241 
16242 static const uint16 sq4CdPatchBikerTimepodMessage[] = {
16243 	0x31, 0x11,                         // bnt 11 [ skip if ulence:egoBusy == 0 ]
16244 	0x38, PATCH_SELECTOR16(modNum),     // pushi modNum
16245 	0x78,                               // push1
16246 	0x39, 0x5a,                         // pushi 5a
16247 	0x38, PATCH_SELECTOR16(say),        // pushi say
16248 	0x78,                               // push1
16249 	0x7a,                               // push2
16250 	0x81, 0x59,                         // lag 59
16251 	0x4a, 0x0c,                         // send 0c [ Sq4GlobalNarrator modNum: 90 say: 2 ]
16252 	PATCH_END
16253 };
16254 
16255 // Hiding from the Sequel Police in the electronics store (room 390) locks up
16256 //  the CD version and has a subsequent animation bug.
16257 //
16258 // This scene is triggered by going east to the escalator after the police
16259 //  arrive and then back to the electronics store. Police appear on both sides
16260 //  and the only way to survive is to hide in the store while they talk before
16261 //  splitting up. Their first message fails to set a caller, locking up the
16262 //  game, and their other messages contain typos and newline characters left
16263 //  over from floppy versions.
16264 //
16265 // We fix the lockup by passing the missing "self" parameter so that the script
16266 //  proceeds. This exposes the next bug, where the police walk to the beltways
16267 //  and instead of standing, shoot wildly into the mall. The script doesn't
16268 //  remove their Walk cyclers and so they continue to animate. This worked in
16269 //  floppy versions but the Cycle classes were upgraded in CD with different
16270 //  behavior. We fix this by removing the Walk cyclers.
16271 //
16272 // Applies to: English PC CD
16273 // Responsible method: sp1Squeeze:changeState
16274 // Fixes bug #10977
16275 static const uint16 sq4CdSignatureHzSoGoodSequelPoliceLockup[] = {
16276 	0x38, SIG_SELECTOR16(say),          // pushi say
16277 	0x78,                               // push1
16278 	SIG_MAGICDWORD,
16279 	0x78,                               // push1
16280 	0x72, SIG_UINT16(0x0630),           // lofsa tSP1
16281 	0x4a, 0x06,                         // send 06  [ tSP1 say: 1 ]
16282 	0x32, SIG_UINT16(0x01c1),           // jmp 01c1 [ end of method ]
16283 	SIG_END
16284 };
16285 
16286 static const uint16 sq4CdPatchHzSoGoodSequelPoliceLockup[] = {
16287 	PATCH_ADDTOOFFSET(+3),
16288 	0x7a,                               // push2
16289 	PATCH_ADDTOOFFSET(+4),
16290 	0x7c,                               // pushSelf
16291 	0x4a, 0x08,                         // send 06 [ tSP1 say: 1 self ]
16292 	0x3a,                               // toss
16293 	0x48,                               // ret
16294 	PATCH_END
16295 };
16296 
16297 static const uint16 sq4CdSignatureHzSoGoodSequelPoliceCycler[] = {
16298 	0x31, 0x20,                         // bnt 20 [ state 12 ]
16299 	SIG_ADDTOOFFSET(+29),
16300 	SIG_MAGICDWORD,
16301 	0x32, SIG_UINT16(0x00a9),           // jmp 00a9 [ end of method ]
16302 	0x3c,                               // dup
16303 	0x35, 0x0c,                         // ldi 0c
16304 	0x1a,                               // eq?
16305 	0x30, SIG_UINT16(0x0030),           // bnt 0030 [ state 13 ]
16306 	0x7a,                               // push2 [ view ]
16307 	0x78,                               // push1
16308 	0x39, 0x0d,                         // pushi 0d
16309 	0x38, SIG_SELECTOR16(setLoop),      // pushi setLoop
16310 	SIG_ADDTOOFFSET(+32),
16311 	0x4a, 0x24,                         // send 24 [ sp1 view: 13 setLoop: ... ]
16312 	SIG_ADDTOOFFSET(+11),
16313 	0x31, 0x21,                         // bnt 21 [ state 14 ]
16314 	SIG_ADDTOOFFSET(+30),
16315 	0x32, SIG_UINT16(0x004b),           // jmp 004b [ end of method ]
16316 	0x3c,                               // dup
16317 	0x35, 0x0e,                         // ldi 0e
16318 	0x1a,                               // eq?
16319 	0x30, SIG_UINT16(0x0030),           // bnt 0030 [ state 15 ]
16320 	0x7a,                               // push2 [ view ]
16321 	0x78,                               // push1
16322 	0x39, 0x0d,                         // pushi 0d
16323 	0x38, SIG_SELECTOR16(setLoop),      // pushi setLoop
16324 	SIG_ADDTOOFFSET(+37),
16325 	0x4a, 0x26,                         // send 26 [ sp2 view: 13 setLoop: ... ]
16326 	SIG_END
16327 };
16328 
16329 static const uint16 sq4CdPatchHzSoGoodSequelPoliceCycler[] = {
16330 	0x31, 0x1d,                         // bnt 1d [ state 12 ]
16331 	PATCH_ADDTOOFFSET(+29),
16332 	0x3c,                               // dup
16333 	0x35, 0x0c,                         // ldi 0c
16334 	0x1a,                               // eq?
16335 	0x31, 0x34,                         // bnt 34 [ state 13 ]
16336 	0x38, PATCH_SELECTOR16(setCycle),   // pushi setCycle
16337 	0x78,                               // push1
16338 	0x76,                               // push0
16339 	0x7a,                               // push2 [ view ]
16340 	0x78,                               // push1
16341 	0x39, 0x0d,                         // pushi 0d
16342 	0x39, PATCH_SELECTOR8(loop),        // pushi loop
16343 	PATCH_ADDTOOFFSET(+32),
16344 	0x4a, 0x2a,                         // send 2a [ sp1 setCycle: 0 view: 13 loop: ... ]
16345 	PATCH_ADDTOOFFSET(+11),
16346 	0x31, 0x1e,                         // bnt 1e [ state 14 ]
16347 	PATCH_ADDTOOFFSET(+30),
16348 	0x3c,                               // dup
16349 	0x35, 0x0e,                         // ldi 0e
16350 	0x1a,                               // eq?
16351 	0x31, 0x34,                         // bnt 34 [ state 15 ]
16352 	0x38, PATCH_SELECTOR16(setCycle),   // pushi setCycle
16353 	0x78,                               // push1
16354 	0x76,                               // push0
16355 	0x7a,                               // push2 [ view ]
16356 	0x78,                               // push1
16357 	0x39, 0x0d,                         // pushi 0d
16358 	0x39, PATCH_SELECTOR8(loop),        // pushi loop
16359 	PATCH_ADDTOOFFSET(+37),
16360 	0x4a, 0x2c,                         // send 2c [ sp2 setCycle: 0 view: 13 loop: ... ]
16361 	PATCH_END
16362 };
16363 
16364 // Room 370 in front of Sock's is missing a flag check and runs a lethal Sequel
16365 //  Police script when escaping the Skate-O-Rama after hiding in the electronics
16366 //  store. A lockup bug in the CD version prevented getting this far. As this is
16367 //  the wrong time to run the script, the police are drawn as if they're still
16368 //  swimming in zero gravity.
16369 //
16370 // Flag 23 is set when the police pursue in escalator room 400 and triggers the
16371 //  squeeze scripts that force entering the Skate-O-Rama in rooms 370 and 390.
16372 //  Flag 22 is set when exiting the Skate-O-Rama after dodging the police. The
16373 //  two rooms handle these flags differently and are out of sync. Room 370
16374 //  clears flag 23 and doesn't test 22 while room 390 doesn't clear 23 and does
16375 //  test 22. The result is that room 370 doesn't see that you've escaped the
16376 //  Skate-O-Rama and assumes the police are still pursuing from room 400.
16377 //
16378 // We fix this by only running sp1Squeeze in room 370 when coming from room 400.
16379 //  This is equivalent to adding the missing flag 22 check but can be done in
16380 //  the available bytes. To make room we use the clear-flag method's return
16381 //  value, which is the previous flag value, and only test if the previous room
16382 //  number is even, which only room 400 is. This patch is split into two parts
16383 //  to surround a bnt instruction whose operand size changed between versions.
16384 //
16385 // Applies to: All versions
16386 // Responsible method: rm570:init
16387 // Fixes bug #10977
16388 static const uint16 sq4SignatureSocksSequelPoliceFlag1[] = {
16389 	0x78,                               // push1
16390 	SIG_MAGICDWORD,
16391 	0x39, 0x17,                         // pushi 17
16392 	0x45, 0x06, 0x02,                   // callb proc0_6 [ is flag 23 set? ]
16393 	SIG_END
16394 };
16395 
16396 static const uint16 sq4PatchSocksSequelPoliceFlag1[] = {
16397 	PATCH_ADDTOOFFSET(+3),
16398 	0x45, 0x08, 0x02,                   // callb proc0_8 [ clear flag 23, was flag set? ]
16399 	PATCH_END
16400 };
16401 
16402 static const uint16 sq4SignatureSocksSequelPoliceFlag2[] = {
16403 	0x78,                               // push1
16404 	SIG_MAGICDWORD,
16405 	0x39, 0x17,                         // pushi 17
16406 	0x45, 0x08, 0x02,                   // callb proc0_8 [ clear flag 23 ]
16407 	SIG_ADDTOOFFSET(+0x27),
16408 	0x38, SIG_SELECTOR16(setRegions),   // pushi setRegions
16409 	SIG_END
16410 };
16411 
16412 static const uint16 sq4PatchSocksSequelPoliceFlag2[] = {
16413 	0x81, 0x0c,                         // lag 0c
16414 	0x78,                               // push1
16415 	0x12,                               // and   [ is previous room number odd? ]
16416 	0x2f, 0x27,                         // bt 27 [ skip sp1Squeeze ]
16417 	PATCH_END
16418 };
16419 
16420 // Clicking Walk while getting shot by the Sequel Police outside of Sock's in
16421 //  room 370 crashes the CD version. This causes an Oops! error in the original.
16422 //  The lookupSelector error comes from within the Grooper and Grycler classes
16423 //  but the real bug is that this room's script fails to call handsOff, allowing
16424 //  movement during ego's death animation, unlike all the other laser scripts.
16425 //
16426 // We prevent the crash by adding the missing handsOff call.
16427 //
16428 // Applies to: English PC CD
16429 // Responsible method: sp2Squeeze:changeState(3)
16430 // Fixes bug #10974
16431 static const uint16 sq4CdSignatureSocksSequelPoliceHandsOff[] = {
16432 	0x76,                               // push0 [ y ]
16433 	0x76,                               // push0
16434 	0x81, 0x00,                         // lag 00
16435 	0x4a, 0x04,                         // send 04 [ ego y? ]
16436 	SIG_MAGICDWORD,
16437 	0x36,                               // push
16438 	0x35, 0x20,                         // ldi 20
16439 	0x04,                               // sub
16440 	0xa3, 0x00,                         // sal 00 [ local0 = ego:y ]
16441 	0x38, SIG_SELECTOR16(setMotion),    // pushi setMotion
16442 	0x78,                               // push1
16443 	0x76,                               // push0
16444 	0x81, 0x00,                         // lag 00
16445 	0x4a, 0x06,                         // send 06 [ ego setMotion: 0 ]
16446 	SIG_END
16447 };
16448 
16449 static const uint16 sq4CdPatchSocksSequelPoliceHandsOff[] = {
16450 	0x38, PATCH_SELECTOR16(setMotion),  // pushi setMotion
16451 	0x78,                               // push1
16452 	0x76,                               // push0
16453 	0x76,                               // push0 [ y ]
16454 	0x76,                               // push0
16455 	0x81, 0x00,                         // lag 00
16456 	0x4a, 0x0a,                         // send 0a [ ego setMotion: 0 y?, saves 4 bytes ]
16457 	0x36,                               // push
16458 	0x35, 0x20,                         // ldi 20
16459 	0x04,                               // sub
16460 	0xa3, 0x00,                         // sal 00 [ local0 = ego:y ]
16461 	0x76,                               // push0
16462 	0x45, 0x02, 0x00,                   // callb proc0_2 00 [ handsOff ]
16463 	PATCH_END
16464 };
16465 
16466 // The door to Sock's is immediately disposed of in the CD version, breaking its
16467 //  Look message and preventing it from being drawn when restoring a saved game.
16468 //  We remove the incorrect dispose call along with a redundant addToPic.
16469 //
16470 // Applies to: English PC CD
16471 // Responsible method: rm370:init
16472 // Fixes bug #10914
16473 static const uint16 sq4CdSignatureSocksDoor[] = {
16474 	0x38, SIG_SELECTOR16(addToPic),     // pushi addToPic
16475 	0x76,                               // push0
16476 	0x39, SIG_SELECTOR8(dispose),       // pushi dispose
16477 	0x76,                               // push0
16478 	SIG_MAGICDWORD,
16479 	0x72, SIG_UINT16(0x0176),           // lofsa door
16480 	0x4a, 0x08,                         // send 08 [ door addToPic: dispose: ]
16481 	SIG_END
16482 };
16483 
16484 static const uint16 sq4CdPatchSocksDoor[] = {
16485 	0x32, PATCH_UINT16(0x0009),         // jmp 0009
16486 	PATCH_END
16487 };
16488 
16489 // The scripts in SQ4CD support simultaneous playing of speech and subtitles,
16490 // but this was not available as an option. The following two patches enable
16491 // this functionality in the game's GUI options dialog.
16492 //
16493 // Patch 1: iconTextSwitch::show, called when the text options button is shown.
16494 //   This is patched to add the "Both" text resource (i.e. we end up with
16495 //   "Speech", "Text" and "Both")
16496 static const uint16 sq4CdSignatureTextOptionsButton[] = {
16497 	SIG_MAGICDWORD,
16498 	0x35, 0x01,                         // ldi 0x01
16499 	0xa1, 0x53,                         // sag global[0x53]
16500 	0x39, 0x03,                         // pushi 0x03
16501 	0x78,                               // push1
16502 	0x39, 0x09,                         // pushi 0x09
16503 	0x54, 0x06,                         // self 0x06
16504 	SIG_END
16505 };
16506 
16507 static const uint16 sq4CdPatchTextOptionsButton[] = {
16508 	PATCH_ADDTOOFFSET(+7),
16509 	0x39, 0x0b,                         // pushi 0x0b
16510 	PATCH_END
16511 };
16512 
16513 // Patch 2: Adjust a check in babbleIcon::init (the two guys from Andromeda),
16514 //  shown when dying/quitting.
16515 //
16516 // Responsible method: babbleIcon::init
16517 // Fixes bug: #6068
16518 static const uint16 sq4CdSignatureBabbleIcon[] = {
16519 	SIG_MAGICDWORD,
16520 	0x89, 0x5a,                         // lsg global[5a]
16521 	0x35, 0x02,                         // ldi 02
16522 	0x1a,                               // eq?
16523 	0x31, 0x26,                         // bnt 26 [02a7]
16524 	SIG_END
16525 };
16526 
16527 static const uint16 sq4CdPatchBabbleIcon[] = {
16528 	0x89, 0x5a,                         // lsg global[5a]
16529 	0x35, 0x01,                         // ldi 01
16530 	0x1a,                               // eq?
16531 	0x2f, 0x26,                         // bt 26 [02a7]
16532 	PATCH_END
16533 };
16534 
16535 // Patch 3: Add the ability to toggle among the three available options
16536 //  when the text options button is clicked: "Speech", "Text" and "Both".
16537 //  Refer to the patch above for additional details.
16538 //
16539 // Responsible method: iconTextSwitch::doit
16540 static const uint16 sq4CdSignatureTextOptions[] = {
16541 	SIG_MAGICDWORD,
16542 	0x89, 0x5a,                         // lsg global[90]
16543 	0x3c,                               // dup
16544 	0x35, 0x01,                         // ldi 0x01
16545 	0x1a,                               // eq?
16546 	0x31, 0x06,                         // bnt 0x06 (0x0691)
16547 	0x35, 0x02,                         // ldi 0x02
16548 	0xa1, 0x5a,                         // sag global[90]
16549 	0x33, 0x0a,                         // jmp 0x0a (0x69b)
16550 	0x3c,                               // dup
16551 	0x35, 0x02,                         // ldi 0x02
16552 	0x1a,                               // eq?
16553 	0x31, 0x04,                         // bnt 0x04 (0x069b)
16554 	0x35, 0x01,                         // ldi 0x01
16555 	0xa1, 0x5a,                         // sag global[90]
16556 	0x3a,                               // toss
16557 	0x38, SIG_SELECTOR16(show),         // pushi show (0x00d9)
16558 	0x76,                               // push0
16559 	0x54, 0x04,                         // self 0x04
16560 	0x48,                               // ret
16561 	SIG_END
16562 };
16563 
16564 static const uint16 sq4CdPatchTextOptions[] = {
16565 	0x89, 0x5a,                         // lsg global[90]
16566 	0x3c,                               // dup
16567 	0x35, 0x03,                         // ldi 0x03 (acc = 3)
16568 	0x1a,                               // eq? (global[90] == 3)
16569 	0x2f, 0x07,                         // bt 0x07
16570 	0x89, 0x5a,                         // lsg global[90]
16571 	0x35, 0x01,                         // ldi 0x01 (acc = 1)
16572 	0x02,                               // add (acc = global[90] + 1)
16573 	0x33, 0x02,                         // jmp 0x02
16574 	0x35, 0x01,                         // ldi 0x01 (reset acc to 1)
16575 	0xa1, 0x5a,                         // sag global[90]
16576 	0x33, 0x03,                         // jmp 0x03 (jump over the wasted bytes below)
16577 	0x34, PATCH_UINT16(0x0000),         // ldi 0x0000 (waste 3 bytes)
16578 	0x3a,                               // toss
16579 	// (the rest of the code is the same)
16580 	PATCH_END
16581 };
16582 
16583 // Vohaul's scene on the PocketPal (room 545) is incompatible with our dual
16584 //  text+speech mode and reportedly has MIDI timing issues in text mode.
16585 //
16586 // This is an unusual scene in that it uses three empty MIDI songs to control
16587 //  its text delays, which is probably why Sierra didn't fully upgrade it to
16588 //  use a Narrator in the CD version. Instead the tVOHAUL Narrator is only used
16589 //  in speech mode without the formatting it would need to display text. In text
16590 //  mode the original floppy code handles that.
16591 //
16592 // We heavily patch this script to support text+speech mode and remove the MIDIs
16593 //  from the equation. The trick to using tVOHAUL in dual mode is to set its
16594 //  nMsgType to 2. This causes Sq4Narrator to change the game's message mode to
16595 //  speech while it says a message, preventing it from displaying text in a
16596 //  message box since it wasn't provided formatting. Our code needs to know the
16597 //  real message mode while tVOHAUL is speaking so we store that in the script's
16598 //  register for later use.
16599 //
16600 // The audio and text for Vohaul's messages aren't the same. The audio for the
16601 //  second message also contains the third, and the third has no audio resource
16602 //  at all. We work around this by setting a three second timer and displaying
16603 //  the third text while the audio is already playing.
16604 //
16605 // Applies to: English PC CD
16606 // Responsible method: vohaulScript:changeState
16607 // Fixes bug #10241
16608 static const uint16 sq4CdSignatureVohaulPocketPalTextSpeech[] = {
16609 	0x3c,                               // dup
16610 	0x35, 0x00,                         // ldi 00
16611 	0x1a,                               // eq?
16612 	0x31, 0x26,                         // bnt 26 [ state 1 ]
16613 	SIG_ADDTOOFFSET(+49),
16614 	0x1a,                               // eq? [ is speech mode? ]
16615 	0x31, 0x22,                         // bnt 22
16616 	SIG_ADDTOOFFSET(+13),
16617 	0x38, SIG_SELECTOR16(modNum),       // pushi modNum [ unnecessary when modNum is room number ]
16618 	0x78,                               // push1
16619 	0x38, SIG_UINT16(0x0221),           // pushi 0221
16620 	SIG_ADDTOOFFSET(11),
16621 	0x32, SIG_UINT16(0x00d1),           // jmp 00d1 [ end of method ]
16622 	0x38, SIG_SELECTOR16(setCycle),     // pushi setCycle
16623 	SIG_ADDTOOFFSET(+9),
16624 	0x38, SIG_SELECTOR16(setCycle),     // pushi setCycle
16625 	0x78,                               // push1
16626 	0x51, 0x59,                         // class RandCycle
16627 	0x36,                               // push
16628 	0x72, SIG_UINT16(0x0578),           // lofsa vohaulEyes
16629 	0x4a, 0x06,                         // send 06
16630 	0x39, 0x04,                         // pushi 04
16631 	SIG_MAGICDWORD,
16632 	0x76,                               // push0
16633 	0x72, SIG_UINT16(0x0740),           // lofsa "Take a good look, Roger:"
16634 	0x36,                               // push
16635 	0x38, SIG_UINT16(0x0353),           // pushi 0353
16636 	0x7c,                               // pushSelf
16637 	0x40, SIG_UINT16(0xf93e), 0x08,     // call localproc_0062 08 [ display text until midi 851 finishes ]
16638 	0x32, SIG_UINT16(0x00a7),           // jmp 00a7 [ end of method ]
16639 	SIG_ADDTOOFFSET(+10),
16640 	0x1a,                               // eq? [ is text mode? ]
16641 	SIG_ADDTOOFFSET(+52),
16642 	0x1a,                               // eq? [ is speech mode? ]
16643 	0x31, 0x0e,                         // bnt 0e
16644 	SIG_ADDTOOFFSET(+11),
16645 	0x32, SIG_UINT16(0x0057),           // jmp 0057 [ end of method ]
16646 	0x39, 0x04,                         // pushi 04
16647 	0x76,                               // push0
16648 	0x72, SIG_UINT16(0x0760),           // lofsa "Remember this poor wretched soul..."
16649 	0x36,                               // push
16650 	0x38, SIG_UINT16(0x0354),           // pushi 0354
16651 	0x7c,                               // pushSelf
16652 	0x40, SIG_UINT16(0xf8dc), 0x08,     // call localproc_0062 08 [ display text until midi 852 finishes ]
16653 	0x32, SIG_UINT16(0x0045),           // jmp 0045 [ end of method ]
16654 	SIG_ADDTOOFFSET(+6),
16655 	0x89, 0x5a,                         // lsg 5a
16656 	0x35, 0x01,                         // ldi 01
16657 	0x1a,                               // eq?    [ is text mode? ]
16658 	0x31, 0x17,                         // bnt 17 [ skip text, set cycles = 1 ]
16659 	0x78,                               // push1
16660 	0x8b, 0x00,                         // lsl 00
16661 	0x45, 0x0c, 0x02,                   // call proc0_12 02 [ unnecessary, localproc_0062 calls this ]
16662 	0x39, 0x04,                         // pushi 04
16663 	0x76,                               // push0
16664 	0x72, SIG_UINT16(0x0784),           // lofsa "...for he is your SON!"
16665 	0x36,                               // push
16666 	0x38, SIG_UINT16(0x0355),           // pushi 0355
16667 	0x7c,                               // pushSelf
16668 	0x40, SIG_UINT16(0xf8b7), 0x08,     // call localproc_0062 08 [ display text until midi 853 finishes ]
16669 	0x33, 0x21,                         // jmp 21 [ end of method ]
16670 	0x35, 0x01,                         // ldi 01
16671 	0x65, 0x1a,                         // aTop cycles [ cycles = 1, speech completed or dismissed by user ]
16672 	0x33, 0x1b,                         // jmp 1b [ end of method ]
16673 	SIG_END
16674 };
16675 
16676 static const uint16 sq4CdPatchVohaulPocketPalTextSpeech[] = {
16677 	0x2f, 0x2a,                         // bt 26 [ state 1 ]
16678 	0x81, 0x5a,                         // lag 5a
16679 	0x65, 0x24,                         // aTop register [ register = message mode ]
16680 	PATCH_ADDTOOFFSET(+49),
16681 	0x12,                               // and [ is speech or dual mode? ]
16682 	0x31, 0x21,                         // bnt 21
16683 	PATCH_ADDTOOFFSET(+13),
16684 	0x38, PATCH_SELECTOR16(nMsgType),   // pushi nMsgType
16685 	0x78,                               // push1
16686 	0x38, PATCH_UINT16(0x0002),         // pushi 0002 [ speech ]
16687 	PATCH_ADDTOOFFSET(+11),
16688 	0x33, 0x1a,                         // jmp 1a
16689 	0x38, PATCH_SELECTOR16(setCycle),   // pushi setCycle
16690 	0x3c,                               // dup [ save 2 bytes ]
16691 	PATCH_ADDTOOFFSET(+9),
16692 	0x78,                               // push1
16693 	0x51, 0x59,                         // class RandCycle
16694 	0x36,                               // push
16695 	0x72, PATCH_UINT16(0x0578),         // lofsa vohaulEyes
16696 	0x4a, 0x06,                         // send 06
16697 	0x35, 0x02,                         // ldi 02
16698 	0x65, 0x1c,                         // aTop seconds [ 2 second delay in text mode ]
16699 	0x63, 0x24,                         // pToa register
16700 	0x78,                               // push1
16701 	0x12,                               // and    [ is text or dual mode? ]
16702 	0x31, 0x0b,                         // bnt 0b [ don't display text ]
16703 	0x39, 0x03,                         // pushi 03
16704 	0x76,                               // push0
16705 	0x74, PATCH_UINT16(0x0740),         // lofss "Take a good look, Roger:"
16706 	0x76,                               // push0
16707 	0x40, PATCH_UINT16(0xf93b), 0x06,   // call localproc_0062 06 [ display text without midi ]
16708 	PATCH_ADDTOOFFSET(+10),
16709 	0x12,                               // and [ is text or dual mode? ]
16710 	PATCH_ADDTOOFFSET(+52),
16711 	0x12,                               // and [ is speech or dual mode? ]
16712 	0x31, 0x0b,                         // bnt 0b
16713 	PATCH_ADDTOOFFSET(+11),
16714 	0x63, 0x24,                         // pToa register
16715 	0x78,                               // push1
16716 	0x12,                               // and    [ is text or dual mode? ]
16717 	0x31, 0x0f,                         // bnt 0f [ don't display text ]
16718 	0x39, 0x03,                         // pushi 03
16719 	0x76,                               // push0
16720 	0x74, PATCH_UINT16(0x0760),         // lofss "Remember this poor wretched soul..."
16721 	0x76,                               // push0
16722 	0x40, PATCH_UINT16(0xf8dd), 0x06,   // call localproc_0062 06 [ display text without midi ]
16723 	0x35, 0x03,                         // ldi 03
16724 	0x65, 0x1c,                         // aTop seconds [ 3 second delay in text or dual mode ]
16725 	PATCH_ADDTOOFFSET(+6),
16726 	0x63, 0x1c,                         // pToa seconds
16727 	0x2f, 0x1d,                         // bnt 1d [ speech dismissed by user, set cycles = 1 ]
16728 	0x63, 0x24,                         // pToa register
16729 	0x78,                               // push1
16730 	0x1a,                               // eq?    [ is text mode? ]
16731 	0x31, 0x04,                         // bnt 04 [ don't set text delay ]
16732 	0x35, 0x03,                         // ldi 03
16733 	0x65, 0x1c,                         // aTop seconds [ 3 second delay in text mode ]
16734 	0x63, 0x24,                         // pToa register
16735 	0x78,                               // push1
16736 	0x12,                               // and    [ is text or dual mode? ]
16737 	0x31, 0x0d,                         // bnt 0d [ skip text, set cycles = 1 ]
16738 	0x39, 0x03,                         // pushi 03
16739 	0x76,                               // push0
16740 	0x74, PATCH_UINT16(0x0784),         // lofss "...for he is your SON!"
16741 	0x76,                               // push0
16742 	0x40, PATCH_UINT16(0xf8b4), 0x06,   // call localproc_0062 06 [ display text without midi ]
16743 	0x33, 0x03,                         // jmp 03
16744 	0x78,                               // push1
16745 	0x69, 0x1a,                         // sTop cycles [ cycles = 1, speech completed ]
16746 	PATCH_END
16747 };
16748 
16749 // Walking around the sewer tunnels in the following sequence locks up the game:
16750 //
16751 //  1. Enter the ladder room (90) from the center room (95) while the slime is
16752 //     just below the middle of the screen
16753 //  2. Enter the southwest room (105) from the ladder room (90)
16754 //
16755 // The script enterNorth has a code path which fails to advance the state and so
16756 //  it gets stuck in handsOff mode. If sewer:status is 3, meaning the slime is
16757 //  moving north or south, then enterNorth assumes that sewer:location, the room
16758 //  the slime is in, must be room 105 or 90, but in the sequence above it is 95.
16759 //
16760 // We fix this by setting enterNorth:state to 1 in the problematic code path so
16761 //  that the script advances. Sierra fixed this bug after the English PC floppy
16762 //  versions but forgot to include the fix in the CD version over a year later.
16763 //
16764 // Applies to: English PC Floppy, English PC CD
16765 // Responsible method: enterNorth:changeState(0)
16766 // Fixes bug #10970
16767 static const uint16 sq4FloppySignatureSewerLockup[] = {
16768 	SIG_MAGICDWORD,
16769 	0x35, 0x01,                         // ldi 01
16770 	0x65, 0x0a,                         // aTop state [ state = 1 ]
16771 	0x32, SIG_UINT16(0x002e),           // jmp 002e   [ end of switch ]
16772 	0x3c,                               // dup
16773 	0x35, 0x5a,                         // ldi 5a [ ladder room ]
16774 	0x1a,                               // eq?
16775 	0x30, SIG_UINT16(0x0027),           // bnt 0027 [ end of switch without setting state ]
16776 	SIG_END
16777 };
16778 
16779 static const uint16 sq4FloppyPatchSewerLockup[] = {
16780 	PATCH_ADDTOOFFSET(+11),
16781 	0x30, PATCH_UINT16(0xfff2),         // bnt fff2 [ set state before end of switch ]
16782 	PATCH_END
16783 };
16784 
16785 static const uint16 sq4CDSignatureSewerLockup[] = {
16786 	SIG_MAGICDWORD,
16787 	0x35, 0x01,                         // ldi 01
16788 	0x65, 0x14,                         // aTop state [ state = 1 ]
16789 	0x33, 0x2c,                         // jmp 2c     [ end of switch ]
16790 	0x3c,                               // dup
16791 	0x35, 0x5a,                         // ldi 5a [ ladder room ]
16792 	0x1a,                               // eq?
16793 	0x31, 0x26,                         // bnt 26 [ end of switch without setting state ]
16794 	SIG_END
16795 };
16796 
16797 static const uint16 sq4CDPatchSewerLockup[] = {
16798 	PATCH_ADDTOOFFSET(+10),
16799 	0x31, 0xf4,                         // bnt f4 [ set state before end of switch ]
16800 	PATCH_END
16801 };
16802 
16803 // SQ4CD had an easter egg room of things removed from Sierra games for legal
16804 //  reasons, but the room itself was removed from the game. Instead the room's
16805 //  pic (570) and messages (271) were left in along with the 18 digit timepod
16806 //  code that attempts to load the missing room 271 and of course crashes.
16807 //
16808 // This wouldn't be a problem except that the code is publicly known due to NRS'
16809 //  modified version of the game which includes a script 271 that recreates the
16810 //  room. The code appears in easter egg lists, and players who don't realize it
16811 //  only applies to a modified version attempt it and crash, so we disable it.
16812 //
16813 // Applies to: English PC CD
16814 // Responsible method: timeToTimeWarpS:changeState(1)
16815 // Fixes bug #11006
16816 static const uint16 sq4CdSignatureRemovedRoomTimepodCode[] = {
16817 	SIG_MAGICDWORD,
16818 	0x35, 0x01,                         // ldi 01 [ 1 == room 271 code was entered ]
16819 	0xa3, 0x72,                         // sal 72
16820 	SIG_END
16821 };
16822 
16823 static const uint16 sq4CdPatchRemovedRoomTimepodCode[] = {
16824 	0x35, 0x00,                         // ldi 00
16825 	PATCH_END
16826 };
16827 
16828 // Walking into Sock's dressing room (room 371) can cause ego to escape obstacle
16829 //  boundaries and get stuck behind the wall or counter. Similar problems occur
16830 //  in the original. The dressing room has no obstacle bounding the edge of the
16831 //  screen. Instead, rm371:doit detects if ego has hit the edge and moves him
16832 //  back to x coordinate 173 but doesn't change ego:y. If ego hits the edge on a
16833 //  diagonal pathfinding move then this can place ego around the corner of one
16834 //  of the obstacles that bound the top and bottom of the dressing room.
16835 //  rm371:doit will then move ego within the obstacle and out of bounds.
16836 //
16837 // We fix this by extending the two obstacles an additional 10 pixels past the
16838 //  edge of the screen so that ego can't get around their corners and get stuck.
16839 //  Sierra reduced one of these coordinates in later floppy versions, and it's
16840 //  not clear why, but this change wasn't included in the CD version.
16841 //
16842 // Applies to: All versions
16843 // Responsible method: rm371:init
16844 // Fixes bug #11055
16845 static const uint16 sq4SignatureSocksDressingRoomObstacles[] = {
16846 	0x38, SIG_ADDTOOFFSET(+2),          // pushi 321d or 319d [ x ]
16847 	0x39, 0x46,                         // pushi 70d  [ y ]
16848 	SIG_ADDTOOFFSET(+125),
16849 	0x38, SIG_MAGICDWORD,               // pushi 321d [ x ]
16850 	      SIG_UINT16(0x0141),
16851 	0x39, 0x49,                         // pushi 73d  [ y ]
16852 	SIG_END
16853 };
16854 
16855 static const uint16 sq4PatchSocksDressingRoomObstacles[] = {
16856 	0x38, PATCH_UINT16(0x014b),         // pushi 331d [ x ]
16857 	PATCH_ADDTOOFFSET(+127),
16858 	0x38, PATCH_UINT16(0x014b),         // pushi 331d [ x ]
16859 	PATCH_END
16860 };
16861 
16862 // SQ4CD lets you keep the unstable ordnance and its points for the entire game.
16863 //
16864 // The bomb in room 40 is a joke item with joke points which kills you when
16865 //  entering the sewer, therefore you're not allowed to pick it up after leaving
16866 //  the sewer. This was originally enforced in the floppy version by setting a
16867 //  short timer which summons the Sequel Police to kill you. The shootEgo script
16868 //  was refactored in the CD version and this code no longer works. Rather than
16869 //  fix this, Sierra left the broken code in place, and added new code to kill
16870 //  you when interacting with the tank if the previous room was the sewer. This
16871 //  is incorrect since you can walk right to room 45 and return to room 40,
16872 //  which defeats both checks and allows you to get and keep the bomb.
16873 //
16874 // We fix this by replacing the previous room test with a flag test. Flag 0 is
16875 //  set when the police are on the streets and is what the original code tested.
16876 //
16877 // Applies to: English PC CD
16878 // Responsible method: tankScript:changeState(2)
16879 // Fixes bug #11077
16880 static const uint16 sq4CdSignatureUnstableOrdnance[] = {
16881 	SIG_MAGICDWORD,
16882 	0x31, 0x2b,                         // bnt 2b
16883 	0x89, 0x0c,                         // lsg 0c [ previous room ]
16884 	0x35, 0x48,                         // ldi 48 [ sewer manhole ]
16885 	0x1a,                               // eq?    [ came from sewer? ]
16886 	SIG_END
16887 };
16888 
16889 static const uint16 sq4CdPatchUnstableOrdnance[] = {
16890 	PATCH_ADDTOOFFSET(+2),
16891 	0x78,                               // push1
16892 	0x76,                               // push0 [ flag 0, set when police are on streets ]
16893 	0x45, 0x06, 0x02,                   // callb proc0_6 02 [ is flag 0 set? ]
16894 	PATCH_END
16895 };
16896 
16897 //          script, description,                                      signature                                      patch
16898 static const SciScriptPatcherEntry sq4Signatures[] = {
16899 	{  true,     1, "Floppy: EGA intro delay fix",                    2, sq4SignatureEgaIntroDelay,                     sq4PatchEgaIntroDelay },
16900 	{  true,   298, "Floppy: endless flight",                         1, sq4FloppySignatureEndlessFlight,               sq4FloppyPatchEndlessFlight },
16901 	{  true,   376, "Floppy: set sequel police description",          1, sq4FloppySignatureSequelPoliceDescription,     sq4FloppyPatchSequelPoliceDescription },
16902 	{  true,   376, "Floppy: click atm card on sequel police fix",    1, sq4FloppySignatureClickAtmCardOnSequelPolice,  sq4FloppyPatchClickAtmCardOnSequelPolice },
16903 	{  true,   376, "Floppy: throw stuff at sequel police fix",       1, sq4FloppySignatureThrowStuffAtSequelPolice,    sq4FloppyPatchThrowStuffAtSequelPolice },
16904 	{  true,   700, "Floppy: throw stuff at sequel police fix",       1, sq4FloppySignatureThrowStuffAtSequelPolice,    sq4FloppyPatchThrowStuffAtSequelPolice },
16905 	{  true,    40, "CD: unstable ordnance fix",                      1, sq4CdSignatureUnstableOrdnance,                sq4CdPatchUnstableOrdnance },
16906 	{  true,    45, "CD: walk in from below for room 45 fix",         1, sq4CdSignatureWalkInFromBelowRoom45,           sq4CdPatchWalkInFromBelowRoom45 },
16907 	{  true,   105, "Floppy: sewer lockup fix",                       1, sq4FloppySignatureSewerLockup,                 sq4FloppyPatchSewerLockup },
16908 	{  true,   105, "CD: sewer lockup fix",                           1, sq4CDSignatureSewerLockup,                     sq4CDPatchSewerLockup },
16909 	{  true,   290, "CD: cedric easter egg fix",                      1, sq4CdSignatureCedricEasterEgg,                 sq4CdPatchCedricEasterEgg },
16910 	{  true,   290, "CD: cedric lockup fix (1/2)",                    1, sq4CdSignatureCedricLockup1,                   sq4CdPatchCedricLockup1 },
16911 	{  true,   290, "CD: cedric lockup fix (2/2)",                    1, sq4CdSignatureCedricLockup2,                   sq4CdPatchCedricLockup2 },
16912 	{  true,   370, "CD: sock's sequel police hands-off fix",         1, sq4CdSignatureSocksSequelPoliceHandsOff,       sq4CdPatchSocksSequelPoliceHandsOff },
16913 	{  true,   370, "CD: sock's door restore and message fix",        1, sq4CdSignatureSocksDoor,                       sq4CdPatchSocksDoor },
16914 	{  true,   370, "CD/Floppy: sock's sequel police flag fix (1/2)", 1, sq4SignatureSocksSequelPoliceFlag1,            sq4PatchSocksSequelPoliceFlag1 },
16915 	{  true,   370, "CD/Floppy: sock's sequel police flag fix (2/2)", 1, sq4SignatureSocksSequelPoliceFlag2,            sq4PatchSocksSequelPoliceFlag2 },
16916 	{  true,   371, "CD/Floppy: sock's dressing room obstacles fix",  1, sq4SignatureSocksDressingRoomObstacles,        sq4PatchSocksDressingRoomObstacles },
16917 	{ false,   370, "Amiga: dress purchase flag check fix",           1, sq4AmigaSignatureDressPurchaseFlagCheck,       sq4AmigaPatchDressPurchaseFlagCheck },
16918 	{ false,   371, "Amiga: dress purchase flag clear fix",           1, sq4AmigaSignatureDressPurchaseFlagClear,       sq4AmigaPatchDressPurchaseFlagClear },
16919 	{ false,   386, "Amiga: dress purchase flag check fix",           1, sq4AmigaSignatureDressPurchaseFlagCheck,       sq4AmigaPatchDressPurchaseFlagCheck },
16920 	{  true,   381, "CD: big and tall room description",              1, sq4CdSignatureBigAndTallDescription,           sq4CdPatchBigAndTallDescription },
16921 	{  true,   385, "CD: monolith burger door message fix",           1, sq4CdSignatureMonolithBurgerDoor,              sq4CdPatchMonolithBurgerDoor },
16922 	{  true,   390, "CD: hz so good sequel police lockup fix",        1, sq4CdSignatureHzSoGoodSequelPoliceLockup,      sq4CdPatchHzSoGoodSequelPoliceLockup },
16923 	{  true,   390, "CD: hz so good sequel police cycler fix",        1, sq4CdSignatureHzSoGoodSequelPoliceCycler,      sq4CdPatchHzSoGoodSequelPoliceCycler },
16924 	{  true,   391, "CD: missing Audio for universal remote control", 1, sq4CdSignatureMissingAudioUniversalRemote,     sq4CdPatchMissingAudioUniversalRemote },
16925 	{  true,   396, "CD: get points for changing back clothes fix",   1, sq4CdSignatureGetPointsForChangingBackClothes, sq4CdPatchGetPointsForChangingBackClothes },
16926 	{  true,   405, "CD/Floppy: zero gravity blast fix",              1, sq4SignatureZeroGravityBlast,                  sq4PatchZeroGravityBlast },
16927 	{  true,   406, "CD/Floppy: zero gravity blast fix",              1, sq4SignatureZeroGravityBlast,                  sq4PatchZeroGravityBlast },
16928 	{  true,   410, "CD/Floppy: zero gravity blast fix",              1, sq4SignatureZeroGravityBlast,                  sq4PatchZeroGravityBlast },
16929 	{  true,   411, "CD/Floppy: zero gravity blast fix",              1, sq4SignatureZeroGravityBlast,                  sq4PatchZeroGravityBlast },
16930 	{ false,   531, "CD: disable timepod code for removed room",      1, sq4CdSignatureRemovedRoomTimepodCode,          sq4CdPatchRemovedRoomTimepodCode },
16931 	{  true,   545, "CD: vohaul pocketpal text+speech fix",           1, sq4CdSignatureVohaulPocketPalTextSpeech,       sq4CdPatchVohaulPocketPalTextSpeech },
16932 	{  true,   610, "CD: biker bar door fix",                         1, sq4CdSignatureBikerBarDoor,                    sq4CdPatchBikerBarDoor },
16933 	{  true,   610, "CD: biker hands-on fix",                         3, sq4CdSignatureBikerHandsOn,                    sq4CdPatchBikerHandsOn },
16934 	{  true,   611, "CD: biker hands-on fix",                         1, sq4CdSignatureBikerHandsOn,                    sq4CdPatchBikerHandsOn },
16935 	{  true,   612, "CD: biker hands-on fix",                         2, sq4CdSignatureBikerHandsOn,                    sq4CdPatchBikerHandsOn },
16936 	{  true,   613, "CD: biker hands-on fix",                         2, sq4CdSignatureBikerHandsOn,                    sq4CdPatchBikerHandsOn },
16937 	{  true,   614, "CD: biker hands-on fix",                         1, sq4CdSignatureBikerHandsOn,                    sq4CdPatchBikerHandsOn },
16938 	{  true,   613, "CD: biker timepod message fix",                  1, sq4CdSignatureBikerTimepodMessage,             sq4CdPatchBikerTimepodMessage },
16939 	{  true,   700, "CD: red shopper message fix",                    1, sq4CdSignatureRedShopperMessageFix,            sq4CdPatchRedShopperMessageFix },
16940 	{  true,   701, "CD: getting shot, while getting rope",           1, sq4CdSignatureGettingShotWhileGettingRope,     sq4CdPatchGettingShotWhileGettingRope },
16941 	{  true,   706, "CD: biker crouch verb fix",                      2, sq4CdSignatureBikerCrouchVerb,                 sq4CdPatchBikerCrouchVerb },
16942 	{  true,     0, "CD: Babble icon speech and subtitles fix",       1, sq4CdSignatureBabbleIcon,                      sq4CdPatchBabbleIcon },
16943 	{  true,   818, "CD: Speech and subtitles option",                1, sq4CdSignatureTextOptions,                     sq4CdPatchTextOptions },
16944 	{  true,   818, "CD: Speech and subtitles option button",         1, sq4CdSignatureTextOptionsButton,               sq4CdPatchTextOptionsButton },
16945 	SCI_SIGNATUREENTRY_TERMINATOR
16946 };
16947 
16948 // ===========================================================================
16949 // When you leave Ulence Flats, another timepod is supposed to appear.
16950 // On fast machines, that timepod appears fully immediately and then
16951 //  starts to appear like it should be. That first appearance is caused
16952 //  by the scripts setting an invalid cel number and the machine being
16953 //  so fast that there is no time for another script to actually fix
16954 //  the cel number. On slower machines, the cel number gets fixed
16955 //  by the cycler and that's why only fast machines are affected.
16956 //  The same issue happens in Sierra SCI.
16957 // We simply set the correct starting cel number to fix the bug.
16958 // Responsible method: robotIntoShip::changeState(9)
16959 static const uint16 sq1vgaSignatureUlenceFlatsTimepodGfxGlitch[] = {
16960 	0x39,
16961 	SIG_MAGICDWORD, SIG_SELECTOR8(cel), // pushi cel
16962 	0x78,                               // push1
16963 	0x39, 0x0a,                         // pushi 0x0a (set ship::cel to 10)
16964 	0x38, SIG_UINT16(0x00a0),           // pushi 0x00a0 (ship::setLoop)
16965 	SIG_END
16966 };
16967 
16968 static const uint16 sq1vgaPatchUlenceFlatsTimepodGfxGlitch[] = {
16969 	PATCH_ADDTOOFFSET(+3),
16970 	0x39, 0x09,                         // pushi 0x09 (set ship::cel to 9)
16971 	PATCH_END
16972 };
16973 
16974 // In Ulence Flats, there is a space ship, that you will use at some point.
16975 //  Near that space ship are 2 force field generators. When you look at the top
16976 //  of those generators, the game will crash. This happens also in Sierra SCI.
16977 //  It's caused by a jump, that goes out of bounds.
16978 //
16979 // We currently do not know if this was caused by a compiler glitch or if it
16980 //  was a developer error. Anyway we patch this glitchy code, so that the game
16981 //  won't crash anymore.
16982 //
16983 // Applies to at least: English Floppy
16984 // Responsible method: radar1::doVerb
16985 // Fixes bug: #6816
16986 static const uint16 sq1vgaSignatureUlenceFlatsGeneratorGlitch[] = {
16987 	SIG_MAGICDWORD, 0x1a,               // eq?
16988 	0x30, SIG_UINT16(0xcdf4),           // bnt [absolute 0xf000]
16989 	SIG_END
16990 };
16991 
16992 static const uint16 sq1vgaPatchUlenceFlatsGeneratorGlitch[] = {
16993 	PATCH_ADDTOOFFSET(+1),
16994 	0x32, PATCH_UINT16(0x0000),         // jmp 0x0000 (waste bytes)
16995 	PATCH_END
16996 };
16997 
16998 // No documentation for this patch (TODO)
16999 static const uint16 sq1vgaSignatureEgoShowsCard[] = {
17000 	SIG_MAGICDWORD,
17001 	0x38, SIG_SELECTOR16(timesShownID), // pushi timesShownID
17002 	0x78,                               // push1
17003 	0x38, SIG_SELECTOR16(timesShownID), // pushi timesShownID
17004 	0x76,                               // push0
17005 	0x51, 0x7c,                         // class DeltaurRegion
17006 	0x4a, 0x04,                         // send 0x04 (get timesShownID)
17007 	0x36,                               // push
17008 	0x35, 0x01,                         // ldi 1
17009 	0x02,                               // add
17010 	0x36,                               // push
17011 	0x51, 0x7c,                         // class DeltaurRegion
17012 	0x4a, 0x06,                         // send 0x06 (set timesShownID)
17013 	0x36,                               // push      (wrong, acc clobbered by class, above)
17014 	0x35, 0x03,                         // ldi 0x03
17015 	0x22,                               // lt?
17016 	SIG_END
17017 };
17018 
17019 // Note that this script patch is merely a reordering of the
17020 // instructions in the original script.
17021 static const uint16 sq1vgaPatchEgoShowsCard[] = {
17022 	0x38, PATCH_SELECTOR16(timesShownID), // pushi timesShownID
17023 	0x76,                               // push0
17024 	0x51, 0x7c,                         // class DeltaurRegion
17025 	0x4a, 0x04,                         // send 0x04 (get timesShownID)
17026 	0x36,                               // push
17027 	0x35, 0x01,                         // ldi 1
17028 	0x02,                               // add
17029 	0x36,                               // push (this push corresponds to the wrong one above)
17030 	0x38, PATCH_SELECTOR16(timesShownID), // pushi timesShownID
17031 	0x78,                               // push1
17032 	0x36,                               // push
17033 	0x51, 0x7c,                         // class DeltaurRegion
17034 	0x4a, 0x06,                         // send 0x06 (set timesShownID)
17035 	0x35, 0x03,                         // ldi 0x03
17036 	0x22,                               // lt?
17037 	PATCH_END
17038 };
17039 
17040 // The spider droid on planet Korona has a fixed movement speed,
17041 //  which is way faster than the default movement speed of ego.
17042 // This means that the player would have to turn up movement speed,
17043 //  otherwise it will be impossible to escape it.
17044 // We fix this issue by making the droid move a bit slower than ego
17045 //  does (relative to movement speed setting).
17046 //
17047 // Applies to at least: English PC floppy
17048 // Responsible method: spider::doit
17049 static const uint16 sq1vgaSignatureSpiderDroidTiming[] = {
17050 	SIG_MAGICDWORD,
17051 	0x63, 0x4e,                         // pToa script
17052 	0x30, SIG_UINT16(0x0005),           // bnt [further method code]
17053 	0x35, 0x00,                         // ldi 00
17054 	0x32, SIG_UINT16(0x0062),           // jmp [super-call]
17055 	0x38, SIG_UINT16(0x0088),           // pushi 0088h (script)
17056 	0x76,                               // push0
17057 	0x81, 0x02,                         // lag global[2] (current room)
17058 	0x4a, 0x04,                         // send 04 (get room script)
17059 	0x30, SIG_UINT16(0x0005),           // bnt [further method code]
17060 	0x35, 0x00,                         // ldi 00
17061 	0x32, SIG_UINT16(0x0052),           // jmp [super-call]
17062 	0x89, 0xa6,                         // lsg global[a6] (set to 1 when ego went up the skeleton tail, set to 2 when going down)
17063 	0x35, 0x01,                         // ldi 01
17064 	0x1a,                               // eq?
17065 	0x30, SIG_UINT16(0x0012),           // bnt [PChase set code] (when global[A6] != 1)
17066 	0x81, 0xb5,                         // lag global[b5]
17067 	0x30, SIG_UINT16(0x000d),           // bnt [PChase set code] (when global[B5] == 0)
17068 	0x38, SIG_UINT16(0x008c),           // pushi 008c
17069 	0x78,                               // push1
17070 	0x72, SIG_UINT16(0x1cb6),           // lofsa moveToPath
17071 	0x36,                               // push
17072 	0x54, 0x06,                         // self 06
17073 	0x32, SIG_UINT16(0x0038),           // jmp [super-call]
17074 	// PChase set call
17075 	0x81, 0xb5,                         // lag global[B5]
17076 	0x18,                               // not
17077 	0x30, SIG_UINT16(0x0032),           // bnt [super-call] (when global[B5] != 0)
17078 	// followed by:
17079 	// is spider in current room
17080 	// is global[A6h] == 2? -> set PChase
17081 	SIG_END
17082 }; // 58 bytes)
17083 
17084 // global[A6h] != 1 (did NOT went up the skeleton)
17085 //  global[B5h] = 0 -> set PChase
17086 //  global[B5h] != 0 -> do not do anything
17087 // global[A6h] = 1 (did went up the skeleton)
17088 //  global[B5h] = 0 -> set PChase
17089 //  global[B5h] != 0 -> set moveToPath
17090 
17091 static const uint16 sq1vgaPatchSpiderDroidTiming[] = {
17092 	0x63, 0x4e,                         // pToa script
17093 	0x2f, 0x68,                         // bt [super-call]
17094 	0x38, PATCH_UINT16(0x0088),         // pushi 0088 (script)
17095 	0x76,                               // push0
17096 	0x81, 0x02,                         // lag global[2] (current room)
17097 	0x4a, 0x04,                         // send 04
17098 	0x2f, 0x5e,                         // bt [super-call]
17099 	// --> 12 bytes saved
17100 	// new code
17101 	0x38, PATCH_UINT16(0x0176),         // pushi 0176 (egoMoveSpeed)
17102 	0x76,                               // push0
17103 	0x81, 0x01,                         // lag global[1]
17104 	0x4a, 0x04,                         // send 04 - sq1::egoMoveSpeed
17105 	0x36,                               // push
17106 	0x36,                               // push
17107 	0x35, 0x03,                         // ldi 03
17108 	0x0c,                               // shr
17109 	0x02,                               // add --> egoMoveSpeed + (egoMoveSpeed >> 3)
17110 	0x39, 0x01,                         // pushi 01 (waste 1 byte)
17111 	0x02,                               // add --> egoMoveSpeed++
17112 	0x65, 0x4c,                         // aTop cycleSpeed
17113 	0x65, 0x5e,                         // aTop moveSpeed
17114 	// new code end
17115 	0x81, 0xb5,                         // lag global[B5]
17116 	0x31, 0x13,                         // bnt [PChase code chunk]
17117 	0x89, 0xa6,                         // lsg global[A6]
17118 	0x35, 0x01,                         // ldi 01
17119 	0x1a,                               // eq?
17120 	0x31, 0x3e,                         // bnt [super-call]
17121 	0x38, PATCH_UINT16(0x008c),         // pushi 008c
17122 	0x78,                               // push1
17123 	0x72, PATCH_UINT16(0x1cb6),         // lofsa moveToPath
17124 	0x36,                               // push
17125 	0x54, 0x06,                         // self 06 - spider::setScript(movePath)
17126 	0x33, 0x32,                         // jmp [super-call]
17127 	// --> 9 bytes saved
17128 	PATCH_END
17129 };
17130 
17131 // The Russian version of SQ1VGA has mangled class names in its scripts. This
17132 //  isn't a problem in Sierra's interpreter since this is just metadata, but our
17133 //  feature detection code looks up several classes by name and requires them to
17134 //  exist. We fix this by patching the Motion, Rm, and Sound strings back to
17135 //  their original values.
17136 //
17137 // Applies to: Russian PC Floppy
17138 // Fixes bug: #10156
17139 static const uint16 sq1vgaSignatureRussianMotionName[] = {
17140 	SIG_MAGICDWORD,
17141 	0x2A, 0x4D, 0x6F, 0x74, 0x69,       // *Motion.
17142 	0x6F, 0x6E, 0x20,
17143 	SIG_END
17144 };
17145 
17146 static const uint16 sq1vgaPatchRussianMotionName[] = {
17147 	0x4D, 0x6F, 0x74, 0x69, 0x6F,       // Motion
17148 	0x6E, 0x00,
17149 	PATCH_END
17150 };
17151 static const uint16 sq1vgaSignatureRussianRmName[] = {
17152 	SIG_MAGICDWORD,
17153 	0x2a, 0x52, 0x6d, 0x00,             // *Rm
17154 	SIG_END
17155 };
17156 
17157 static const uint16 sq1vgaPatchRussianRmName[] = {
17158 	0x52, 0x6d, 0x00,                   // Rm
17159 	PATCH_END
17160 };
17161 
17162 static const uint16 sq1vgaSignatureRussianSoundName[] = {
17163 	SIG_MAGICDWORD,
17164 	0x87, 0xa2, 0xe3, 0xaa, 0x00, 0x00, // ....
17165 	SIG_END
17166 };
17167 
17168 static const uint16 sq1vgaPatchRussianSoundName[] = {
17169 	0x53, 0x6f, 0x75, 0x63, 0x64,       // Sound
17170 	PATCH_END
17171 };
17172 
17173 //          script, description,                                      signature                                   patch
17174 static const SciScriptPatcherEntry sq1vgaSignatures[] = {
17175 	{  true,    45, "Ulence Flats: timepod graphic glitch",        1, sq1vgaSignatureUlenceFlatsTimepodGfxGlitch, sq1vgaPatchUlenceFlatsTimepodGfxGlitch },
17176 	{  true,    45, "Ulence Flats: force field generator glitch",  1, sq1vgaSignatureUlenceFlatsGeneratorGlitch,  sq1vgaPatchUlenceFlatsGeneratorGlitch },
17177 	{  true,    58, "Sarien armory droid zapping ego first time",  1, sq1vgaSignatureEgoShowsCard,                sq1vgaPatchEgoShowsCard },
17178 	{  true,   704, "spider droid timing issue",                   1, sq1vgaSignatureSpiderDroidTiming,           sq1vgaPatchSpiderDroidTiming },
17179 	{  true,   989, "rename russian Sound class",                  1, sq1vgaSignatureRussianSoundName,            sq1vgaPatchRussianSoundName },
17180 	{  true,   992, "rename russian Motion class",                 1, sq1vgaSignatureRussianMotionName,           sq1vgaPatchRussianMotionName },
17181 	{  true,   994, "rename russian Rm class",                     1, sq1vgaSignatureRussianRmName,               sq1vgaPatchRussianRmName },
17182 	SCI_SIGNATUREENTRY_TERMINATOR
17183 };
17184 
17185 // ===========================================================================
17186 // The toolbox in sq5 is buggy. When you click on the upper part of the "put
17187 //  in inventory" button (some items only - for example the hole puncher at the
17188 //  upper left), points will get awarded correctly, and the item will get put
17189 //  into the player's inventory, but you will then get a "not here" message,
17190 //  and the item will also remain as the current mouse cursor.
17191 // The bug report says items may get lost when exiting the toolbox screen,
17192 //  That was not reproduced.
17193 // This is caused by the mouse-click event getting reprocessed (which wouldn't
17194 //  be a problem by itself). Reprocessing treats coordinates differently from
17195 //  the first click (script 226 includes a local subroutine, which checks
17196 //  coordinates in a hardcoded way w/o port-adjustment).
17197 // Because of this, the hotspot for the button is lower than it should be,
17198 //  which results in the game thinking the user didn't click on the button and
17199 //  also results in the "not here" message.
17200 // We fix it by combining state 0 + 1 of takeTool::changeState and so stopping
17201 //  the event from being reprocessed... without touching SCI system scripts.
17202 // Applies to at least: English/German/French PC floppy
17203 // Responsible method: takeTool::changeState
17204 // Fixes bug: #6457
17205 static const uint16 sq5SignatureToolboxFix[] = {
17206 	0x31, 0x13,                    // bnt [check for state 1]
17207 	SIG_MAGICDWORD,
17208 	0x38, SIG_UINT16(0x00aa),      // pushi 00aa
17209 	0x39, 0x05,                    // pushi 05
17210 	0x39, 0x16,                    // pushi 16
17211 	0x76,                          // push0
17212 	0x39, 0x03,                    // pushi 03
17213 	0x76,                          // push0
17214 	0x7c,                          // pushSelf
17215 	0x81, 0x5b,                    // lag global[5b]
17216 	0x4a, 0x0e,                    // send 0e
17217 	0x32, SIG_UINT16(0x0088),      // jmp [end of method]
17218 	0x3c,                          // dup
17219 	0x35, 0x01,                    // ldi 01
17220 	0x1a,                          // eq?
17221 	0x31, 0x28,                    // bnt [check for state 2]
17222 	SIG_END
17223 };
17224 
17225 static const uint16 sq5PatchToolboxFix[] = {
17226 	0x31, 0x41,                    // bnt [check for state 2]
17227 	PATCH_ADDTOOFFSET(+16),        // skip to jmp offset
17228 	0x35, 0x01,                    // ldi 01
17229 	0x65, 0x14,                    // aTop [state]
17230 	0x36, 0x00, 0x00,              // ldi 0000 (waste 3 bytes)
17231 	0x35, 0x00,                    // ldi 00 (waste 2 bytes)
17232 	PATCH_END
17233 };
17234 
17235 // WORKAROUND: Script needed, because of differences in our pathfinding
17236 // algorithm
17237 // After entering the drive bay (room 1000) through the hallway, clicking walk
17238 //  in most places causes ego to automatically turn around and return to the
17239 //  previous room. This is due to differences in our pathfinding algorithm from
17240 //  Sierra's which results in ego first walking backwards into the control area
17241 //  that triggers the script sExitToHall.
17242 //
17243 // We work around this by adjusting ego's initial MoveTo position by a few
17244 //  pixels to one which doesn't cause pathfinding to send ego backwards.
17245 //
17246 // Applies to: PC Floppy
17247 // Responsible method: sEnterFromHall:changeState(0)
17248 // Fixes bug: #7155
17249 static const uint16 sq5SignatureDriveBayPathfindingFix[] = {
17250 	SIG_MAGICDWORD,
17251 	0x39, 0x0e,                     // pushi 0e [ x = 14d ]
17252 	0x39, 0x6e,                     // pushi 6e [ y = 110d ]
17253 	SIG_END
17254 };
17255 
17256 static const uint16 sq5PatchDriveBayPathfindingFix[] = {
17257 	0x39, 0x10,                     // pushi 10 [ x = 16d ]
17258 	0x39, 0x6f,                     // pushi 6f [ y = 111d ]
17259 	PATCH_END
17260 };
17261 
17262 // Sitting in the captain's chair while Droole plays paddle ball randomly locks
17263 //  up the game. Upon sitting, sTakeCommand plays a sound using theMusic3 and
17264 //  waits for it to complete. This is the same object that's used to play the
17265 //  paddle ball sound. If ego sits before a paddle ball sound starts or Droole
17266 //  stops paddling and disposes the sound then sTakeCommand is never cued.
17267 //
17268 // We fix this conflict by using a different Sound object for the chair.
17269 //  theMusic4 is only used once while meeting the crew in sNewCaptain.
17270 //
17271 // Applies to: All versions
17272 // Responsible method: sTakeCommand:changeState
17273 // Fixes bug: #6130
17274 static const uint16 sq5SignatureCaptainChairFix[] = {
17275 	SIG_MAGICDWORD,
17276 	0x76,                           // push0
17277 	0x72, SIG_UINT16(0x0018),       // lofsa theMusic4
17278 	SIG_ADDTOOFFSET(+947),
17279 	0x72, SIG_UINT16(0x02ec),       // lofsa theMusic3
17280 	SIG_ADDTOOFFSET(+26),
17281 	0x72, SIG_UINT16(0x02ec),       // lofsa theMusic3
17282 	SIG_END
17283 };
17284 
17285 static const uint16 sq5PatchCaptainChairFix[] = {
17286 	PATCH_ADDTOOFFSET(+951),
17287 	0x72, PATCH_UINT16(0x0018),     // lofsa theMusic4
17288 	PATCH_ADDTOOFFSET(+26),
17289 	0x72, PATCH_UINT16(0x0018),     // lofsa theMusic4
17290 	PATCH_END
17291 };
17292 
17293 // When using the fruit on WD40 in room 305, she can take off before ego's
17294 //  animation completes and lock up the game. WD40 remains on the log for five
17295 //  seconds but ego's animation runs at the game speed setting and the scripts
17296 //  don't coordinate. At slow speeds, ego's animation can take all five seconds.
17297 //
17298 // We fix this by not allowing WD40 to take off while ego has a script running.
17299 //  To do this we use existing code in sWD40LandOverRog that retries the current
17300 //  state after 10 ticks. This preserves the scene's timing unless WD40 would
17301 //  have gotten stuck, in which case she now waits for sFruitUpWD40 to complete.
17302 //
17303 // Applies to: All versions
17304 // Responsible method: sWD40LandOverRog:changeState
17305 // Fixes bug: #5162
17306 static const uint16 sq5SignatureWd40FruitFix[] = {
17307 	0x6d, 0x14,                     // dpToa state [ state-- ]
17308 	0x35, 0x0a,                     // ldi 0a
17309 	SIG_MAGICDWORD,
17310 	0x65, 0x20,                     // aTop ticks [ ticks = 10 ]
17311 	0x32, SIG_UINT16(0x0106),       // jmp 0106   [ end of method ]
17312 	SIG_ADDTOOFFSET(+57),
17313 	0x31, 0x28,                     // bnt 28 [ state 5 ]
17314 	SIG_ADDTOOFFSET(+7),
17315 	0x31, 0x18,                     // bnt 18
17316 	0x78,                           // push1
17317 	SIG_ADDTOOFFSET(+20),
17318 	0x32, SIG_UINT16(0x00aa),       // jmp 00aa [ end of method ]
17319 	0x35, 0x01,                     // ldi 01
17320 	0x65, 0x1a,                     // aTop cycles
17321 	0x32, SIG_UINT16(0x00a3),       // jmp 00a3 [ end of method ]
17322 	0x3c,                           // dup
17323 	0x35, 0x05,                     // ldi 05
17324 	0x1a,                           // eq?
17325 	0x31, 0x11,                     // bnt 11 [ state 6 ]
17326 	0x39, SIG_SELECTOR8(play),      // pushi play
17327 	0x78,                           // push1
17328 	0x39, 0x4b,                     // pushi 4b
17329 	0x72, SIG_UINT16(0x096e),       // lofsa theMusic3
17330 	0x4a, 0x06,                     // send 06 [ theMusic3 play: 75 ]
17331 	0x35, 0x05,                     // ldi 05
17332 	0x65, 0x1c,                     // aTop seconds [ seconds = 5 ]
17333 	0x32, SIG_UINT16(0x008c),       // jmp 008c [ end of method ]
17334 	0x3c,                           // dup
17335 	0x35, 0x06,                     // ldi 06
17336 	0x1a,                           // eq?
17337 	0x30, SIG_UINT16(0x0053),       // bnt 0053 [ state 7 ]
17338 	SIG_END
17339 };
17340 
17341 static const uint16 sq5PatchWd40FruitFix[] = {
17342 	PATCH_ADDTOOFFSET(+66),
17343 	0x31, 0x22,                     // bnt 22 [ state 5 ]
17344 	PATCH_ADDTOOFFSET(+7),
17345 	0x78,                           // push1
17346 	0x31, 0x16,                     // bnt 16
17347 	PATCH_ADDTOOFFSET(+20),
17348 	0x3a,                           // toss
17349 	0x48,                           // ret
17350 	0x69, 0x1a,                     // sTop cycles [ cycles = 1 ]
17351 	0x3c,                           // dup
17352 	0x35, 0x05,                     // ldi 05
17353 	0x1a,                           // eq?
17354 	0x31, 0x0d,                     // bnt 0d [ state 6 ]
17355 	0x39, PATCH_SELECTOR8(play),    // pushi play
17356 	0x78,                           // push1
17357 	0x39, 0x4b,                     // pushi 4b
17358 	0x72, PATCH_UINT16(0x096e),     // lofsa theMusic3
17359 	0x4a, 0x06,                     // send 06 [ theMusic3 play: 75 ]
17360 	0x69, 0x1c,                     // sTop seconds [ seconds = 5 ]
17361 	0x48,                           // ret
17362 	0x3c,                           // dup
17363 	0x35, 0x06,                     // ldi 06
17364 	0x1a,                           // eq?
17365 	0x31, 0x5e,                     // bnt 5e [ state 7 ]
17366 	0X38, PATCH_SELECTOR16(script), // pushi script
17367 	0x76,                           // push0
17368 	0x81, 0x00,                     // lag 00
17369 	0x4a, 0x04,                     // send 04 [ ego script? ]
17370 	0x2e, PATCH_UINT16(0xff76),     // bt ff76 [ state--, ticks = 10 ]
17371 	PATCH_END
17372 };
17373 
17374 // In the first release of SQ5, when the cloaking device alarm countdown on
17375 //  WD40's ship expires, a script enters an infinite loop and the interpreter
17376 //  stops responding.
17377 //
17378 // We fix this as Sierra did in later versions by adding a call to SQ5:handsOn
17379 //  and removing the call to sCountDown:dispose before going to deathRoom.
17380 //
17381 // Applies to: English PC 1.03
17382 // Responsible method: sCountDown:changeState(3)
17383 // Fixes bug: #11255
17384 static const uint16 sq5SignatureWd40AlarmCountdownFix[] = {
17385 	0x3c,                           // dup
17386 	0x35, 0x03,                     // ldi 03
17387 	0x1a,                           // eq?
17388 	0x31, 0x0b,                     // bnt 0b [ end of method ]
17389 	0x78,                           // push1
17390 	0x39, 0x15,                     // pushi 15
17391 	SIG_MAGICDWORD,
17392 	0x45, 0x09, 0x02,               // callb proc0_9 02 [ go to deathRoom ]
17393 	0x39, SIG_SELECTOR8(dispose),   // pushi dispose
17394 	0x76,                           // push0
17395 	0x54, 0x04,                     // self 04 [ self dispose: ]
17396 	SIG_END
17397 };
17398 
17399 static const uint16 sq5PatchWd40AlarmCountdownFix[] = {
17400 	0x38, PATCH_SELECTOR16(handsOn),// pushi handsOn
17401 	0x76,                           // push0
17402 	0x81, 0x01,                     // lag 01
17403 	0x4a, 0x04,                     // send 04 [ SQ5 handsOn: ]
17404 	0x78,                           // push1
17405 	0x39, 0x15,                     // pushi 15
17406 	0x45, 0x09, 0x02,               // callb proc0_9 02 [ go to deathRoom ]
17407 	0x3a,                           // toss
17408 	0x48,                           // ret
17409 	PATCH_END
17410 };
17411 
17412 // In the transporter room, several scripts attempt to temporarily set ego's
17413 //  speed to 6 but instead change the game speed. This prevents ego's speed from
17414 //  being restored. The user must then do this manually in the control panel.
17415 //  These bugs are due to calling ego:setSpeed instead of ego:cycleSpeed, which
17416 //  we fix. This occurs when randomly beaming in with the funnyBeam script and
17417 //  when talking to Cliffy about Bea before curing her.
17418 //
17419 // Applies to: All versions
17420 // Responsible methods: funnyBeam:changeState, talkAboutBea:changeState
17421 // Fixes bug: #11264
17422 static const uint16 sq5SignatureTransporterRoomSpeedFix[] = {
17423 	0x38, SIG_MAGICDWORD,           // pushi setSpeed
17424 	      SIG_SELECTOR16(setSpeed),
17425 	0x78,                           // push1
17426 	0x39, 0x06,                     // pushi 06
17427 	SIG_END
17428 };
17429 
17430 static const uint16 sq5PatchTransporterRoomSpeedFix[] = {
17431 	0x38, PATCH_SELECTOR16(cycleSpeed), // pushi cycleSpeed
17432 	PATCH_END
17433 };
17434 
17435 //          script, description,                                      signature                             patch
17436 static const SciScriptPatcherEntry sq5Signatures[] = {
17437 	{  true,   200, "captain chair lockup fix",                    1, sq5SignatureCaptainChairFix,          sq5PatchCaptainChairFix },
17438 	{  true,   226, "toolbox fix",                                 1, sq5SignatureToolboxFix,               sq5PatchToolboxFix },
17439 	{  true,   243, "transporter room speed fix",                  3, sq5SignatureTransporterRoomSpeedFix,  sq5PatchTransporterRoomSpeedFix },
17440 	{  true,   305, "wd40 fruit fix",                              1, sq5SignatureWd40FruitFix,             sq5PatchWd40FruitFix },
17441 	{  true,   335, "wd40 alarm countdown fix",                    1, sq5SignatureWd40AlarmCountdownFix,    sq5PatchWd40AlarmCountdownFix },
17442 	{  true,  1000, "drive bay pathfinding fix",                   1, sq5SignatureDriveBayPathfindingFix,   sq5PatchDriveBayPathfindingFix },
17443 	SCI_SIGNATUREENTRY_TERMINATOR
17444 };
17445 
17446 #ifdef ENABLE_SCI32
17447 #pragma mark -
17448 #pragma mark RAMA
17449 
17450 // RAMA has custom video benchmarking code that needs to be disabled; see
17451 // sci2BenchmarkSignature
17452 static const uint16 ramaBenchmarkSignature[] = {
17453 	0x38, SIG_SELECTOR16(view), // pushi view
17454 	SIG_MAGICDWORD,
17455 	0x78,                       // push1
17456 	0x38, SIG_UINT16(0xfdd4),   // pushi 64980
17457 	SIG_END
17458 };
17459 
17460 static const uint16 ramaBenchmarkPatch[] = {
17461 	0x34, PATCH_UINT16(0x2710), // ldi 10000
17462 	0x48,                       // ret
17463 	PATCH_END
17464 };
17465 
17466 // RAMA uses a custom save game format that game scripts read and write
17467 // manually. The save game format serialises object references, which SSCI could
17468 // be done just by writing int16s (since object references were just 16-bit
17469 // indexes), but in ScummVM we have to write the full 32-bit reg_t. We hijack
17470 // kFileIOReadWord/kFileIOWriteWord to do this for us, but we need the game to
17471 // agree to use those kFileIO calls instead of doing raw reads and creating its
17472 // own numbers, as it tries to do here in `SaveManager::readWord`.
17473 static const uint16 ramaSerializeRegTSignature1[] = {
17474 	SIG_MAGICDWORD,
17475 	0x38, SIG_SELECTOR16(newWith), // pushi newWith ($10b)
17476 	0x7a,                          // push2
17477 	0x7a,                          // push2
17478 	0x72, SIG_UINT16(0x0000),      // lofsa ""
17479 	0x36,                          // push
17480 	0x51, 0x0f,                    // class Str
17481 	SIG_END
17482 };
17483 
17484 static const uint16 ramaSerializeRegTPatch1[] = {
17485 	0x38, PATCH_SELECTOR16(readWord),    // pushi readWord
17486 	0x76,                                // push0
17487 	0x62, PATCH_SELECTOR16(saveFilePtr), // pToa saveFilePtr
17488 	0x4a, PATCH_UINT16(0x0004),          // send 4
17489 	0x48,                                // ret
17490 	PATCH_END
17491 };
17492 
17493 // When restoring a NukeTimer client, the game makes a self-call to
17494 // `NukeTimer::getSubscriberObj` from `NukeTimer::serialize`, but forgets to
17495 // pass a required argument. In SSCI this happens to work because the value on
17496 // the stack where the first argument should be is the `getSubscriberObj`
17497 // selector, so it evaluates to true, but currently ScummVM defaults
17498 // uninitialised param reads to 0 so the game was following the wrong path and
17499 // breaking.
17500 // Applies to at least: US English
17501 // Fixes bug: #10263
17502 static const uint16 ramaNukeTimerSignature[] = {
17503 	0x7e, SIG_ADDTOOFFSET(+2),              // line whatever
17504 	SIG_MAGICDWORD,
17505 	0x38, SIG_SELECTOR16(getSubscriberObj), // pushi getSubscriberObj ($3ca)
17506 	0x76,                                   // push0
17507 	0x54, SIG_UINT16(0x0004),               // self 4
17508 	SIG_END
17509 };
17510 
17511 static const uint16 ramaNukeTimerPatch[] = {
17512 	0x38, PATCH_SELECTOR16(getSubscriberObj), // pushi getSubscriberObj ($3ca)
17513 	0x78,                                     // push1
17514 	0x38, PATCH_UINT16(0x0001),               // pushi 1 (wasting bytes)
17515 	0x54, PATCH_UINT16(0x0006),               // self 6
17516 	PATCH_END
17517 };
17518 
17519 // When opening a datacube on the pocket computer, `DocReader::init` will try
17520 // to perform arithmetic on a pointer to `thighComputer::plane` then use the
17521 // resulting value as the priority for the DocReader. This happened to work in
17522 // SSCI because the plane pointer would just be a high numeric value, but
17523 // ScummVM needs an actual number, not a pointer.
17524 // Applies to at least: US English
17525 static const uint16 ramaDocReaderInitSignature[] = {
17526 	0x39, SIG_SELECTOR8(priority), // pushi priority ($1a)
17527 	0x78,                          // push1
17528 	0x39, SIG_SELECTOR8(plane),    // pushi plane ($19)
17529 	0x76,                          // push0
17530 	0x7a,                          // push2
17531 	SIG_MAGICDWORD,
17532 	0x39, 0x2c,                    // pushi 44
17533 	0x76,                          // push0
17534 	0x43, 0x02, SIG_UINT16(0x04),  // callk ScriptID, 4
17535 	SIG_END
17536 };
17537 
17538 static const uint16 ramaDocReaderInitPatch[] = {
17539 	PATCH_ADDTOOFFSET(+3),           // pushi priority, push1
17540 	0x39, PATCH_SELECTOR8(priority), // pushi priority
17541 	PATCH_END
17542 };
17543 
17544 // It is not possible to change the directory for ScummVM save games, so
17545 // disable the "change directory" button in the RAMA save dialog.
17546 static const uint16 ramaChangeDirSignature[] = {
17547 	SIG_MAGICDWORD,
17548 	0x7e, SIG_UINT16(0x0064),   // line 100
17549 	0x39, SIG_SELECTOR8(state), // pushi state ($1d)
17550 	0x78,                       // push1
17551 	0x39, 0x03,                 // pushi 3
17552 	0x72, SIG_ADDTOOFFSET(+2),  // lofsa changeDirI
17553 	0x4a, SIG_UINT16(0x000e),   // send 14
17554 	SIG_END
17555 };
17556 
17557 static const uint16 ramaChangeDirPatch[] = {
17558 	PATCH_ADDTOOFFSET(+6),    // line 100, pushi state, push1
17559 	0x39, 0x00,               // pushi 0
17560 	PATCH_END
17561 };
17562 
17563 static const SciScriptPatcherEntry ramaSignatures[] = {
17564 	{  true,    55, "fix bad DocReader::init priority calculation",   1, ramaDocReaderInitSignature,      ramaDocReaderInitPatch },
17565 	{  true,    85, "fix SaveManager to use normal readWord calls",   1, ramaSerializeRegTSignature1,     ramaSerializeRegTPatch1 },
17566 	{  true,   201, "fix crash restoring save games using NukeTimer", 1, ramaNukeTimerSignature,          ramaNukeTimerPatch },
17567 	{  true, 64908, "disable video benchmarking",                     1, ramaBenchmarkSignature,          ramaBenchmarkPatch },
17568 	{  true, 64990, "disable change directory button",                1, ramaChangeDirSignature,          ramaChangeDirPatch },
17569 	SCI_SIGNATUREENTRY_TERMINATOR
17570 };
17571 
17572 #pragma mark -
17573 #pragma mark Shivers
17574 
17575 // In room 35170, there is a CCTV control station with a joystick that must be
17576 // clicked and dragged to pan the camera. In order to enable dragging, on
17577 // mousedown, the `vJoystick::handleEvent` method calls `vJoystick::doVerb(1)`,
17578 // which enables the drag functionality of the joystick. However,
17579 // `vJoystick::handleEvent` then makes a super call to
17580 // `ShiversProp::handleEvent`, which calls `vJoystick::doVerb()`. This second
17581 // call, which fails to pass an argument, causes an uninitialized read off the
17582 // stack for the first parameter. In SSCI, this happens to work because the
17583 // uninitialized value on the stack happens to be 1. Disabling the super call
17584 // avoids the bad doVerb call without any apparent ill effect.
17585 // The same problem exists when trying to drag the volume & brightness sliders
17586 // in the main menu. These controls are also fixed by this patch.
17587 // Applies to at least: US English
17588 static const uint16 shiversEventSuperCallSignature[] = {
17589 	SIG_MAGICDWORD,
17590 	0x38, SIG_SELECTOR16(handleEvent), // pushi handleEvent
17591 	0x78,                              // push1
17592 	0x8f, 0x01,                        // lsp param[1]
17593 	0x59, 0x02,                        // &rest 2
17594 	0x57, 0x7f, SIG_UINT16(0x0006),    // super ShiversProp[7f], 6
17595 	SIG_END
17596 };
17597 
17598 static const uint16 shiversEventSuperCallPatch[] = {
17599 	0x48, // ret
17600 	PATCH_END
17601 };
17602 
17603 // When the Ixupi is present in the Gods and Items intro room, the game tries to
17604 // play a sound using the play selector, but its arguments are only appropriate
17605 // for the fade selector.
17606 // If the badly constructed sound object from this call ends up receiving a
17607 // signal at any time in the future, the game will try to send to a number and
17608 // crash (because the third argument to play is supposed to be a client object,
17609 // but here it is a number instead). Other rooms make this same call with the
17610 // correct fade selector, so fix the selector here to match.
17611 // Applies to at least: English CD
17612 static const uint16 shiversGodsIxupiPlaySoundSignature[] = {
17613 	SIG_MAGICDWORD,
17614 	0x39, SIG_SELECTOR8(play), // pushi play ($33)
17615 	0x38, SIG_UINT16(0x0006),  // pushi 6
17616 	SIG_END
17617 };
17618 
17619 static const uint16 shiversGodsIxupiPlaySoundPatch[] = {
17620 	0x38, PATCH_SELECTOR16(fade), // pushi fade ($f3)
17621 	0x39, 0x06,                   // pushi 6
17622 	PATCH_END
17623 };
17624 
17625 //          script, description,                                      signature                           patch
17626 static const SciScriptPatcherEntry shiversSignatures[] = {
17627 	{  true,   990, "fix volume & brightness sliders",             2, shiversEventSuperCallSignature,     shiversEventSuperCallPatch },
17628 	{  true, 23090, "fix bad Ixupi sound call",                    1, shiversGodsIxupiPlaySoundSignature, shiversGodsIxupiPlaySoundPatch },
17629 	{  true, 35170, "fix CCTV joystick interaction",               1, shiversEventSuperCallSignature,     shiversEventSuperCallPatch },
17630 	{  true, 64908, "disable video benchmarking",                  1, sci2BenchmarkSignature,             sci2BenchmarkPatch },
17631 	SCI_SIGNATUREENTRY_TERMINATOR
17632 };
17633 
17634 #pragma mark -
17635 #pragma mark Space Quest 6
17636 
17637 // After the explosion in the Quarters of Deepship 86, the game tries to perform
17638 // a dramatic long fade, but does this with an unreasonably large number of
17639 // divisions which takes tens of seconds to finish (because transitions are not
17640 // CPU-dependent in ScummVM).
17641 // Fixes bug: #9590
17642 static const uint16 sq6SlowTransitionSignature1[] = {
17643 	SIG_MAGICDWORD,
17644 	0x38, SIG_UINT16(0x0578),           // pushi $578
17645 	0x51, 0x33,                         // class Styler
17646 	SIG_END
17647 };
17648 
17649 static const uint16 sq6SlowTransitionPatch1[] = {
17650 	0x38, PATCH_UINT16(0x01f4),         // pushi 500
17651 	PATCH_END
17652 };
17653 
17654 // For whatever reason, SQ6 sets the default number of transition divisions to
17655 // be a much larger value at startup (200 vs 30) if it thinks it is running in
17656 // Windows. Room 410 (eulogy room) also unconditionally resets divisions to the
17657 // larger value.
17658 // Fixes bug: #9590
17659 static const uint16 sq6SlowTransitionSignature2[] = {
17660 	SIG_MAGICDWORD,
17661 	0x38, SIG_UINT16(0x00c8),           // pushi $c8
17662 	0x51, 0x33,                         // class Styler
17663 	SIG_END
17664 };
17665 
17666 static const uint16 sq6SlowTransitionPatch2[] = {
17667 	0x38, PATCH_UINT16(0x001e),         // pushi 30
17668 	PATCH_END
17669 };
17670 
17671 // SQ6 has custom video benchmarking code that needs to be disabled; see
17672 // sci2BenchmarkSignature. (The sci2BenchmarkPatch is suitable for use with
17673 // SQ6 as well.)
17674 static const uint16 sq6BenchmarkSignature[] = {
17675 	SIG_MAGICDWORD,
17676 	0x38, SIG_SELECTOR16(init),  // pushi init
17677 	0x76,                        // push0
17678 	0x7e, SIG_ADDTOOFFSET(+2),   // line
17679 	0x38, SIG_SELECTOR16(posn),  // pushi posn
17680 	SIG_END
17681 };
17682 
17683 // SQ6 advertises a maximum score of 500 but the game is missing two points.
17684 //  The "Official Player's Guide" authorized by Sierra includes a point list
17685 //  that adds up to 500. It claims that three points should be awarded instead
17686 //  of two when untangling the hose and attaching the staple and celery.
17687 //
17688 // Since an official point list exists that adds up to the 500 points that the
17689 //  game advertises, we add the two missing points to the inventory actions.
17690 //  Two versions of this patch are necessary because the English PC versions
17691 //  were compiled with line number debugging instructions and the subsequent
17692 //  French, German, and Mac versions weren't.
17693 //
17694 // Applies to: All versions
17695 // Responsible methods: Hookah_Hose:cue, Staple:doVerb, Celery:doVerb
17696 // Fixes bug: #11275
17697 static const uint16 sq6MissingPointsSignature[] = {
17698 	0x7e, SIG_ADDTOOFFSET(+2),      // line
17699 	SIG_MAGICDWORD,
17700 	0x39, SIG_SELECTOR8(points),    // pushi points
17701 	0x78,                           // push1
17702 	0x7a,                           // push2
17703 	SIG_END
17704 };
17705 
17706 static const uint16 sq6MissingPointsPatch[] = {
17707 	0x39, PATCH_SELECTOR8(points),  // pushi points
17708 	0x39, 0x01,                     // pushi 01
17709 	0x38, PATCH_UINT16(0x0003),     // pushi 0003
17710 	PATCH_END
17711 };
17712 
17713 // French, German, and Mac versions don't include line number instructions
17714 //  and require specific patches.
17715 static const uint16 sq6StapleCeleryPointSignature[] = {
17716 	0x72, SIG_ADDTOOFFSET(+2),      // lofsa Grappling_Hook
17717 	0x36,                           // push
17718 	0x81, 0x09,                     // lag 09
17719 	0x4a, SIG_UINT16(0x0006),       // send 06
17720 	0x39, SIG_SELECTOR8(points),    // pushi points
17721 	SIG_MAGICDWORD,
17722 	0x78,                           // push1
17723 	0x7a,                           // push2
17724 	0x38, SIG_SELECTOR16(setCursor),// pushi setCursor
17725 	SIG_END
17726 };
17727 
17728 static const uint16 sq6StapleCeleryPointPatch[] = {
17729 	0x74, PATCH_ADDTOOFFSET(+2),    // lofss Grappling_Hook
17730 	0x81, 0x09,                     // lag 09
17731 	0x4a, PATCH_UINT16(0x0006),     // send 06
17732 	0x39, PATCH_SELECTOR8(points),  // pushi points
17733 	0x78,                           // push1
17734 	0x39, 0x03,                     // pushi 03
17735 	PATCH_END
17736 };
17737 
17738 static const uint16 sq6HookahHosePointSignature[] = {
17739 	0x30, SIG_UINT16(0x0054),       // bnt 0054
17740 	SIG_ADDTOOFFSET(+75),
17741 	0x72, SIG_ADDTOOFFSET(+2),      // lofsa Hookah_Connected
17742 	0x36,                           // push
17743 	0x81, 0x09,                     // lag 09
17744 	0x4a, SIG_UINT16(0x000c),       // send 0c
17745 	0x39, SIG_SELECTOR8(points),    // pushi points
17746 	SIG_MAGICDWORD,
17747 	0x78,                           // push1
17748 	0x7a,                           // push2
17749 	0x81, 0x01,                     // lag 01
17750 	SIG_END
17751 };
17752 
17753 static const uint16 sq6HookahHosePointPatch[] = {
17754 	0x30, PATCH_UINT16(0x0053),     // bnt 0053
17755 	PATCH_ADDTOOFFSET(+75),
17756 	0x74, PATCH_ADDTOOFFSET(+2),    // lofss Hookah_Connected
17757 	0x81, 0x09,                     // lag 09
17758 	0x4a, PATCH_UINT16(0x000c),     // send 0c
17759 	0x39, PATCH_SELECTOR8(points),  // pushi points
17760 	0x78,                           // push1
17761 	0x39, 0x03,                     // pushi 03
17762 	PATCH_END
17763 };
17764 
17765 // Filling the helmet in room 690 awards three points, but this happens every
17766 //  time the player returns to fill the helmet with no limit. We fix this by
17767 //  associating the points with flag 297 so that they're only awarded once, as
17768 //  Sierra did in the French, German, and Mac versions.
17769 //
17770 // Applies to: English PC
17771 // Responsible method: sGetStuff:changeState(8)
17772 static const uint16 sq6DuplicatePointsSignature[] = {
17773 	SIG_MAGICDWORD,
17774 	0x39, SIG_SELECTOR8(points),        // pushi points
17775 	0x78,                               // push1
17776 	0x39, 0x03,                         // pushi 03
17777 	0x81, 0x01,                         // lag 01
17778 	0x4a, SIG_UINT16(0x0006),           // send 06 [ SQ6 points: 3 ]
17779 	0x7e,                               // line
17780 	SIG_END
17781 };
17782 
17783 static const uint16 sq6DuplicatePointsPatch[] = {
17784 	PATCH_ADDTOOFFSET(+2),
17785 	0x7a,                               // push2
17786 	PATCH_ADDTOOFFSET(+4),
17787 	0x38, PATCH_UINT16(0x0129),         // pushi 0129
17788 	0x4a, PATCH_UINT16(0x0008),         // send 08 [ SQ6 points: 3 297 ]
17789 	PATCH_END
17790 };
17791 
17792 // When attempting to restore a game that was saved with a different version of
17793 //  the interpreter, SQ6 displays a standard error dialog but then crashes due
17794 //  to a script bug. This also occurs in the original. SQ6 has custom code in
17795 //  Game:restore to set and restore the cursor differently depending on the
17796 //  current room. If in room 100, the main menu, then it attempts to restore the
17797 //  cursor by sending a message to an uninitialized variable.
17798 //
17799 // We fix this by not restoring the cursor when temp1 is zero. This patch relies
17800 //  on an existing uninitialized read workaround for Game:restore. The dialog
17801 //  still renders poorly, as it does in the original, but that's unrelated.
17802 //
17803 // Applies to: All versions
17804 // Responsible method: Game:restore
17805 // Fixes bug: #9702
17806 static const uint16 sq6RestoreErrorDialogSignature[] = {
17807 	0x38, SIG_SELECTOR16(setCursor),    // pushi setCursor
17808 	SIG_MAGICDWORD,
17809 	0x7a,                               // push2
17810 	0x8d, 0x01,                         // lst 01
17811 	0x76,                               // push0
17812 	0x43, 0x54, SIG_UINT16(0x0000),     // callk HaveMouse 00
17813 	0x36,                               // push
17814 	0x54, SIG_UINT16(0x0008),           // self 0008 [ self setCursor: temp1 kHaveMouse ]
17815 	SIG_END
17816 };
17817 
17818 static const uint16 sq6RestoreErrorDialogPatch[] = {
17819 	0x85, 0x01,                         // lat 01
17820 	0x30, PATCH_UINT16(0x000a),         // bnt 000a [ skip setCursor if temp1 == 0 ]
17821 	0x38, PATCH_SELECTOR16(setCursor),  // pushi setCursor
17822 	0x7a,                               // push2
17823 	0x8d, 0x01,                         // lst 01
17824 	0x78,                               // push1
17825 	PATCH_END
17826 };
17827 
17828 //          script, description,                                      signature                        patch
17829 static const SciScriptPatcherEntry sq6Signatures[] = {
17830 	{  true,     0, "fix slow transitions",                        1, sq6SlowTransitionSignature2,     sq6SlowTransitionPatch2 },
17831 	{  true,    15, "fix english pc missing points",               3, sq6MissingPointsSignature,       sq6MissingPointsPatch },
17832 	{  true,    15, "fix staple/celery missing point",             2, sq6StapleCeleryPointSignature,   sq6StapleCeleryPointPatch },
17833 	{  true,    15, "fix hookah hose missing point",               1, sq6HookahHosePointSignature,     sq6HookahHosePointPatch },
17834 	{  true,    15, "fix invalid array construction",              1, sci21IntArraySignature,          sci21IntArrayPatch },
17835 	{  true,    22, "fix invalid array construction",              1, sci21IntArraySignature,          sci21IntArrayPatch },
17836 	{  true,   410, "fix slow transitions",                        1, sq6SlowTransitionSignature2,     sq6SlowTransitionPatch2 },
17837 	{  true,   460, "fix invalid array construction",              1, sci21IntArraySignature,          sci21IntArrayPatch },
17838 	{  true,   500, "fix slow transitions",                        1, sq6SlowTransitionSignature1,     sq6SlowTransitionPatch1 },
17839 	{  true,   510, "fix invalid array construction",              1, sci21IntArraySignature,          sci21IntArrayPatch },
17840 	{  true,   690, "fix duplicate points",                        1, sq6DuplicatePointsSignature,     sq6DuplicatePointsPatch },
17841 	{  true, 64908, "disable video benchmarking",                  1, sq6BenchmarkSignature,           sci2BenchmarkPatch },
17842 	{  true, 64990, "increase number of save games (1/2)",         1, sci2NumSavesSignature1,          sci2NumSavesPatch1 },
17843 	{  true, 64990, "increase number of save games (2/2)",         1, sci2NumSavesSignature2,          sci2NumSavesPatch2 },
17844 	{  true, 64990, "disable change directory button",             1, sci2ChangeDirSignature,          sci2ChangeDirPatch },
17845 	{  true, 64994, "fix restore-error dialog",                    1, sq6RestoreErrorDialogSignature,  sq6RestoreErrorDialogPatch },
17846 	SCI_SIGNATUREENTRY_TERMINATOR
17847 };
17848 
17849 #pragma mark -
17850 #pragma mark Torins Passage
17851 
17852 // A subroutine that gets called by 'Torin::init' unconditionally resets the
17853 // audio volumes to defaults, but the game should always use the volume stored
17854 // in ScummVM. This patch is basically identical to the patch for LSL7, except
17855 // that they left line numbers in the LSL7 scripts and changed the music volume.
17856 // Applies to at least: English CD
17857 // Fixes bug: #9700
17858 static const uint16 torinVolumeResetSignature1[] = {
17859 	SIG_MAGICDWORD,
17860 	0x35, 0x28, // ldi $28
17861 	0xa1, 0xe3, // sag global[$e3] (music volume)
17862 	0x35, 0x3c, // ldi $3c
17863 	0xa1, 0xe4, // sag global[$e4] (sfx volume)
17864 	0x35, 0x64, // ldi $64
17865 	0xa1, 0xe5, // sag global[$e5] (speech volume)
17866 	SIG_END
17867 };
17868 
17869 static const uint16 torinVolumeResetPatch1[] = {
17870 	0x33, 0x0a, // jmp [past volume resets]
17871 	PATCH_END
17872 };
17873 
17874 // A subroutine that gets called by 'Torin::init' unconditionally resets the
17875 // audio volumes to values stored in torin.prf, but the game should always use
17876 // the volume stored in ScummVM. This patch is basically identical to the patch
17877 // for LSL7, except that they left line numbers in the LSL7 scripts.
17878 // Applies to at least: English CD
17879 // Fixes bug: #9700
17880 static const uint16 torinVolumeResetSignature2[] = {
17881 	SIG_MAGICDWORD,
17882 	0x38, SIG_SELECTOR16(readWord),     // pushi readWord ($020b)
17883 	0x76,                               // push0
17884 	SIG_ADDTOOFFSET(+6),                // ...
17885 	0xa1, 0xe3,                         // sag global[$e3] (music volume)
17886 	SIG_ADDTOOFFSET(+10),               // ...
17887 	0xa1, 0xe4,                         // sag global[$e4] (sfx volume)
17888 	SIG_ADDTOOFFSET(+10),               // ...
17889 	0xa1, 0xe5,                         // sag global[$e5] (speech volume)
17890 	SIG_END
17891 };
17892 
17893 static const uint16 torinVolumeResetPatch2[] = {
17894 	PATCH_ADDTOOFFSET(+10),
17895 	0x18, 0x18,                         // (waste bytes)
17896 	PATCH_ADDTOOFFSET(+10),             // ...
17897 	0x18, 0x18,                         // (waste bytes)
17898 	PATCH_ADDTOOFFSET(+10),             // ...
17899 	0x18, 0x18,                         // (waste bytes)
17900 	PATCH_END
17901 };
17902 
17903 // In Escarpa, it is possible for Boogle to be left outside of Torin's bag
17904 // when fast-forwarding through the exit animation of the seraglio. If this
17905 // happens, when the player goes from the seraglio to the dragon's cave and
17906 // then tries to worm Boogle to the left side of the cave, the game will hang
17907 // because Boogle is on the wrong side of the navigable area barrier and cannot
17908 // move through it to continue the cutscene. This patch fixes the fast-forward
17909 // code 'soBoogleBackUp::ff' in the seraglio so that Boogle's in-the-bag flag
17910 // is set when fast-forwarding.
17911 // Applies to at least: English CD, Spanish CD
17912 // Fixes bug: #9836
17913 static const uint16 torinSeraglioBoogleFlagSignature[] = {
17914 	0x35, 0x00,                 // ldi 0
17915 	SIG_MAGICDWORD,
17916 	0xa3, 0x00,                 // sal local[0]
17917 	0x38, SIG_SELECTOR16(test), // pushi test
17918 	SIG_ADDTOOFFSET(+0x5a),     // all the rest of the method
17919 	// CHECKME: Spanish version seems to have a total of 0x5d bytes from this point to the ret
17920 	// FIXME: Check for end of method (e.g. ret) and add different signatures in case localized versions are different
17921 	SIG_END
17922 };
17923 
17924 static const uint16 torinSeraglioBoogleFlagPatch[] = {
17925 	// @1e5f
17926 	// ldi 0, sal local[0] removed from here (+4 bytes)
17927 
17928 	// @1e5f (+4 bytes)
17929 	// local[0] = /* oFlags */ ScriptID(64017, 0);
17930 	0x7a,                               // push2
17931 	0x38, PATCH_UINT16(0xfa11),         // pushi 64017
17932 	0x76,                               // push0
17933 	0x43, 0x02, PATCH_UINT16(0x0004),   // callk ScriptID[2], 4
17934 	0xa3, 0x00,                         // sal local[0] (-2 bytes)
17935 
17936 	// @1e6a (+2 bytes)
17937 	// acc = local[0].test(94);
17938 	0x38, PATCH_SELECTOR16(test),       // pushi test
17939 	0x78,                               // push1
17940 	0x39, 0x5e,                         // pushi 94
17941 	0x4a, PATCH_UINT16(0x0006),         // send 6
17942 
17943 	// @1e73 (+2 bytes)
17944 	// if (!acc) goto elseCase;
17945 	0x30, PATCH_UINT16(0x0034),         // bnt 0x31 + 3
17946 
17947 	// @1e76 (+2 bytes)
17948 	// global[0].get(ScriptID(64001, 0).get(20));
17949 	0x38, PATCH_SELECTOR16(get),        // pushi get
17950 	0x78,                               // push1
17951 	0x38, PATCH_SELECTOR16(get),        // pushi get
17952 	0x78,                               // push1
17953 	0x39, 0x14,                         // pushi 20
17954 	0x7a,                               // push2
17955 	0x38, PATCH_UINT16(0xfa01),         // pushi 64001
17956 	0x76,                               // push0
17957 	0x43, 0x02, PATCH_UINT16(0x0004),   // callk ScriptID[2], 4
17958 	0x4a, PATCH_UINT16(0x0006),         // send 6
17959 	0x36,                               // push
17960 	0x81, 0x00,                         // lag global[0] (ego)
17961 	0x4a, PATCH_UINT16(0x0006),         // send 6
17962 
17963 	// @1e92 (+2 bytes)
17964 	// local[0].set(52);
17965 	0x38, PATCH_SELECTOR16(set),        // pushi set
17966 	0x78,                               // push1
17967 	0x39, 0x34,                         // pushi 52
17968 	0x83, 0x00,                         // lal local[0] (+7 byte)
17969 	0x4a, PATCH_UINT16(0x0006),         // send 6
17970 
17971 	// @1e9d (+9 bytes)
17972 	// goto endOfBranch;
17973 	0x33, 0x0b,                         // jmp [to end of conditional branch] (+1 byte)
17974 
17975 	// @1e9f (+10 bytes)
17976 	// elseCase: local[0].clear(97);
17977 	0x38, PATCH_SELECTOR16(clear),      // pushi clear
17978 	0x78,                               // push1
17979 	0x39, 0x61,                         // pushi 97
17980 	0x83, 0x00,                         // lal local[0] (+7 bytes)
17981 	0x4a, PATCH_UINT16(0x0006),         // send 6
17982 
17983 	// @1eaa (+17 bytes)
17984 	// endOfBranch: local[0].set(232);
17985 	0x38, PATCH_SELECTOR16(set),        // pushi set (-3 bytes)
17986 	0x78,                               // push1 (-1 byte)
17987 	0x38, PATCH_UINT16(0x00e8),         // pushi 232 (Boogle-in-bag flag) (-3 bytes)
17988 	0x83, 0x00,                         // lal local[0] (-2 bytes)
17989 	0x4a, PATCH_UINT16(0x0006),         // send 6 (-3 bytes)
17990 
17991 	// @1eb6 (+5 bytes)
17992 	// local[0] = 0; self.dispose();
17993 	0x38, PATCH_SELECTOR16(dispose),    // pushi dispose
17994 	0x76,                               // push0
17995 	0x3c,                               // dup (-1 byte)
17996 	0xab, 0x00,                         // ssl local[0] (-2 bytes)
17997 	0x54, PATCH_UINT16(0x0004),         // self 4
17998 	0x48,                               // ret
17999 
18000 	// @1ec1 (+2 bytes)
18001 	PATCH_END
18002 };
18003 
18004 // At least some French PointSoft releases of Torin's Passage managed to get
18005 // released with script 20700 from the official Sierra TORINPAT patch and
18006 // *unpatched* heap 20700. Worse, the selector table is not the same as the one
18007 // in the US release, so it is not possible to just apply TORINPAT to the game
18008 // (it will just explode later when mismatched selectors are used). So, here we
18009 // are hot-patching all of the wrong offsets in the original heap to match the
18010 // patched script.
18011 // Applies to at least: French PointSoft CD release
18012 static const uint16 torinPointSoft20700HeapSignature[] = {
18013 	0xe1, 0x15, 0x23, 0x16, // end of patched 20700.SCR (so we don't
18014 							// accidentally patch the heap when it is correctly
18015 							// matched with an unpatched script)
18016 	SIG_ADDTOOFFSET(1),     // padding byte added by Script::load
18017 	SIG_ADDTOOFFSET(0x1d2), // first bad offset in the heap is at 0x1d2
18018 	SIG_MAGICDWORD,
18019 	SIG_UINT16(0xd8),
18020 	SIG_UINT16(0xd8),
18021 	SIG_ADDTOOFFSET(0x200 - 0x1d2 - 4), // second bad offset, etc.
18022 	SIG_UINT16(0xde),
18023 	SIG_UINT16(0xde),
18024 	SIG_ADDTOOFFSET(0x280 - 0x200 - 4),
18025 	SIG_UINT16(0xe0),
18026 	SIG_UINT16(0xe0),
18027 	SIG_ADDTOOFFSET(0x300 - 0x280 - 4),
18028 	SIG_UINT16(0xe2),
18029 	SIG_UINT16(0xe2),
18030 	SIG_ADDTOOFFSET(0x374 - 0x300 - 4),
18031 	SIG_UINT16(0xe4),
18032 	SIG_UINT16(0xe4),
18033 	SIG_ADDTOOFFSET(0x3ce - 0x374 - 4),
18034 	SIG_UINT16(0xee),
18035 	SIG_UINT16(0xee),
18036 	SIG_ADDTOOFFSET(0x44e - 0x3ce - 4),
18037 	SIG_UINT16(0xf0),
18038 	SIG_UINT16(0xf0),
18039 	SIG_ADDTOOFFSET(0x482 - 0x44e - 4),
18040 	SIG_UINT16(0xf6),
18041 	SIG_UINT16(0xf6),
18042 	SIG_ADDTOOFFSET(0x4b6 - 0x482 - 4),
18043 	SIG_UINT16(0xfc),
18044 	SIG_UINT16(0xfc),
18045 	SIG_ADDTOOFFSET(0x4ea - 0x4b6 - 4),
18046 	SIG_UINT16(0x106),
18047 	SIG_UINT16(0x106),
18048 	SIG_ADDTOOFFSET(0x51e - 0x4ea - 4),
18049 	SIG_UINT16(0x110),
18050 	SIG_UINT16(0x110),
18051 	SIG_ADDTOOFFSET(0x55c - 0x51e - 4),
18052 	SIG_UINT16(0x116),
18053 	SIG_UINT16(0x116),
18054 	SIG_ADDTOOFFSET(0x5a2 - 0x55c - 4),
18055 	SIG_UINT16(0x118),
18056 	SIG_UINT16(0x118),
18057 	SIG_END
18058 };
18059 
18060 static const uint16 torinPointSoft20700HeapPatch[] = {
18061 	PATCH_ADDTOOFFSET(4),      // end of patched 20700.SCR
18062 	PATCH_ADDTOOFFSET(1),      // padding byte
18063 	PATCH_ADDTOOFFSET(0x1d2),  // first bad offset
18064 	PATCH_UINT16(0xdc),
18065 	PATCH_UINT16(0xdc),
18066 	PATCH_ADDTOOFFSET(0x200 - 0x1d2 - 4), // second bad offset, etc.
18067 	PATCH_UINT16(0xe6),
18068 	PATCH_UINT16(0xe6),
18069 	PATCH_ADDTOOFFSET(0x280 - 0x200 - 4),
18070 	PATCH_UINT16(0xe8),
18071 	PATCH_UINT16(0xe8),
18072 	PATCH_ADDTOOFFSET(0x300 - 0x280 - 4),
18073 	PATCH_UINT16(0xea),
18074 	PATCH_UINT16(0xea),
18075 	PATCH_ADDTOOFFSET(0x374 - 0x300 - 4),
18076 	PATCH_UINT16(0xec),
18077 	PATCH_UINT16(0xec),
18078 	PATCH_ADDTOOFFSET(0x3ce - 0x374 - 4),
18079 	PATCH_UINT16(0xf6),
18080 	PATCH_UINT16(0xf6),
18081 	PATCH_ADDTOOFFSET(0x44e - 0x3ce - 4),
18082 	PATCH_UINT16(0xf8),
18083 	PATCH_UINT16(0xf8),
18084 	PATCH_ADDTOOFFSET(0x482 - 0x44e - 4),
18085 	PATCH_UINT16(0xfe),
18086 	PATCH_UINT16(0xfe),
18087 	PATCH_ADDTOOFFSET(0x4b6 - 0x482 - 4),
18088 	PATCH_UINT16(0x104),
18089 	PATCH_UINT16(0x104),
18090 	PATCH_ADDTOOFFSET(0x4ea - 0x4b6 - 4),
18091 	PATCH_UINT16(0x10e),
18092 	PATCH_UINT16(0x10e),
18093 	PATCH_ADDTOOFFSET(0x51e - 0x4ea - 4),
18094 	PATCH_UINT16(0x118),
18095 	PATCH_UINT16(0x118),
18096 	PATCH_ADDTOOFFSET(0x55c - 0x51e - 4),
18097 	PATCH_UINT16(0x11e),
18098 	PATCH_UINT16(0x11e),
18099 	PATCH_ADDTOOFFSET(0x5a2 - 0x55c - 4),
18100 	PATCH_UINT16(0x120),
18101 	PATCH_UINT16(0x120),
18102 	PATCH_END
18103 };
18104 
18105 //          script, description,                                      signature                         patch
18106 static const SciScriptPatcherEntry torinSignatures[] = {
18107 	{  true, 20600, "fix wrong boogle bag flag on fast-forward",   1, torinSeraglioBoogleFlagSignature,  torinSeraglioBoogleFlagPatch },
18108 	{  true, 20700, "fix bad heap in PointSoft release",           1, torinPointSoft20700HeapSignature,  torinPointSoft20700HeapPatch },
18109 	{  true, 64000, "disable volume reset on startup (1/2)",       1, torinVolumeResetSignature1,        torinVolumeResetPatch1 },
18110 	{  true, 64000, "disable volume reset on startup (2/2)",       1, torinVolumeResetSignature2,        torinVolumeResetPatch2 },
18111 	{  true, 64866, "increase number of save games",               1, torinLarry7NumSavesSignature,      torinLarry7NumSavesPatch },
18112 	SCI_SIGNATUREENTRY_TERMINATOR
18113 };
18114 
18115 #endif
18116 
18117 // =================================================================================
18118 
ScriptPatcher()18119 ScriptPatcher::ScriptPatcher() {
18120 	int selectorCount = ARRAYSIZE(selectorNameTable);
18121 	int selectorNr;
18122 
18123 	// Allocate table for selector-IDs and initialize that table as well
18124 	_selectorIdTable = new Selector[ selectorCount ];
18125 	for (selectorNr = 0; selectorNr < selectorCount; selectorNr++)
18126 		_selectorIdTable[selectorNr] = -1;
18127 
18128 	_runtimeTable = NULL;
18129 	_isMacSci11 = false;
18130 }
18131 
~ScriptPatcher()18132 ScriptPatcher::~ScriptPatcher() {
18133 	delete[] _runtimeTable;
18134 	delete[] _selectorIdTable;
18135 }
18136 
18137 // will actually patch previously found signature area
applyPatch(const SciScriptPatcherEntry * patchEntry,SciSpan<byte> scriptData,int32 signatureOffset)18138 void ScriptPatcher::applyPatch(const SciScriptPatcherEntry *patchEntry, SciSpan<byte> scriptData, int32 signatureOffset) {
18139 	const uint16 *patchData = patchEntry->patchData;
18140 	byte orgData[PATCH_VALUELIMIT];
18141 	int32 offset = signatureOffset;
18142 	uint16 patchWord = *patchEntry->patchData;
18143 	uint16 patchSelector = 0;
18144 
18145 	// Copy over original bytes from script
18146 	uint32 orgDataSize = scriptData.size() - offset;
18147 	if (orgDataSize > PATCH_VALUELIMIT)
18148 		orgDataSize = PATCH_VALUELIMIT;
18149 	scriptData.subspan(offset, orgDataSize).unsafeCopyDataTo(orgData);
18150 
18151 	while (patchWord != PATCH_END) {
18152 		uint16 patchCommand = patchWord & PATCH_COMMANDMASK;
18153 		uint16 patchValue = patchWord & PATCH_VALUEMASK;
18154 		switch (patchCommand) {
18155 		case PATCH_CODE_ADDTOOFFSET: {
18156 			// add value to offset
18157 			offset += patchValue;
18158 			break;
18159 		}
18160 		case PATCH_CODE_GETORIGINALBYTE: {
18161 			// get original byte from script and adjust it
18162 			if (patchValue >= orgDataSize)
18163 				error("Script-Patcher: can not get requested original byte from script");
18164 			byte orgByte = orgData[patchValue];
18165 			int16 adjustValue;
18166 			patchData++; adjustValue = (int16)(*patchData);
18167 			scriptData[offset] = orgByte + adjustValue;
18168 			offset++;
18169 			break;
18170 		}
18171 		case PATCH_CODE_GETORIGINALUINT16: {
18172 			// get original byte from script and adjust it
18173 			if ((patchValue >= orgDataSize) || (((uint32)patchValue + 1) >= orgDataSize))
18174 				error("Script-Patcher: can not get requested original uint16 from script");
18175 			uint16 orgUINT16;
18176 			int16 adjustValue;
18177 
18178 			if (!_isMacSci11) {
18179 				orgUINT16 = orgData[patchValue] | (orgData[patchValue + 1] << 8);
18180 			} else {
18181 				orgUINT16 = orgData[patchValue + 1] | (orgData[patchValue] << 8);
18182 			}
18183 			patchData++; adjustValue = (int16)(*patchData);
18184 			orgUINT16 += adjustValue;
18185 			if (!_isMacSci11) {
18186 				scriptData[offset] = orgUINT16 & 0xFF;
18187 				scriptData[offset + 1] = orgUINT16 >> 8;
18188 			} else {
18189 				scriptData[offset] = orgUINT16 >> 8;
18190 				scriptData[offset + 1] = orgUINT16 & 0xFF;
18191 			}
18192 			offset += 2;
18193 			break;
18194 		}
18195 		case PATCH_CODE_UINT16:
18196 		case PATCH_CODE_SELECTOR16: {
18197 			byte byte1;
18198 			byte byte2;
18199 
18200 			switch (patchCommand) {
18201 			case PATCH_CODE_UINT16: {
18202 				byte1 = patchValue & PATCH_BYTEMASK;
18203 				patchData++; patchWord = *patchData;
18204 				if (patchWord & PATCH_COMMANDMASK)
18205 					error("Script-Patcher: Patch inconsistent");
18206 				byte2 = patchWord & PATCH_BYTEMASK;
18207 				break;
18208 			}
18209 			case PATCH_CODE_SELECTOR16: {
18210 				patchSelector = _selectorIdTable[patchValue];
18211 				byte1 = patchSelector & 0xFF;
18212 				byte2 = patchSelector >> 8;
18213 				break;
18214 			}
18215 			default:
18216 				byte1 = 0; byte2 = 0;
18217 			}
18218 			if (!_isMacSci11) {
18219 				scriptData[offset++] = byte1;
18220 				scriptData[offset++] = byte2;
18221 			} else {
18222 				// SCI1.1+ on macintosh had uint16s in script in BE-order
18223 				scriptData[offset++] = byte2;
18224 				scriptData[offset++] = byte1;
18225 			}
18226 			break;
18227 		}
18228 		case PATCH_CODE_SELECTOR8: {
18229 			patchSelector = _selectorIdTable[patchValue];
18230 			if (patchSelector & 0xFF00)
18231 				error("Script-Patcher: 8 bit selector required, game uses 16 bit selector");
18232 			scriptData[offset] = patchSelector & 0xFF;
18233 			offset++;
18234 			break;
18235 		}
18236 		case PATCH_CODE_BYTE:
18237 			scriptData[offset] = patchValue & PATCH_BYTEMASK;
18238 			offset++;
18239 		}
18240 		patchData++;
18241 		patchWord = *patchData;
18242 	}
18243 }
18244 
verifySignature(uint32 byteOffset,const uint16 * signatureData,const char * signatureDescription,const SciSpan<const byte> & scriptData)18245 bool ScriptPatcher::verifySignature(uint32 byteOffset, const uint16 *signatureData, const char *signatureDescription, const SciSpan<const byte> &scriptData) {
18246 	uint16 sigSelector = 0;
18247 
18248 	uint16 sigWord = *signatureData;
18249 	while (sigWord != SIG_END) {
18250 		uint16 sigCommand = sigWord & SIG_COMMANDMASK;
18251 		uint16 sigValue = sigWord & SIG_VALUEMASK;
18252 		switch (sigCommand) {
18253 		case SIG_CODE_ADDTOOFFSET: {
18254 			// add value to offset
18255 			byteOffset += sigValue;
18256 			break;
18257 		}
18258 		case SIG_CODE_UINT16:
18259 		case SIG_CODE_SELECTOR16: {
18260 			if (byteOffset + 1 < scriptData.size()) {
18261 				byte byte1;
18262 				byte byte2;
18263 
18264 				switch (sigCommand) {
18265 				case SIG_CODE_UINT16: {
18266 					byte1 = sigValue & SIG_BYTEMASK;
18267 					signatureData++; sigWord = *signatureData;
18268 					if (sigWord & SIG_COMMANDMASK)
18269 						error("Script-Patcher: signature inconsistent\nFaulty signature: '%s'", signatureDescription);
18270 					byte2 = sigWord & SIG_BYTEMASK;
18271 					break;
18272 				}
18273 				case SIG_CODE_SELECTOR16: {
18274 					sigSelector = _selectorIdTable[sigValue];
18275 					byte1 = sigSelector & 0xFF;
18276 					byte2 = sigSelector >> 8;
18277 					break;
18278 				}
18279 				default:
18280 					byte1 = 0; byte2 = 0;
18281 				}
18282 				if (!_isMacSci11) {
18283 					if ((scriptData[byteOffset] != byte1) || (scriptData[byteOffset + 1] != byte2))
18284 						sigWord = SIG_MISMATCH;
18285 				} else {
18286 					// SCI1.1+ on macintosh had uint16s in script in BE-order
18287 					if ((scriptData[byteOffset] != byte2) || (scriptData[byteOffset + 1] != byte1))
18288 						sigWord = SIG_MISMATCH;
18289 				}
18290 				byteOffset += 2;
18291 			} else {
18292 				sigWord = SIG_MISMATCH;
18293 			}
18294 			break;
18295 		}
18296 		case SIG_CODE_SELECTOR8: {
18297 			if (byteOffset < scriptData.size()) {
18298 				sigSelector = _selectorIdTable[sigValue];
18299 				if (sigSelector & 0xFF00)
18300 					error("Script-Patcher: 8 bit selector required, game uses 16 bit selector\nFaulty signature: '%s'", signatureDescription);
18301 				if (scriptData[byteOffset] != (sigSelector & 0xFF))
18302 					sigWord = SIG_MISMATCH;
18303 				byteOffset++;
18304 			} else {
18305 				sigWord = SIG_MISMATCH; // out of bounds
18306 			}
18307 			break;
18308 		}
18309 		case SIG_CODE_BYTE:
18310 			if (byteOffset < scriptData.size()) {
18311 				if (scriptData[byteOffset] != sigWord)
18312 					sigWord = SIG_MISMATCH;
18313 				byteOffset++;
18314 			} else {
18315 				sigWord = SIG_MISMATCH; // out of bounds
18316 			}
18317 		}
18318 
18319 		if (sigWord == SIG_MISMATCH)
18320 			break;
18321 
18322 		signatureData++;
18323 		sigWord = *signatureData;
18324 	}
18325 
18326 	if (sigWord == SIG_END) // signature fully matched?
18327 		return true;
18328 	return false;
18329 }
18330 
18331 // will return -1 if no match was found, otherwise an offset to the start of the signature match
findSignature(uint32 magicDWord,int magicOffset,const uint16 * signatureData,const char * patchDescription,const SciSpan<const byte> & scriptData)18332 int32 ScriptPatcher::findSignature(uint32 magicDWord, int magicOffset, const uint16 *signatureData, const char *patchDescription, const SciSpan<const byte> &scriptData) {
18333 	if (scriptData.size() < 4) // we need to find a DWORD, so less than 4 bytes is not okay
18334 		return -1;
18335 
18336 	// magicDWord is in platform-specific BE/LE form, so that the later match will work, this was done for performance
18337 	const uint32 searchLimit = scriptData.size() - 3;
18338 	uint32 DWordOffset = 0;
18339 	// first search for the magic DWORD
18340 	while (DWordOffset < searchLimit) {
18341 		if (magicDWord == scriptData.getUint32At(DWordOffset)) {
18342 			// magic DWORD found, check if actual signature matches
18343 			uint32 offset = DWordOffset + magicOffset;
18344 
18345 			if (verifySignature(offset, signatureData, patchDescription, scriptData))
18346 				return offset;
18347 		}
18348 		DWordOffset++;
18349 	}
18350 	// nothing found
18351 	return -1;
18352 }
18353 
findSignature(const SciScriptPatcherEntry * patchEntry,const SciScriptPatcherRuntimeEntry * runtimeEntry,const SciSpan<const byte> & scriptData)18354 int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, const SciScriptPatcherRuntimeEntry *runtimeEntry, const SciSpan<const byte> &scriptData) {
18355 	return findSignature(runtimeEntry->magicDWord, runtimeEntry->magicOffset, patchEntry->signatureData, patchEntry->description, scriptData);
18356 }
18357 
18358 // Attention: Magic DWord is returned using platform specific byte order. This is done on purpose for performance.
calculateMagicDWordAndVerify(const char * signatureDescription,const uint16 * signatureData,bool magicDWordIncluded,uint32 & calculatedMagicDWord,int & calculatedMagicDWordOffset)18359 void ScriptPatcher::calculateMagicDWordAndVerify(const char *signatureDescription, const uint16 *signatureData, bool magicDWordIncluded, uint32 &calculatedMagicDWord, int &calculatedMagicDWordOffset) {
18360 	Selector curSelector = -1;
18361 	int magicOffset;
18362 	byte magicDWord[4];
18363 	int magicDWordLeft = 0;
18364 	uint16 curWord;
18365 	uint16 curCommand;
18366 	uint32 curValue;
18367 	byte byte1 = 0;
18368 	byte byte2 = 0;
18369 
18370 	memset(magicDWord, 0, sizeof(magicDWord));
18371 
18372 	curWord = *signatureData;
18373 	magicOffset = 0;
18374 	while (curWord != SIG_END) {
18375 		curCommand = curWord & SIG_COMMANDMASK;
18376 		curValue   = curWord & SIG_VALUEMASK;
18377 		switch (curCommand) {
18378 		case SIG_MAGICDWORD: {
18379 			if (magicDWordIncluded) {
18380 				if ((calculatedMagicDWord) || (magicDWordLeft))
18381 					error("Script-Patcher: Magic-DWORD specified multiple times in signature\nFaulty patch: '%s'", signatureDescription);
18382 				magicDWordLeft = 4;
18383 				calculatedMagicDWordOffset = magicOffset;
18384 			} else {
18385 				error("Script-Patcher: Magic-DWORD sequence found in patch data\nFaulty patch: '%s'", signatureDescription);
18386 			}
18387 			break;
18388 		}
18389 		case SIG_CODE_ADDTOOFFSET: {
18390 			magicOffset -= curValue;
18391 			if (magicDWordLeft)
18392 				error("Script-Patcher: Magic-DWORD contains AddToOffset command\nFaulty patch: '%s'", signatureDescription);
18393 			break;
18394 		}
18395 		case SIG_CODE_UINT16:
18396 		case SIG_CODE_SELECTOR16: {
18397 			// UINT16 or 1
18398 			switch (curCommand) {
18399 			case SIG_CODE_UINT16: {
18400 				signatureData++; curWord = *signatureData;
18401 				if (curWord & SIG_COMMANDMASK)
18402 					error("Script-Patcher: signature entry inconsistent\nFaulty patch: '%s'", signatureDescription);
18403 				if (!_isMacSci11) {
18404 					byte1 = curValue;
18405 					byte2 = curWord & SIG_BYTEMASK;
18406 				} else {
18407 					byte1 = curWord & SIG_BYTEMASK;
18408 					byte2 = curValue;
18409 				}
18410 				break;
18411 			}
18412 			case SIG_CODE_SELECTOR16: {
18413 				curSelector = _selectorIdTable[curValue];
18414 				if (curSelector == -1) {
18415 					curSelector = g_sci->getKernel()->findSelector(selectorNameTable[curValue]);
18416 					_selectorIdTable[curValue] = curSelector;
18417 				}
18418 				if (!_isMacSci11) {
18419 					byte1 = curSelector & 0x00FF;
18420 					byte2 = curSelector >> 8;
18421 				} else {
18422 					byte1 = curSelector >> 8;
18423 					byte2 = curSelector & 0x00FF;
18424 				}
18425 				break;
18426 			}
18427 			}
18428 			magicOffset -= 2;
18429 			if (magicDWordLeft) {
18430 				// Remember current word for Magic DWORD
18431 				magicDWord[4 - magicDWordLeft] = byte1;
18432 				magicDWordLeft--;
18433 				if (magicDWordLeft) {
18434 					magicDWord[4 - magicDWordLeft] = byte2;
18435 					magicDWordLeft--;
18436 				}
18437 				if (!magicDWordLeft) {
18438 					// Magic DWORD is now known, convert to platform specific byte order
18439 					calculatedMagicDWord = READ_UINT32(magicDWord);
18440 				}
18441 			}
18442 			break;
18443 		}
18444 		case SIG_CODE_BYTE:
18445 		case SIG_CODE_SELECTOR8: {
18446 			if (curCommand == SIG_CODE_SELECTOR8) {
18447 				curSelector = _selectorIdTable[curValue];
18448 				if (curSelector == -1) {
18449 					curSelector = g_sci->getKernel()->findSelector(selectorNameTable[curValue]);
18450 					_selectorIdTable[curValue] = curSelector;
18451 					if (curSelector != -1) {
18452 						if (curSelector & 0xFF00)
18453 							error("Script-Patcher: 8 bit selector required, game uses 16 bit selector\nFaulty patch: '%s'", signatureDescription);
18454 					}
18455 				}
18456 				curValue = curSelector;
18457 			}
18458 			magicOffset--;
18459 			if (magicDWordLeft) {
18460 				// Remember current byte for Magic DWORD
18461 				magicDWord[4 - magicDWordLeft] = (byte)curValue;
18462 				magicDWordLeft--;
18463 				if (!magicDWordLeft) {
18464 					// Magic DWORD is now known, convert to platform specific byte order
18465 					calculatedMagicDWord = READ_UINT32(magicDWord);
18466 				}
18467 			}
18468 			break;
18469 		}
18470 		case PATCH_CODE_GETORIGINALBYTE:
18471 		case PATCH_CODE_GETORIGINALUINT16: {
18472 			signatureData++; // skip over extra uint16
18473 			break;
18474 		}
18475 		default:
18476 			break;
18477 		}
18478 		signatureData++;
18479 		curWord = *signatureData;
18480 	}
18481 
18482 	if (magicDWordLeft)
18483 		error("Script-Patcher: Magic-DWORD beyond End-Of-Signature\nFaulty patch: '%s'", signatureDescription);
18484 	if (magicDWordIncluded) {
18485 		if (!calculatedMagicDWord) {
18486 			error("Script-Patcher: Magic-DWORD not specified in signature\nFaulty patch: '%s'", signatureDescription);
18487 		}
18488 	}
18489 }
18490 
18491 // This method calculates the magic DWORD for each entry in the signature table
18492 //  and it also initializes the selector table for selectors used in the signatures/patches of the current game
initSignature(const SciScriptPatcherEntry * patchTable)18493 void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable) {
18494 	const SciScriptPatcherEntry *curEntry = patchTable;
18495 	SciScriptPatcherRuntimeEntry *curRuntimeEntry;
18496 	int patchEntryCount = 0;
18497 
18498 	// Count entries and allocate runtime data
18499 	while (curEntry->signatureData) {
18500 		patchEntryCount++; curEntry++;
18501 	}
18502 	_runtimeTable = new SciScriptPatcherRuntimeEntry[patchEntryCount];
18503 	memset(_runtimeTable, 0, sizeof(SciScriptPatcherRuntimeEntry) * patchEntryCount);
18504 
18505 	curEntry = patchTable;
18506 	curRuntimeEntry = _runtimeTable;
18507 	while (curEntry->signatureData) {
18508 		// process signature
18509 		curRuntimeEntry->active = curEntry->defaultActive;
18510 		curRuntimeEntry->magicDWord = 0;
18511 		curRuntimeEntry->magicOffset = 0;
18512 
18513 		// We verify the signature data and remember the calculated magic DWord from the signature data
18514 		calculateMagicDWordAndVerify(curEntry->description, curEntry->signatureData, true, curRuntimeEntry->magicDWord, curRuntimeEntry->magicOffset);
18515 		// We verify the patch data
18516 		calculateMagicDWordAndVerify(curEntry->description, curEntry->patchData, false, curRuntimeEntry->magicDWord, curRuntimeEntry->magicOffset);
18517 
18518 		curEntry++; curRuntimeEntry++;
18519 	}
18520 }
18521 
18522 // This method enables certain patches
18523 //  It's used for patches, which are not meant to get applied all the time
enablePatch(const SciScriptPatcherEntry * patchTable,const char * searchDescription)18524 void ScriptPatcher::enablePatch(const SciScriptPatcherEntry *patchTable, const char *searchDescription) {
18525 	const SciScriptPatcherEntry *curEntry = patchTable;
18526 	SciScriptPatcherRuntimeEntry *runtimeEntry = _runtimeTable;
18527 	int searchDescriptionLen = strlen(searchDescription);
18528 	int matchCount = 0;
18529 
18530 	while (curEntry->signatureData) {
18531 		if (strncmp(curEntry->description, searchDescription, searchDescriptionLen) == 0) {
18532 			// match found, enable patch
18533 			runtimeEntry->active = true;
18534 			matchCount++;
18535 		}
18536 		curEntry++; runtimeEntry++;
18537 	}
18538 
18539 	if (!matchCount)
18540 		error("Script-Patcher: no patch found to enable");
18541 }
18542 
processScript(uint16 scriptNr,SciSpan<byte> scriptData)18543 void ScriptPatcher::processScript(uint16 scriptNr, SciSpan<byte> scriptData) {
18544 	const SciScriptPatcherEntry *signatureTable = NULL;
18545 	const SciScriptPatcherEntry *curEntry = NULL;
18546 	SciScriptPatcherRuntimeEntry *curRuntimeEntry = NULL;
18547 	const Sci::SciGameId gameId = g_sci->getGameId();
18548 
18549 	switch (gameId) {
18550 	case GID_CAMELOT:
18551 		signatureTable = camelotSignatures;
18552 		break;
18553 	case GID_ECOQUEST:
18554 		signatureTable = ecoquest1Signatures;
18555 		break;
18556 	case GID_ECOQUEST2:
18557 		signatureTable = ecoquest2Signatures;
18558 		break;
18559 	case GID_FANMADE:
18560 		signatureTable = fanmadeSignatures;
18561 		break;
18562 	case GID_FREDDYPHARKAS:
18563 		signatureTable = freddypharkasSignatures;
18564 		break;
18565 	case GID_HOYLE4:
18566 		signatureTable = hoyle4Signatures;
18567 		break;
18568 #ifdef ENABLE_SCI32
18569 	case GID_HOYLE5:
18570 		if (g_sci->getResMan()->testResource(ResourceId(kResourceTypeScript, 100)) &&
18571 			g_sci->getResMan()->testResource(ResourceId(kResourceTypeScript, 700)))
18572 			signatureTable = hoyle5Signatures;
18573 		else if (g_sci->getResMan()->testResource(ResourceId(kResourceTypeScript, 100)) &&
18574 			    !g_sci->getResMan()->testResource(ResourceId(kResourceTypeScript, 700)))
18575 			signatureTable = hoyle5ChildrensCollectionSignatures;
18576 		else if (!g_sci->getResMan()->testResource(ResourceId(kResourceTypeScript, 100)) &&
18577 			      g_sci->getResMan()->testResource(ResourceId(kResourceTypeScript, 700)))
18578 			signatureTable = hoyle5BridgeSignatures;
18579 		break;
18580 	case GID_GK1:
18581 		signatureTable = gk1Signatures;
18582 		break;
18583 	case GID_GK2:
18584 		signatureTable = gk2Signatures;
18585 		break;
18586 #endif
18587 	case GID_ICEMAN:
18588 		signatureTable = icemanSignatures;
18589 		break;
18590 	case GID_KQ5:
18591 		signatureTable = kq5Signatures;
18592 		break;
18593 	case GID_KQ6:
18594 		signatureTable = kq6Signatures;
18595 		break;
18596 #ifdef ENABLE_SCI32
18597 	case GID_KQ7:
18598 		signatureTable = kq7Signatures;
18599 		break;
18600 #endif
18601 	case GID_LAURABOW:
18602 		signatureTable = laurabow1Signatures;
18603 		break;
18604 	case GID_LAURABOW2:
18605 		signatureTable = laurabow2Signatures;
18606 		break;
18607 #ifdef ENABLE_SCI32
18608 	case GID_LIGHTHOUSE:
18609 		signatureTable = lighthouseSignatures;
18610 		break;
18611 #endif
18612 	case GID_LONGBOW:
18613 		signatureTable = longbowSignatures;
18614 		break;
18615 	case GID_LSL1:
18616 		signatureTable = larry1Signatures;
18617 		break;
18618 	case GID_LSL2:
18619 		signatureTable = larry2Signatures;
18620 		break;
18621 	case GID_LSL5:
18622 		signatureTable = larry5Signatures;
18623 		break;
18624 	case GID_LSL6:
18625 		signatureTable = larry6Signatures;
18626 		break;
18627 #ifdef ENABLE_SCI32
18628 	case GID_LSL6HIRES:
18629 		signatureTable = larry6HiresSignatures;
18630 		break;
18631 	case GID_LSL7:
18632 		signatureTable = larry7Signatures;
18633 		break;
18634 #endif
18635 	case GID_MOTHERGOOSE256:
18636 		signatureTable = mothergoose256Signatures;
18637 		break;
18638 #ifdef ENABLE_SCI32
18639 	case GID_MOTHERGOOSEHIRES:
18640 		signatureTable = mothergooseHiresSignatures;
18641 		break;
18642 
18643 	case GID_PHANTASMAGORIA:
18644 		signatureTable = phantasmagoriaSignatures;
18645 		break;
18646 
18647 	case GID_PHANTASMAGORIA2:
18648 		signatureTable = phantasmagoria2Signatures;
18649 		break;
18650 #endif
18651 	case GID_PQ1:
18652 		signatureTable = pq1vgaSignatures;
18653 		break;
18654 	case GID_PQ3:
18655 		signatureTable = pq3Signatures;
18656 		break;
18657 #ifdef ENABLE_SCI32
18658 	case GID_PQ4:
18659 		signatureTable = pq4Signatures;
18660 		break;
18661 	case GID_PQSWAT:
18662 		signatureTable = pqSwatSignatures;
18663 		break;
18664 #endif
18665 	case GID_QFG1:
18666 		signatureTable = qfg1egaSignatures;
18667 		break;
18668 	case GID_QFG1VGA:
18669 		signatureTable = qfg1vgaSignatures;
18670 		break;
18671 	case GID_QFG2:
18672 		signatureTable = qfg2Signatures;
18673 		break;
18674 	case GID_QFG3:
18675 		signatureTable = qfg3Signatures;
18676 		break;
18677 #ifdef ENABLE_SCI32
18678 	case GID_QFG4:
18679 		signatureTable = qfg4Signatures;
18680 		break;
18681 	case GID_RAMA:
18682 		signatureTable = ramaSignatures;
18683 		break;
18684 	case GID_SHIVERS:
18685 		signatureTable = shiversSignatures;
18686 		break;
18687 #endif
18688 	case GID_SQ1:
18689 		signatureTable = sq1vgaSignatures;
18690 		break;
18691 	case GID_SQ4:
18692 		signatureTable = sq4Signatures;
18693 		break;
18694 	case GID_SQ5:
18695 		signatureTable = sq5Signatures;
18696 		break;
18697 #ifdef ENABLE_SCI32
18698 	case GID_SQ6:
18699 		signatureTable = sq6Signatures;
18700 		break;
18701 	case GID_TORIN:
18702 		signatureTable = torinSignatures;
18703 		break;
18704 #endif
18705 	default:
18706 		break;
18707 	}
18708 
18709 	if (signatureTable) {
18710 		_isMacSci11 = (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1);
18711 
18712 		if (!_runtimeTable) {
18713 			// signature table needs to get initialized (Magic DWORD set, selector table set)
18714 			initSignature(signatureTable);
18715 
18716 			// Do additional game-specific initialization
18717 			switch (gameId) {
18718 			case GID_FREDDYPHARKAS:
18719 				if (_isMacSci11 && !g_sci->getResMan()->testResource(ResourceId(kResourceTypeView, 844))) {
18720 					enablePatch(signatureTable, "Mac: skip broken hop singh scene");
18721 				}
18722 				break;
18723 			case GID_GK2:
18724 				// Enable subtitle compatibility if a sync resource is present
18725 				if (g_sci->getResMan()->testResource(ResourceId(kResourceTypeSync, 10))) {
18726 					enablePatch(signatureTable, "subtitle patch compatibility");
18727 				}
18728 				break;
18729 			case GID_KQ5:
18730 				if (g_sci->_features->useAltWinGMSound()) {
18731 					// See the explanation in the kq5SignatureWinGMSignals comment
18732 					enablePatch(signatureTable, "Win: GM Music signal checks");
18733 				}
18734 				break;
18735 			case GID_KQ6:
18736 				if (g_sci->isCD()) {
18737 					// Enables Dual mode patches (audio + subtitles at the same time) for King's Quest 6
18738 					enablePatch(signatureTable, "CD: audio + text support");
18739 				}
18740 				if (_isMacSci11) {
18741 					// Enables Mac-only patch to work around missing pic
18742 					enablePatch(signatureTable, "Mac: Drink Me pic");
18743 				}
18744 				break;
18745 			case GID_LAURABOW2:
18746 				if (g_sci->isCD()) {
18747 					// Enables Dual mode patches (audio + subtitles at the same time) for Laura Bow 2
18748 					enablePatch(signatureTable, "CD: audio + text support");
18749 				}
18750 				break;
18751 			case GID_QFG4:
18752 				if (g_sci->isCD()) {
18753 					// Floppy doesn't need this
18754 					enablePatch(signatureTable, "CD: fix rope during Igor rescue (1/2)");
18755 					enablePatch(signatureTable, "CD: fix rope during Igor rescue (2/2)");
18756 
18757 					// Similar signatures that patch with different addresses/offsets
18758 					enablePatch(signatureTable, "CD: fix guild tunnel access (3/3)");
18759 					enablePatch(signatureTable, "CD: fix crest bookshelf");
18760 					enablePatch(signatureTable, "CD: fix peer bats, upper door (2/2)");
18761 				} else {
18762 					enablePatch(signatureTable, "Floppy: fix guild tunnel access (3/3)");
18763 					enablePatch(signatureTable, "Floppy: fix crest bookshelf");
18764 					enablePatch(signatureTable, "Floppy: fix peer bats, upper door (2/2)");
18765 				}
18766 				break;
18767 			case GID_SQ4:
18768 				// Enable the dress-purchase flag fixes for English Amiga only.
18769 				//  One of these patches is applied to scripts that are the same as those
18770 				//  in other versions which must not be patched, including German Amiga.
18771 				if (g_sci->getPlatform() == Common::kPlatformAmiga) {
18772 					// Check for buggy Sock's (room 371) script from English Amiga version
18773 					Resource *socksScript = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 371), false);
18774 					if (socksScript && socksScript->size() == 14340) {
18775 						enablePatch(signatureTable, "Amiga: dress purchase flag check fix");
18776 						enablePatch(signatureTable, "Amiga: dress purchase flag clear fix");
18777 					}
18778 				}
18779 
18780 				if (g_sci->isCD() && !g_sci->getResMan()->testResource(ResourceId(kResourceTypeScript, 271))) {
18781 					enablePatch(signatureTable, "CD: disable timepod code for removed room");
18782 				}
18783 				break;
18784 			default:
18785 				break;
18786 			}
18787 		}
18788 
18789 		curEntry = signatureTable;
18790 		curRuntimeEntry = _runtimeTable;
18791 
18792 		while (curEntry->signatureData) {
18793 			if ((scriptNr == curEntry->scriptNr) && (curRuntimeEntry->active)) {
18794 				int32 foundOffset = 0;
18795 				int16 applyCount = curEntry->applyCount;
18796 				do {
18797 					foundOffset = findSignature(curEntry, curRuntimeEntry, scriptData);
18798 					if (foundOffset != -1) {
18799 						// found, so apply the patch
18800 						debugC(kDebugLevelScriptPatcher, "Script-Patcher: '%s' on script %d offset %d", curEntry->description, scriptNr, foundOffset);
18801 						applyPatch(curEntry, scriptData, foundOffset);
18802 					}
18803 					applyCount--;
18804 				} while ((foundOffset != -1) && (applyCount));
18805 			}
18806 			curEntry++; curRuntimeEntry++;
18807 		}
18808 	}
18809 }
18810 
18811 } // End of namespace Sci
18812