diff options
author | Colin Snover | 2016-08-10 18:47:21 -0500 |
---|---|---|
committer | Colin Snover | 2016-08-12 10:26:06 -0500 |
commit | efaf7f20d66faecd50c3563a64c952e249ec1b60 (patch) | |
tree | 3713e5b16bef20a01348112dda82e2c1b872c633 | |
parent | 811f73f557a5106f4b1b934ffc014f8961f26948 (diff) | |
download | scummvm-rg350-efaf7f20d66faecd50c3563a64c952e249ec1b60.tar.gz scummvm-rg350-efaf7f20d66faecd50c3563a64c952e249ec1b60.tar.bz2 scummvm-rg350-efaf7f20d66faecd50c3563a64c952e249ec1b60.zip |
SCI32: Fix crash when multiple nodes are deleted during kListEachElementDo
This happens in e.g. Phant1, when walking from dining, to
reception, to up the stairs.
-rw-r--r-- | engines/sci/engine/klists.cpp | 29 | ||||
-rw-r--r-- | engines/sci/engine/segment.h | 15 |
2 files changed, 39 insertions, 5 deletions
diff --git a/engines/sci/engine/klists.cpp b/engines/sci/engine/klists.cpp index c0da2daaeb..53be26b37f 100644 --- a/engines/sci/engine/klists.cpp +++ b/engines/sci/engine/klists.cpp @@ -374,13 +374,21 @@ reg_t kFindKey(EngineState *s, int argc, reg_t *argv) { reg_t kDeleteKey(EngineState *s, int argc, reg_t *argv) { reg_t node_pos = kFindKey(s, 2, argv); - Node *n; List *list = s->_segMan->lookupList(argv[0]); if (node_pos.isNull()) return NULL_REG; // Signal failure - n = s->_segMan->lookupNode(node_pos); + Node *n = s->_segMan->lookupNode(node_pos); + +#ifdef ENABLE_SCI32 + for (int i = 1; i <= list->numRecursions; ++i) { + if (list->nextNodes[i] == node_pos) { + list->nextNodes[i] = n->succ; + } + } +#endif + if (list->first == node_pos) list->first = n->succ; if (list->last == node_pos) @@ -544,9 +552,18 @@ reg_t kListEachElementDo(EngineState *s, int argc, reg_t *argv) { ObjVarRef address; + ++list->numRecursions; + + if (list->numRecursions > ARRAYSIZE(list->nextNodes)) { + error("Too much recursion in kListEachElementDo"); + } + while (curNode) { - // We get the next node here as the current node might be gone after the invoke - reg_t nextNode = curNode->succ; + // We get the next node here as the current node might be deleted by the + // invoke. In the case that the next node is also deleted, kDeleteKey + // needs to be able to adjust the location of the next node, which is + // why it is stored on the list instead of on the stack + list->nextNodes[list->numRecursions] = curNode->succ; curObject = curNode->value; // First, check if the target selector is a variable @@ -561,9 +578,11 @@ reg_t kListEachElementDo(EngineState *s, int argc, reg_t *argv) { invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2); } - curNode = s->_segMan->lookupNode(nextNode); + curNode = s->_segMan->lookupNode(list->nextNodes[list->numRecursions]); } + --list->numRecursions; + return s->r_acc; } diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h index 7c39050f18..97a6cb585f 100644 --- a/engines/sci/engine/segment.h +++ b/engines/sci/engine/segment.h @@ -200,6 +200,21 @@ struct Node { struct List { reg_t first; reg_t last; + +#ifdef ENABLE_SCI32 + /** + * The next node for each level of recursion during iteration over this list + * by kListEachElementDo. + */ + reg_t nextNodes[10]; + + /** + * The current level of recursion of kListEachElementDo for this list. + */ + int numRecursions; + + List() : numRecursions(0) {} +#endif }; struct Hunk { |