aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/engine/klists.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/engine/klists.cpp')
-rw-r--r--engines/sci/engine/klists.cpp454
1 files changed, 246 insertions, 208 deletions
diff --git a/engines/sci/engine/klists.cpp b/engines/sci/engine/klists.cpp
index c0da2daaeb..de5d7f1536 100644
--- a/engines/sci/engine/klists.cpp
+++ b/engines/sci/engine/klists.cpp
@@ -302,14 +302,14 @@ reg_t kAddToEnd(EngineState *s, int argc, reg_t *argv) {
reg_t kAddAfter(EngineState *s, int argc, reg_t *argv) {
List *list = s->_segMan->lookupList(argv[0]);
- Node *firstnode = argv[1].isNull() ? NULL : s->_segMan->lookupNode(argv[1]);
- Node *newnode = s->_segMan->lookupNode(argv[2]);
+ Node *firstNode = s->_segMan->lookupNode(argv[1]);
+ Node *newNode = s->_segMan->lookupNode(argv[2]);
#ifdef CHECK_LISTS
checkListPointer(s->_segMan, argv[0]);
#endif
- if (!newnode) {
+ if (!newNode) {
error("New 'node' %04x:%04x is not a node", PRINT_REG(argv[2]));
return NULL_REG;
}
@@ -320,22 +320,64 @@ reg_t kAddAfter(EngineState *s, int argc, reg_t *argv) {
}
if (argc == 4)
- newnode->key = argv[3];
+ newNode->key = argv[3];
- if (firstnode) { // We're really appending after
- reg_t oldnext = firstnode->succ;
+ if (firstNode) { // We're really appending after
+ const reg_t oldNext = firstNode->succ;
- newnode->pred = argv[1];
- firstnode->succ = argv[2];
- newnode->succ = oldnext;
+ newNode->pred = argv[1];
+ firstNode->succ = argv[2];
+ newNode->succ = oldNext;
- if (oldnext.isNull()) // Appended after last node?
+ if (oldNext.isNull()) // Appended after last node?
// Set new node as last list node
list->last = argv[2];
else
- s->_segMan->lookupNode(oldnext)->pred = argv[2];
+ s->_segMan->lookupNode(oldNext)->pred = argv[2];
- } else { // !firstnode
+ } else {
+ addToFront(s, argv[0], argv[2]); // Set as initial list node
+ }
+
+ return s->r_acc;
+}
+
+reg_t kAddBefore(EngineState *s, int argc, reg_t *argv) {
+ List *list = s->_segMan->lookupList(argv[0]);
+ Node *firstNode = s->_segMan->lookupNode(argv[1]);
+ Node *newNode = s->_segMan->lookupNode(argv[2]);
+
+#ifdef CHECK_LISTS
+ checkListPointer(s->_segMan, argv[0]);
+#endif
+
+ if (!newNode) {
+ error("New 'node' %04x:%04x is not a node", PRINT_REG(argv[2]));
+ return NULL_REG;
+ }
+
+ if (argc != 3 && argc != 4) {
+ error("kAddBefore: Haven't got 3 or 4 arguments, aborting");
+ return NULL_REG;
+ }
+
+ if (argc == 4)
+ newNode->key = argv[3];
+
+ if (firstNode) { // We're really appending before
+ const reg_t oldPred = firstNode->pred;
+
+ newNode->succ = argv[1];
+ firstNode->pred = argv[2];
+ newNode->pred = oldPred;
+
+ if (oldPred.isNull()) // Appended before first node?
+ // Set new node as first list node
+ list->first = argv[2];
+ else
+ s->_segMan->lookupNode(oldPred)->succ = argv[2];
+
+ } else {
addToFront(s, argv[0], argv[2]); // Set as initial list node
}
@@ -374,13 +416,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)
@@ -486,7 +536,7 @@ reg_t kListAt(EngineState *s, int argc, reg_t *argv) {
List *list = s->_segMan->lookupList(argv[0]);
reg_t curAddress = list->first;
if (list->first.isNull()) {
- error("kListAt tried to reference empty list (%04x:%04x)", PRINT_REG(argv[0]));
+ // Happens in Torin when examining Di's locket in chapter 3
return NULL_REG;
}
Node *curNode = s->_segMan->lookupNode(curAddress);
@@ -539,15 +589,23 @@ reg_t kListEachElementDo(EngineState *s, int argc, reg_t *argv) {
List *list = s->_segMan->lookupList(argv[0]);
Node *curNode = s->_segMan->lookupNode(list->first);
- reg_t curObject;
Selector slc = argv[1].toUint16();
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;
- curObject = curNode->value;
+ // 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;
+ reg_t curObject = curNode->value;
// First, check if the target selector is a variable
if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) {
@@ -559,11 +617,18 @@ reg_t kListEachElementDo(EngineState *s, int argc, reg_t *argv) {
}
} else {
invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2);
+ // Check if the call above leads to a game restore, in which case
+ // the segment manager will be reset, and the original list will
+ // be invalidated
+ if (s->abortScriptProcessing == kAbortLoadGame)
+ return s->r_acc;
}
- curNode = s->_segMan->lookupNode(nextNode);
+ curNode = s->_segMan->lookupNode(list->nextNodes[list->numRecursions]);
}
+ --list->numRecursions;
+
return s->r_acc;
}
@@ -571,37 +636,51 @@ reg_t kListFirstTrue(EngineState *s, int argc, reg_t *argv) {
List *list = s->_segMan->lookupList(argv[0]);
Node *curNode = s->_segMan->lookupNode(list->first);
- reg_t curObject;
Selector slc = argv[1].toUint16();
ObjVarRef address;
- s->r_acc = NULL_REG; // reset the accumulator
+ s->r_acc = NULL_REG;
+
+ ++list->numRecursions;
+
+ if (list->numRecursions >= ARRAYSIZE(list->nextNodes)) {
+ error("Too much recursion in kListFirstTrue");
+ }
while (curNode) {
- reg_t nextNode = curNode->succ;
- curObject = curNode->value;
+ // 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;
+ reg_t curObject = curNode->value;
// First, check if the target selector is a variable
if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) {
// If it's a variable selector, check its value.
// Example: script 64893 in Torin, MenuHandler::isHilited checks
// all children for variable selector 0x03ba (bHilited).
- if (!readSelector(s->_segMan, curObject, slc).isNull())
- return curObject;
+ if (!readSelector(s->_segMan, curObject, slc).isNull()) {
+ s->r_acc = curObject;
+ break;
+ }
} else {
invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2);
// Check if the result is true
- if (!s->r_acc.isNull())
- return curObject;
+ if (!s->r_acc.isNull()) {
+ s->r_acc = curObject;
+ break;
+ }
}
- curNode = s->_segMan->lookupNode(nextNode);
+ curNode = s->_segMan->lookupNode(list->nextNodes[list->numRecursions]);
}
- // No selector returned true
- return NULL_REG;
+ --list->numRecursions;
+
+ return s->r_acc;
}
reg_t kListAllTrue(EngineState *s, int argc, reg_t *argv) {
@@ -613,10 +692,20 @@ reg_t kListAllTrue(EngineState *s, int argc, reg_t *argv) {
ObjVarRef address;
- s->r_acc = make_reg(0, 1); // reset the accumulator
+ s->r_acc = TRUE_REG;
+
+ ++list->numRecursions;
+
+ if (list->numRecursions >= ARRAYSIZE(list->nextNodes)) {
+ error("Too much recursion in kListAllTrue");
+ }
while (curNode) {
- 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
@@ -631,7 +720,53 @@ reg_t kListAllTrue(EngineState *s, int argc, reg_t *argv) {
if (s->r_acc.isNull())
break;
- curNode = s->_segMan->lookupNode(nextNode);
+ curNode = s->_segMan->lookupNode(list->nextNodes[list->numRecursions]);
+ }
+
+ --list->numRecursions;
+
+ return s->r_acc;
+}
+
+reg_t kListSort(EngineState *s, int argc, reg_t *argv) {
+ List *list = s->_segMan->lookupList(argv[0]);
+ const int16 selector = argv[1].toSint16();
+ const bool isDescending = argc > 2 ? (bool)argv[2].toUint16() : false;
+
+ reg_t firstNode = list->first;
+ for (reg_t node = firstNode; node != NULL_REG; node = s->_segMan->lookupNode(firstNode)->succ) {
+
+ reg_t a;
+ if (selector == -1) {
+ a = s->_segMan->lookupNode(node)->value;
+ } else {
+ a = readSelector(s->_segMan, s->_segMan->lookupNode(node)->value, selector);
+ }
+
+ firstNode = node;
+ for (reg_t newNode = s->_segMan->lookupNode(node)->succ; newNode != NULL_REG; newNode = s->_segMan->lookupNode(newNode)->succ) {
+ reg_t b;
+ if (selector == -1) {
+ b = s->_segMan->lookupNode(newNode)->value;
+ } else {
+ b = readSelector(s->_segMan, s->_segMan->lookupNode(newNode)->value, selector);
+ }
+
+ if ((!isDescending && b < a) || (isDescending && a < b)) {
+ firstNode = newNode;
+ a = b;
+ }
+ }
+
+ if (firstNode != node) {
+ reg_t buf[4] = { argv[0], s->_segMan->lookupNode(firstNode)->key };
+ kDeleteKey(s, 2, buf);
+
+ buf[1] = node;
+ buf[2] = firstNode;
+ buf[3] = s->_segMan->lookupNode(firstNode)->value;
+ kAddBefore(s, 4, buf);
+ }
}
return s->r_acc;
@@ -643,11 +778,6 @@ reg_t kList(EngineState *s, int argc, reg_t *argv) {
error("not supposed to call this");
}
-reg_t kAddBefore(EngineState *s, int argc, reg_t *argv) {
- error("Unimplemented function kAddBefore called");
- return s->r_acc;
-}
-
reg_t kMoveToFront(EngineState *s, int argc, reg_t *argv) {
error("Unimplemented function kMoveToFront called");
return s->r_acc;
@@ -659,197 +789,105 @@ reg_t kMoveToEnd(EngineState *s, int argc, reg_t *argv) {
}
reg_t kArray(EngineState *s, int argc, reg_t *argv) {
- uint16 op = argv[0].toUint16();
-
- // Use kString when accessing strings
- // This is possible, as strings inherit from arrays
- // and in this case (type 3) arrays are of type char *.
- // kString is almost exactly the same as kArray, so
- // this call is possible
- // TODO: we need to either merge SCI2 strings and
- // arrays together, and in the future merge them with
- // the SCI1 strings and arrays in the segment manager
- bool callStringFunc = false;
- if (op == 0) {
- // New, check if the target type is 3 (string)
- if (argv[2].toUint16() == 3)
- callStringFunc = true;
- } else {
- if (s->_segMan->getSegmentType(argv[1].getSegment()) == SEG_TYPE_STRING ||
- s->_segMan->getSegmentType(argv[1].getSegment()) == SEG_TYPE_SCRIPT) {
- callStringFunc = true;
- }
-
-#if 0
- if (op == 6) {
- if (s->_segMan->getSegmentType(argv[3].getSegment()) == SEG_TYPE_STRING ||
- s->_segMan->getSegmentType(argv[3].getSegment()) == SEG_TYPE_SCRIPT) {
- callStringFunc = true;
- }
- }
-#endif
- }
-
- if (callStringFunc) {
- Kernel *kernel = g_sci->getKernel();
- uint16 kernelStringFuncId = kernel->_kernelFunc_StringId;
- if (kernelStringFuncId) {
- const KernelFunction *kernelStringFunc = &kernel->_kernelFuncs[kernelStringFuncId];
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
- if (op < kernelStringFunc->subFunctionCount) {
- // subfunction-id is valid
- const KernelSubFunction *kernelStringSubCall = &kernelStringFunc->subFunctions[op];
- argc--;
- argv++; // remove subfunction-id from arguments
- // and call the kString subfunction
- return kernelStringSubCall->function(s, argc, argv);
- }
- }
- }
+reg_t kArrayNew(EngineState *s, int argc, reg_t *argv) {
+ uint16 size = argv[0].toUint16();
+ const SciArrayType type = (SciArrayType)argv[1].toUint16();
- switch (op) {
- case 0: { // New
- reg_t arrayHandle;
- SciArray<reg_t> *array = s->_segMan->allocateArray(&arrayHandle);
- array->setType(argv[2].toUint16());
- array->setSize(argv[1].toUint16());
- return arrayHandle;
- }
- case 1: { // Size
- SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
- return make_reg(0, array->getSize());
- }
- case 2: { // At (return value at an index)
- SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
- if (g_sci->getGameId() == GID_PHANTASMAGORIA2) {
- // HACK: Phantasmagoria 2 keeps trying to access past the end of an
- // array when it starts. I'm assuming it's trying to see where the
- // array ends, or tries to resize it. Adjust the array size
- // accordingly, and return NULL for now.
- if (array->getSize() == argv[2].toUint16()) {
- array->setSize(argv[2].toUint16());
- return NULL_REG;
- }
- }
- return array->getValue(argv[2].toUint16());
+ if (type == kArrayTypeString) {
+ ++size;
}
- case 3: { // Atput (put value at an index)
- SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
-
- uint32 index = argv[2].toUint16();
- uint32 count = argc - 3;
- if (index + count > 65535)
- break;
+ reg_t arrayHandle;
+ s->_segMan->allocateArray(type, size, &arrayHandle);
+ return arrayHandle;
+}
- if (array->getSize() < index + count)
- array->setSize(index + count);
+reg_t kArrayGetSize(EngineState *s, int argc, reg_t *argv) {
+ const SciArray &array = *s->_segMan->lookupArray(argv[0]);
+ return make_reg(0, array.size());
+}
- for (uint16 i = 0; i < count; i++)
- array->setValue(i + index, argv[i + 3]);
+reg_t kArrayGetElement(EngineState *s, int argc, reg_t *argv) {
+ SciArray &array = *s->_segMan->lookupArray(argv[0]);
+ return array.getAsID(argv[1].toUint16());
+}
- return argv[1]; // We also have to return the handle
- }
- case 4: // Free
- // Freeing of arrays is handled by the garbage collector
- return s->r_acc;
- case 5: { // Fill
- SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
- uint16 index = argv[2].toUint16();
+reg_t kArraySetElements(EngineState *s, int argc, reg_t *argv) {
+ SciArray &array = *s->_segMan->lookupArray(argv[0]);
+ array.setElements(argv[1].toUint16(), argc - 2, argv + 2);
+ return argv[0];
+}
- // A count of -1 means fill the rest of the array
- uint16 count = argv[3].toSint16() == -1 ? array->getSize() - index : argv[3].toUint16();
- uint16 arraySize = array->getSize();
+reg_t kArrayFree(EngineState *s, int argc, reg_t *argv) {
+ s->_segMan->freeArray(argv[0]);
+ return s->r_acc;
+}
- if (arraySize < index + count)
- array->setSize(index + count);
+reg_t kArrayFill(EngineState *s, int argc, reg_t *argv) {
+ SciArray &array = *s->_segMan->lookupArray(argv[0]);
+ array.fill(argv[1].toUint16(), argv[2].toUint16(), argv[3]);
+ return argv[0];
+}
- for (uint16 i = 0; i < count; i++)
- array->setValue(i + index, argv[4]);
+reg_t kArrayCopy(EngineState *s, int argc, reg_t *argv) {
+ SciArray &target = *s->_segMan->lookupArray(argv[0]);
+ const uint16 targetIndex = argv[1].toUint16();
- return argv[1];
+ SciArray source;
+ // String copies may be made from static script data
+ if (!s->_segMan->isArray(argv[2])) {
+ source.setType(kArrayTypeString);
+ source.fromString(s->_segMan->getString(argv[2]));
+ } else {
+ source = *s->_segMan->lookupArray(argv[2]);
}
- case 6: { // Cpy
- if (argv[1].isNull() || argv[3].isNull()) {
- if (getSciVersion() == SCI_VERSION_3) {
- // FIXME: Happens in SCI3, probably because of a missing kernel function.
- warning("kArray(Cpy): Request to copy from or to a null pointer");
- return NULL_REG;
- } else {
- // SCI2-2.1: error out
- error("kArray(Cpy): Request to copy from or to a null pointer");
- }
- }
-
- reg_t arrayHandle = argv[1];
- SciArray<reg_t> *array1 = s->_segMan->lookupArray(argv[1]);
- SciArray<reg_t> *array2 = s->_segMan->lookupArray(argv[3]);
- uint32 index1 = argv[2].toUint16();
- uint32 index2 = argv[4].toUint16();
-
- // The original engine ignores bad copies too
- if (index2 > array2->getSize())
- break;
-
- // A count of -1 means fill the rest of the array
- uint32 count = argv[5].toSint16() == -1 ? array2->getSize() - index2 : argv[5].toUint16();
+ const uint16 sourceIndex = argv[3].toUint16();
+ const uint16 count = argv[4].toUint16();
- if (array1->getSize() < index1 + count)
- array1->setSize(index1 + count);
+ target.copy(source, sourceIndex, targetIndex, count);
+ return argv[0];
+}
- for (uint16 i = 0; i < count; i++)
- array1->setValue(i + index1, array2->getValue(i + index2));
+reg_t kArrayDuplicate(EngineState *s, int argc, reg_t *argv) {
+ reg_t targetHandle;
- return arrayHandle;
+ // String duplicates may be made from static script data
+ if (!s->_segMan->isArray(argv[0])) {
+ const Common::String source = s->_segMan->getString(argv[0]);
+ SciArray &target = *s->_segMan->allocateArray(kArrayTypeString, source.size(), &targetHandle);
+ target.fromString(source);
+ } else {
+ SciArray &source = *s->_segMan->lookupArray(argv[0]);
+ SciArray &target = *s->_segMan->allocateArray(source.getType(), source.size(), &targetHandle);
+ target = source;
}
- case 7: // Cmp
- // Not implemented in SSCI
- warning("kArray(Cmp) called");
- return s->r_acc;
- case 8: { // Dup
- if (argv[1].isNull()) {
- warning("kArray(Dup): Request to duplicate a null pointer");
-#if 0
- // Allocate an array anyway
- reg_t arrayHandle;
- SciArray<reg_t> *dupArray = s->_segMan->allocateArray(&arrayHandle);
- dupArray->setType(3);
- dupArray->setSize(0);
- return arrayHandle;
-#endif
- return NULL_REG;
- }
- SegmentObj *sobj = s->_segMan->getSegmentObj(argv[1].getSegment());
- if (!sobj || sobj->getType() != SEG_TYPE_ARRAY)
- error("kArray(Dup): Request to duplicate a segment which isn't an array");
- reg_t arrayHandle;
- SciArray<reg_t> *dupArray = s->_segMan->allocateArray(&arrayHandle);
- // This must occur after allocateArray, as inserting a new object
- // in the heap object list might invalidate this pointer. Also refer
- // to the same issue in kClone()
- SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
-
- dupArray->setType(array->getType());
- dupArray->setSize(array->getSize());
-
- for (uint32 i = 0; i < array->getSize(); i++)
- dupArray->setValue(i, array->getValue(i));
-
- return arrayHandle;
- }
- case 9: // Getdata
- if (!s->_segMan->isHeapObject(argv[1]))
- return argv[1];
+ return targetHandle;
+}
- return readSelector(s->_segMan, argv[1], SELECTOR(data));
- default:
- error("Unknown kArray subop %d", op);
+reg_t kArrayGetData(EngineState *s, int argc, reg_t *argv) {
+ if (s->_segMan->isObject(argv[0])) {
+ return readSelector(s->_segMan, argv[0], SELECTOR(data));
}
- return NULL_REG;
+ return argv[0];
}
+reg_t kArrayByteCopy(EngineState *s, int argc, reg_t *argv) {
+ SciArray &target = *s->_segMan->lookupArray(argv[0]);
+ const uint16 targetOffset = argv[1].toUint16();
+ const SciArray &source = *s->_segMan->lookupArray(argv[2]);
+ const uint16 sourceOffset = argv[3].toUint16();
+ const uint16 count = argv[4].toUint16();
+
+ target.byteCopy(source, sourceOffset, targetOffset, count);
+ return argv[0];
+}
#endif
} // End of namespace Sci