diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index 60f66b0e49d..9f72f56b7c9 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -11723,6 +11723,51 @@ static const uint16 phant1RatPatch[] = { PATCH_END }; +// In the basement there is a moment where clicking the fast-forward button +// locks up the game. Fast-forward changes the current room script to state +// zero and sets global 115. Most scripts' changeState methods start by testing +// this global to implement fast-forward behavior. They then reset the global +// when disposing before calling handsOn. scaryHandsOnCode:doit enables input, +// empties the event queue, and then polls and processes one final event. +// This creates a window where handsOn can process a fast-forward click and +// re-enter the room script while it's in the middle of disposing itself. +// +// Most room scripts have a structure that survives this re-entrancy, but room +// 20100's sEnterFrom20200 is unusual. It doesn't test the fast-forward global +// in state zero. Instead, it unconditionally calls handsOff, causing the +// original handsOn call to undo its work before it returns. +// +// We fix this by swapping two function calls in sEnterFrom20200:dispose: +// handsOn and super:dispose. Now the script is fully disposed before handsOn. +// If a fast-forward click occurs it will no longer have any effect, because +// the room no longer has a script. If other scripts are discovered with this +// same problem, this patch will probably apply. +// +// Applies to: All versions +// Responsible method: sEnterFrom20200:dispose +// Fixes bug: #14368 +static const uint16 phant1BasementFastForwardSignature[] = { + 0x35, 0x00, // ldi 00 + 0xa1, SIG_MAGICDWORD, 0x73, // lag 73 [ fast-forward = 0 ] + 0x38, SIG_SELECTOR16(handsOn), // pushi handsOn + 0x76, // push0 + 0x81, 0x01, // lag 01 + 0x4a, SIG_UINT16(0x0004), // send 04 [ Scary handsOn: ] + 0x38, SIG_SELECTOR16(dispose), // pushi dispose + 0x76, // push0 + 0x59, 0x01, // &rest 01 + 0x57, SIG_ADDTOOFFSET(+1), // super 04 [ super dispose: &rest ] + SIG_UINT16(0x0004), + SIG_END +}; + +static const uint16 phant1BasementFastForwardPatch[] = { + PATCH_ADDTOOFFSET(+4), // [ fast-forward = 0 ] + PATCH_GETORIGINALBYTES(13, 10), // [ super dispose: &rest ] + PATCH_GETORIGINALBYTES(4, 9), // [ Scary handsOn: ] + PATCH_END +}; + // In Phantasmagoria the cursor's hover state will not trigger on any of the // buttons in the main menu after returning to the main menu from a game, or // when choosing "Quit" on the main menu and then cancelling the quit in the @@ -12127,6 +12172,7 @@ static const SciScriptPatcherEntry phantasmagoriaSignatures[] = { { true, 901, "fix invalid array construction", 1, sci21IntArraySignature, sci21IntArrayPatch }, { true, 901, "fix delete save", 1, phant1DeleteSaveSignature, phant1DeleteSavePatch }, { true, 1111, "ignore audio settings from save game", 1, phant1SavedVolumeSignature, phant1SavedVolumePatch }, + { true, 20100, "fix basement fast-forward", 1, phant1BasementFastForwardSignature, phant1BasementFastForwardPatch }, { true, 20200, "fix broken rat init in sEnterFromAlcove", 1, phant1RatSignature, phant1RatPatch }, { true, 20200, "fix chapter 5 wine cask hotspot", 1, phant1WineCaskHotspotSignature, phant1WineCaskHotspotPatch }, { true, 45950, "fix chase file deletion", 1, phant1DeleteChaseFileSignature, phant1DeleteChaseFilePatch },