aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBastien Bouclet2017-08-11 19:05:28 +0200
committerBastien Bouclet2017-08-11 19:05:55 +0200
commitee588a8c33b3f75c9ee64ca28ef5010e292b2458 (patch)
treed2061c30bc238291d9144f36be3bf53a634e6f43
parentc28d246cb0d8ad7773eef8008fd18dae53abbdbf (diff)
downloadscummvm-rg350-ee588a8c33b3f75c9ee64ca28ef5010e292b2458.tar.gz
scummvm-rg350-ee588a8c33b3f75c9ee64ca28ef5010e292b2458.tar.bz2
scummvm-rg350-ee588a8c33b3f75c9ee64ca28ef5010e292b2458.zip
MOHAWK: Riven: Patch an invalid card change when entering Gehn's office
Fixes #10118.
-rw-r--r--engines/mohawk/riven_card.cpp115
-rw-r--r--engines/mohawk/riven_scripts.cpp33
-rw-r--r--engines/mohawk/riven_scripts.h8
3 files changed, 148 insertions, 8 deletions
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, ...);