void MessageState::outputString(reg_t buf, const Common::String &str) { #ifdef ENABLE_SCI32 if (getSciVersion() >= SCI_VERSION_2) { if (_segMan->getSegmentType(buf.getSegment()) == SEG_TYPE_STRING) { SciString *sciString = _segMan->lookupString(buf); sciString->setSize(str.size() + 1); for (uint32 i = 0; i < str.size(); i++) sciString->setValue(i, str.c_str()[i]); sciString->setValue(str.size(), 0); } else if (_segMan->getSegmentType(buf.getSegment()) == SEG_TYPE_ARRAY) { // Happens in the intro of LSL6, we are asked to write the string // into an array SciArray<reg_t> *sciString = _segMan->lookupArray(buf); sciString->setSize(str.size() + 1); for (uint32 i = 0; i < str.size(); i++) sciString->setValue(i, make_reg(0, str.c_str()[i])); sciString->setValue(str.size(), NULL_REG); } } else { #endif SegmentRef buffer_r = _segMan->dereference(buf); if ((unsigned)buffer_r.maxSize >= str.size() + 1) { _segMan->strcpy(buf, str.c_str()); } else { // LSL6 sets an exit text here, but the buffer size allocated // is too small. Don't display a warning in this case, as we // don't use the exit text anyway - bug report #3035533 if (g_sci->getGameId() == GID_LSL6 && str.hasPrefix("\r\n(c) 1993 Sierra On-Line, Inc")) { // LSL6 buggy exit text, don't show warning } else { warning("Message: buffer %04x:%04x invalid or too small to hold the following text of %i bytes: '%s'", PRINT_REG(buf), str.size() + 1, str.c_str()); } // Set buffer to empty string if possible if (buffer_r.maxSize > 0) _segMan->strcpy(buf, ""); } #ifdef ENABLE_SCI32 } #endif }
reg_t kMakeSaveFileName(EngineState *s, int argc, reg_t *argv) { // Creates a savegame name from a slot number. Used when deleting saved games. // Param 0: the output buffer (same as in kMakeSaveCatName) // Param 1: a string with game parameters, ignored // Param 2: the selected slot SciString *resultString = s->_segMan->lookupString(argv[0]); uint16 virtualId = argv[2].toUint16(); if ((virtualId < SAVEGAMEID_OFFICIALRANGE_START) || (virtualId > SAVEGAMEID_OFFICIALRANGE_END)) error("kMakeSaveFileName: invalid savegame ID specified"); uint saveSlot = virtualId - SAVEGAMEID_OFFICIALRANGE_START; Common::Array<SavegameDesc> saves; listSavegames(saves); Common::String filename = g_sci->getSavegameName(saveSlot); resultString->fromString(filename); return argv[0]; }
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 if (op == 0) { // New, check if the target type is 3 (string) if (argv[2].toUint16() == 3) return kString(s, argc, argv); } else { if (s->_segMan->getSegmentType(argv[1].segment) == SEG_TYPE_STRING || s->_segMan->getSegmentType(argv[1].segment) == SEG_TYPE_SCRIPT) { return kString(s, argc, argv); } #if 0 if (op == 6) { if (s->_segMan->getSegmentType(argv[3].segment) == SEG_TYPE_STRING || s->_segMan->getSegmentType(argv[3].segment) == SEG_TYPE_SCRIPT) { return kString(s, argc, argv); } } #endif } 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]); return array->getValue(argv[2].toUint16()); } 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; if (array->getSize() < index + count) array->setSize(index + count); for (uint16 i = 0; i < count; i++) array->setValue(i + index, argv[i + 3]); 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(); // 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(); if (arraySize < index + count) array->setSize(index + count); for (uint16 i = 0; i < count; i++) array->setValue(i + index, argv[4]); return argv[1]; } 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> *array1 = !argv[1].isNull() ? s->_segMan->lookupArray(argv[1]) : s->_segMan->allocateArray(&arrayHandle); 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(); if (array1->getSize() < index1 + count) array1->setSize(index1 + count); for (uint16 i = 0; i < count; i++) array1->setValue(i + index1, array2->getValue(i + index2)); return arrayHandle; } 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; } SegmentType sourceType = s->_segMan->getSegmentObj(argv[1].segment)->getType(); if (sourceType == SEG_TYPE_SCRIPT) { // A technique used in later SCI2.1 and SCI3 games: the contents of a script // are loaded in an array (well, actually a string). Script *scr = s->_segMan->getScript(argv[1].segment); reg_t stringHandle; SciString *dupString = s->_segMan->allocateString(&stringHandle); dupString->setSize(scr->getBufSize()); dupString->fromString(Common::String((const char *)scr->getBuf())); return stringHandle; } else if (sourceType != SEG_TYPE_ARRAY && sourceType != SEG_TYPE_SCRIPT) { error("kArray(Dup): Request to duplicate a segment which isn't an array or a script"); } 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 readSelector(s->_segMan, argv[1], SELECTOR(data)); default: error("Unknown kArray subop %d", op); } return NULL_REG; }