From ee588a8c33b3f75c9ee64ca28ef5010e292b2458 Mon Sep 17 00:00:00 2001 From: Bastien Bouclet Date: Fri, 11 Aug 2017 19:05:28 +0200 Subject: MOHAWK: Riven: Patch an invalid card change when entering Gehn's office Fixes #10118. --- engines/mohawk/riven_card.cpp | 115 ++++++++++++++++++++++++++++++++++++--- engines/mohawk/riven_scripts.cpp | 33 +++++++++++ engines/mohawk/riven_scripts.h | 8 +++ 3 files changed, 148 insertions(+), 8 deletions(-) (limited to 'engines/mohawk') diff --git a/engines/mohawk/riven_card.cpp b/engines/mohawk/riven_card.cpp index 3aab79d82f..feea22a663 100644 --- a/engines/mohawk/riven_card.cpp +++ b/engines/mohawk/riven_card.cpp @@ -130,13 +130,7 @@ void RivenCard::applyPatches(uint16 id) { forwardEnabled.index }; - // Script data is expected to be in big endian - for (uint i = 0; i < ARRAYSIZE(patchData); i++) { - patchData[i] = TO_BE_16(patchData[i]); - } - - Common::MemoryReadStream patchStream((const byte *)(patchData), ARRAYSIZE(patchData) * sizeof(uint16)); - RivenScriptPtr patchScript = _vm->_scriptMan->readScript(&patchStream); + RivenScriptPtr patchScript = _vm->_scriptMan->readScriptFromData(patchData, ARRAYSIZE(patchData)); // Append the patch to the existing script RivenScriptPtr loadScript = getScript(kCardLoadScript); @@ -145,6 +139,111 @@ void RivenCard::applyPatches(uint16 id) { debugC(kRivenDebugPatches, "Applied fix always enabled forward hotspot in card %x", globalId); } + // In Gehn's office, after having encountered him once before and coming back + // with the trap book, the draw update script of card 1 tries to switch to + // card 2 while still loading card 1. Switching cards is not allowed during + // draw update scripts, resulting in an use after free crash. + // + // Here we backport the fix that has been made in the DVD version to the CD version. + // + // Script before patch: + // == Script 1 == + // type: CardUpdate + // switch (agehn) { + // case 1: + // switch (atrapbook) { + // case 1: + // obutton = 1; + // transition(16); + // switchCard(2); + // break; + // } + // break; + // case 2: + // activatePLST(5); + // break; + // case 3: + // activatePLST(5); + // break; + // } + // + // + // Script after patch: + // == Script 1 == + // type: CardUpdate + // switch (agehn) { + // case 1: + // switch (atrapbook) { + // case 1: + // obutton = 1; + // activatePLST(6); + // break; + // } + // break; + // case 2: + // activatePLST(5); + // break; + // case 3: + // activatePLST(5); + // break; + // } + // + // == Script 2 == + // type: CardEnter + // switch (agehn) { + // case 1: + // switch (atrapbook) { + // case 1: + // transition(16); + // switchCard(2); + // break; + // } + // break; + // } + if (globalId == 0x2E76 && !(_vm->getFeatures() & GF_DVD)) { + uint16 aGehnVariable = _vm->getStack()->getIdFromName(kVariableNames, "agehn"); + uint16 aTrapBookVariable = _vm->getStack()->getIdFromName(kVariableNames, "atrapbook"); + uint16 patchData[] = { + 1, // Command count in script + kRivenCommandSwitch, + 2, // Unused + aGehnVariable, + 1, // Branches count + + 1, // agehn == 1 branch + 1, // Command count in sub-script + kRivenCommandSwitch, + 2, // Unused + aTrapBookVariable, + 1, // Branches count + + 1, // atrapbook == 1 branch + 2, // Command count in sub-script + kRivenCommandTransition, + 1, // Argument count + kRivenTransitionBlend, + kRivenCommandChangeCard, + 1, // Argument count + 2 // Card id + }; + + // Add the new script to the list + RivenTypedScript patchScript; + patchScript.type = kCardEnterScript; + patchScript.script = _vm->_scriptMan->readScriptFromData(patchData, ARRAYSIZE(patchData)); + _scripts.push_back(patchScript); + + // Add a black picture to the card's list to be able to use it in the second part of the patch + Picture blackPicture; + blackPicture.index = 6; + blackPicture.id = 117; + blackPicture.rect = Common::Rect(608, 392); + _pictureList.push_back(blackPicture); + + debugC(kRivenDebugPatches, "Applied invalid card change during screen update (1/2) to card %x", globalId); + // The second part of this patch is in the script patches + } + // Apply script patches for (uint i = 0; i < _scripts.size(); i++) { _scripts[i].script->applyCardPatches(_vm, globalId, _scripts[i].type, 0xFFFF); @@ -698,7 +797,7 @@ RivenScriptPtr RivenCard::onKeyAction(RivenKeyAction keyAction) { static const char *upNames [] = { "up", nullptr }; static const char *downNames [] = { "down", nullptr }; - static const char **hotspotNames; + const char **hotspotNames = nullptr; switch (keyAction) { case kKeyActionMoveForward: hotspotNames = forwardNames; diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp index 21c18fa468..cb040ee4a0 100644 --- a/engines/mohawk/riven_scripts.cpp +++ b/engines/mohawk/riven_scripts.cpp @@ -179,6 +179,16 @@ RivenScriptPtr RivenScriptManager::createScriptFromData(uint16 commandCount, ... return readScript(&readStream); } +RivenScriptPtr RivenScriptManager::readScriptFromData(uint16 *data, uint16 size) { + // Script data is expected to be in big endian + for (uint i = 0; i < size; i++) { + data[i] = TO_BE_16(data[i]); + } + + Common::MemoryReadStream patchStream((const byte *)(data), size * sizeof(uint16)); + return _vm->_scriptMan->readScript(&patchStream); +} + RivenScriptPtr RivenScriptManager::createScriptWithCommand(RivenCommand *command) { assert(command); @@ -312,6 +322,29 @@ void RivenScript::applyCardPatches(MohawkEngine_Riven *vm, uint32 cardGlobalId, debugC(kRivenDebugPatches, "Applied missing closing sound patch to card %x", cardGlobalId); } + // Second part of the patch to fix the invalid card change when entering Gehn's office + // The first part is in the card patches. + if (cardGlobalId == 0x2E76 && scriptType == kCardUpdateScript && !(vm->getFeatures() & GF_DVD)) { + shouldApplyPatches = true; + + for (uint i = 0; i < _commands.size(); i++) { + int transitionIndex = -1; + if (_commands[i]->getType() == kRivenCommandTransition) { + transitionIndex = i; + } + if (transitionIndex >= 0) { + _commands.remove_at(transitionIndex + 1); + _commands.remove_at(transitionIndex); + + RivenSimpleCommand::ArgumentArray arguments; + arguments.push_back(6); + _commands.push_back(RivenCommandPtr(new RivenSimpleCommand(vm, kRivenCommandActivatePLST, arguments))); + } + } + + debugC(kRivenDebugPatches, "Applied invalid card change during screen update (2/2) to card %x", cardGlobalId); + } + if (shouldApplyPatches) { for (uint i = 0; i < _commands.size(); i++) { _commands[i]->applyCardPatches(cardGlobalId, scriptType, hotspotId); diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h index e77b9ae2d9..25cf363450 100644 --- a/engines/mohawk/riven_scripts.h +++ b/engines/mohawk/riven_scripts.h @@ -170,6 +170,14 @@ public: /** Read a single script from a stream */ RivenScriptPtr readScript(Common::ReadStream *stream); + /** + * Read a script from an array of uint16 + * @param data Script data array. Will be modified. + * @param size Number of uint16 in data + * @return + */ + RivenScriptPtr readScriptFromData(uint16 *data, uint16 size); + /** Create a script from the caller provided arguments containing raw data */ RivenScriptPtr createScriptFromData(uint16 commandCount, ...); -- cgit v1.2.3