diff options
Diffstat (limited to 'engines/sci/engine/klists.cpp')
-rw-r--r-- | engines/sci/engine/klists.cpp | 454 |
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 |