diff options
author | Alejandro Marzini | 2010-06-08 03:31:27 +0000 |
---|---|---|
committer | Alejandro Marzini | 2010-06-08 03:31:27 +0000 |
commit | 7ea78b10364d34ae607a9a1da00e4d42ad691aa1 (patch) | |
tree | c16c3d224b2ad1179dbf3e7c54b5c0212b0581a4 /engines/sci/engine | |
parent | 7e9d54a69a3444f5335b8cf6ecabdeffe2830644 (diff) | |
parent | ea2e2053f25c216342c74bb7a74dabc682766720 (diff) | |
download | scummvm-rg350-7ea78b10364d34ae607a9a1da00e4d42ad691aa1.tar.gz scummvm-rg350-7ea78b10364d34ae607a9a1da00e4d42ad691aa1.tar.bz2 scummvm-rg350-7ea78b10364d34ae607a9a1da00e4d42ad691aa1.zip |
Merged from trunk.
svn-id: r49499
Diffstat (limited to 'engines/sci/engine')
29 files changed, 1394 insertions, 1478 deletions
diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp index e51867332a..48f7c2d64f 100644 --- a/engines/sci/engine/features.cpp +++ b/engines/sci/engine/features.cpp @@ -56,7 +56,7 @@ reg_t GameFeatures::getDetectionAddr(const Common::String &objName, Selector slc } if (methodNum == -1) { - if (lookup_selector(_segMan, objAddr, slc, NULL, &addr) != kSelectorMethod) { + if (lookupSelector(_segMan, objAddr, slc, NULL, &addr) != kSelectorMethod) { warning("getDetectionAddr: target selector is not a method of object %s", objName.c_str()); return NULL_REG; } @@ -87,7 +87,7 @@ bool GameFeatures::autoDetectSoundType() { opcode = extOpcode >> 1; // Check for end of script - if (opcode == op_ret || offset >= script->_bufSize) + if (opcode == op_ret || offset >= script->getBufSize()) break; // The play method of the Sound object pushes the DoSound command @@ -189,7 +189,7 @@ SciVersion GameFeatures::detectSetCursorType() { } // Now we check what the number variable holds in the handCursor object. - uint16 number = GET_SEL32V(_segMan, objAddr, SELECTOR(number)); + uint16 number = readSelectorValue(_segMan, objAddr, SELECTOR(number)); // If the number is 0, it uses views and therefore the SCI1.1 kSetCursor semantics, // otherwise it uses the SCI0 early kSetCursor semantics. @@ -223,7 +223,7 @@ bool GameFeatures::autoDetectLofsType(int methodNum) { opcode = extOpcode >> 1; // Check for end of script - if (opcode == op_ret || offset >= script->_bufSize) + if (opcode == op_ret || offset >= script->getBufSize()) break; if (opcode == op_lofsa || opcode == op_lofss) { @@ -231,13 +231,13 @@ bool GameFeatures::autoDetectLofsType(int methodNum) { uint16 lofs = opparams[0]; // Check for going out of bounds when interpreting as abs/rel - if (lofs >= script->_bufSize) + if (lofs >= script->getBufSize()) _lofsType = SCI_VERSION_0_EARLY; if ((signed)offset + (int16)lofs < 0) _lofsType = SCI_VERSION_1_MIDDLE; - if ((signed)offset + (int16)lofs >= (signed)script->_bufSize) + if ((signed)offset + (int16)lofs >= (signed)script->getBufSize()) _lofsType = SCI_VERSION_1_MIDDLE; if (_lofsType != SCI_VERSION_NONE) @@ -266,7 +266,7 @@ SciVersion GameFeatures::detectLofsType() { // Find a function of the game object which invokes lofsa/lofss reg_t gameClass = _segMan->findObjectByName("Game"); - Object *obj = _segMan->getObject(gameClass); + const Object *obj = _segMan->getObject(gameClass); bool found = false; for (uint m = 0; m < obj->getMethodCount(); m++) { @@ -309,7 +309,7 @@ bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) { opcode = extOpcode >> 1; // Check for end of script - if (opcode == op_ret || offset >= script->_bufSize) + if (opcode == op_ret || offset >= script->getBufSize()) break; if (opcode == op_callk) { @@ -332,18 +332,47 @@ bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) { SciVersion GameFeatures::detectGfxFunctionsType() { if (_gfxFunctionsType == SCI_VERSION_NONE) { - // This detection only works (and is only needed) for SCI0 games - if (getSciVersion() >= SCI_VERSION_01) { + if (getSciVersion() == SCI_VERSION_0_EARLY) { + // Old SCI0 games always used old graphics functions + _gfxFunctionsType = SCI_VERSION_0_EARLY; + } else if (getSciVersion() >= SCI_VERSION_01) { + // SCI01 and newer games always used new graphics functions _gfxFunctionsType = SCI_VERSION_0_LATE; - } else if (getSciVersion() > SCI_VERSION_0_EARLY) { + } else { // SCI0 late // Check if the game is using an overlay - bool found = false; + bool searchRoomObj = false; + reg_t rmObjAddr = _segMan->findObjectByName("Rm"); + + if (_kernel->_selectorCache.overlay != -1) { + // The game has an overlay selector, check how it calls kDrawPicto determine + // the graphics functions type used + if (lookupSelector(_segMan, rmObjAddr, _kernel->_selectorCache.overlay, NULL, NULL) == kSelectorMethod) { + if (!autoDetectGfxFunctionsType()) { + warning("Graphics functions detection failed, taking an educated guess"); + + // Try detecting the graphics function types from the existence of the motionCue + // selector (which is a bit of a hack) + if (_kernel->findSelector("motionCue") != -1) + _gfxFunctionsType = SCI_VERSION_0_LATE; + else + _gfxFunctionsType = SCI_VERSION_0_EARLY; + } + } else { + // The game has an overlay selector, but it's not a method of the Rm object + // (like in Hoyle 1 and 2), so search for other methods + searchRoomObj = true; + } + } else { + // The game doesn't have an overlay selector, so search for it manually + searchRoomObj = true; + } - if (_kernel->_selectorCache.overlay == -1) { - // No overlay selector found, check if any method of the Rm object - // is calling kDrawPic, as the overlay selector might be missing in demos + if (searchRoomObj) { + // If requested, check if any method of the Rm object is calling kDrawPic, + // as the overlay selector might be missing in demos + bool found = false; - Object *obj = _segMan->getObject(_segMan->findObjectByName("Rm")); + const Object *obj = _segMan->getObject(rmObjAddr); for (uint m = 0; m < obj->getMethodCount(); m++) { found = autoDetectGfxFunctionsType(m); if (found) @@ -351,30 +380,11 @@ SciVersion GameFeatures::detectGfxFunctionsType() { } if (!found) { - // No overlay selector found, therefore the game is definitely - // using old graphics functions + // No method of the Rm object is calling kDrawPic, thus the game + // doesn't have overlays and is using older graphics functions _gfxFunctionsType = SCI_VERSION_0_EARLY; } - } else { // _kernel->_selectorCache.overlay != -1 - // An in-between case: The game does not have a shiftParser - // selector, but it does have an overlay selector, so it uses an - // overlay. Therefore, check it to see how it calls kDrawPic to - // determine the graphics functions type used - - if (!autoDetectGfxFunctionsType()) { - warning("Graphics functions detection failed, taking an educated guess"); - - // Try detecting the graphics function types from the existence of the motionCue - // selector (which is a bit of a hack) - if (_kernel->findSelector("motionCue") != -1) - _gfxFunctionsType = SCI_VERSION_0_LATE; - else - _gfxFunctionsType = SCI_VERSION_0_EARLY; - } } - } else { // (getSciVersion() == SCI_VERSION_0_EARLY) - // Old SCI0 games always used old graphics functions - _gfxFunctionsType = SCI_VERSION_0_EARLY; } debugC(1, kDebugLevelVM, "Detected graphics functions type: %s", getSciVersionDesc(_gfxFunctionsType)); @@ -402,7 +412,7 @@ bool GameFeatures::autoDetectSci21KernelType() { opcode = extOpcode >> 1; // Check for end of script - if (opcode == op_ret || offset >= script->_bufSize) + if (opcode == op_ret || offset >= script->getBufSize()) break; if (opcode == op_callk) { @@ -455,7 +465,7 @@ bool GameFeatures::autoDetectMoveCountType() { opcode = extOpcode >> 1; // Check for end of script - if (opcode == op_ret || offset >= script->_bufSize) + if (opcode == op_ret || offset >= script->getBufSize()) break; if (opcode == op_callk) { diff --git a/engines/sci/engine/game.cpp b/engines/sci/engine/game.cpp index d7fdd9be6e..bc10099e52 100644 --- a/engines/sci/engine/game.cpp +++ b/engines/sci/engine/game.cpp @@ -67,13 +67,7 @@ int script_init_engine(EngineState *s) { s->script_000 = s->_segMan->getScript(script_000_segment); - s->sys_strings = s->_segMan->allocateSysStrings(&s->sys_strings_segment); - - // Allocate static buffer for savegame and CWD directories - SystemString *str = &s->sys_strings->_strings[SYS_STRING_SAVEDIR]; - str->_name = "savedir"; - str->_maxSize = MAX_SAVE_DIR_SIZE; - str->_value = (char *)calloc(MAX_SAVE_DIR_SIZE, sizeof(char)); + s->_segMan->initSysStrings(); s->r_acc = s->r_prev = NULL_REG; s->restAdjust = 0; @@ -105,31 +99,26 @@ int game_init(EngineState *s) { return 1; } - if (s->_voc) { - s->_voc->parserIsValid = false; // Invalidate parser - s->_voc->parser_event = NULL_REG; // Invalidate parser event - s->_voc->parser_base = make_reg(s->sys_strings_segment, SYS_STRING_PARSER_BASE); + // Reset parser + Vocabulary *voc = g_sci->getVocabulary(); + if (voc) { + voc->parserIsValid = false; // Invalidate parser + voc->parser_event = NULL_REG; // Invalidate parser event + voc->parser_base = make_reg(s->_segMan->getSysStringsSegment(), SYS_STRING_PARSER_BASE); } // Initialize menu TODO: Actually this should be another init() if (g_sci->_gfxMenu) g_sci->_gfxMenu->reset(); - s->successor = NULL; // No successor - - SystemString *str = &s->sys_strings->_strings[SYS_STRING_PARSER_BASE]; - str->_name = "parser-base"; - str->_maxSize = MAX_PARSER_BASE; - str->_value = (char *)calloc(MAX_PARSER_BASE, sizeof(char)); + s->restoring = false; s->game_start_time = g_system->getMillis(); s->last_wait_time = s->game_start_time; srand(g_system->getMillis()); // Initialize random number generator -// script_dissect(0, s->_selectorNames); - // The first entry in the export table of script 0 points to the game object - s->_gameObj = s->_segMan->lookupScriptExport(0, 0); + s->_gameObj = g_sci->getResMan()->findGameObject(); #ifdef USE_OLD_MUSIC_FUNCTIONS if (s->sfx_init_flags & SFX_STATE_FLAG_NOSOUND) @@ -145,9 +134,8 @@ int game_init(EngineState *s) { } int game_exit(EngineState *s) { - s->_executionStack.clear(); - - if (!s->successor) { + if (!s->restoring) { + s->_executionStack.clear(); #ifdef USE_OLD_MUSIC_FUNCTIONS s->_sound.sfx_exit(); // Reinit because some other code depends on having a valid state diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp index 49266f3a18..6ebee2dfbd 100644 --- a/engines/sci/engine/kernel.cpp +++ b/engines/sci/engine/kernel.cpp @@ -332,11 +332,12 @@ SciKernelFunction kfunct_mappers[] = { DEFUN("DoSync", kDoSync, ".*"), DEFUN("MemorySegment", kMemorySegment, "iri*"), DEFUN("Intersections", kIntersections, "iiiiriiiri"), + DEFUN("MergePoly", kMergePoly, "rli"), DEFUN("ResCheck", kResCheck, "iii*"), DEFUN("SetQuitStr", kSetQuitStr, "r"), DEFUN("ShowMovie", kShowMovie, ".*"), DEFUN("SetVideoMode", kSetVideoMode, "i"), - DEFUN("Platform", kPlatform, "i.*"), + DEFUN("Platform", kPlatform, ".*"), DEFUN("TextColors", kTextColors, ".*"), DEFUN("TextFonts", kTextFonts, ".*"), DEFUN("Portrait", kPortrait, ".*"), @@ -362,7 +363,7 @@ SciKernelFunction kfunct_mappers[] = { DEFUN("ListIndexOf", kListIndexOf, "lZo"), DEFUN("OnMe", kOnMe, "iio.*"), DEFUN("InPolygon", kInPolygon, "iio"), - DEFUN("CreateTextBitmap", kCreateTextBitmap, "iiio"), + DEFUN("CreateTextBitmap", kCreateTextBitmap, "i.*"), // SCI2.1 Kernel Functions DEFUN("Save", kSave, ".*"), @@ -382,7 +383,6 @@ SciKernelFunction kfunct_mappers[] = { DEFUN("ShiftScreen", kShiftScreen, ".*"), DEFUN("ListOps", kListOps, ".*"), DEFUN("ATan", kATan, ".*"), - DEFUN("MergePoly", kMergePoly, ".*"), DEFUN("Record", kRecord, ".*"), DEFUN("PlayBack", kPlayBack, ".*"), DEFUN("DbugStr", kDbugStr, ".*"), @@ -628,7 +628,7 @@ int Kernel::findRegType(reg_t reg) { switch (mobj->getType()) { case SEG_TYPE_SCRIPT: - if (reg.offset <= (*(Script *)mobj)._bufSize && + if (reg.offset <= (*(Script *)mobj).getBufSize() && reg.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET && RAW_IS_OBJECT((*(Script *)mobj)._buf + reg.offset)) { return ((Script *)mobj)->getObject(reg.offset) ? KSIG_OBJECT : KSIG_REF; diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index 7717743e19..8f8f34f74e 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -414,6 +414,7 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv); reg_t kDoSync(EngineState *s, int argc, reg_t *argv); reg_t kMemorySegment(EngineState *s, int argc, reg_t *argv); reg_t kIntersections(EngineState *s, int argc, reg_t *argv); +reg_t kMergePoly(EngineState *s, int argc, reg_t *argv); reg_t kResCheck(EngineState *s, int argc, reg_t *argv); reg_t kSetQuitStr(EngineState *s, int argc, reg_t *argv); reg_t kShowMovie(EngineState *s, int argc, reg_t *argv); diff --git a/engines/sci/engine/kernel32.cpp b/engines/sci/engine/kernel32.cpp index 465e0e92df..0afdc3f2eb 100644 --- a/engines/sci/engine/kernel32.cpp +++ b/engines/sci/engine/kernel32.cpp @@ -501,7 +501,7 @@ reg_t kArray(EngineState *s, int argc, reg_t *argv) { if (!s->_segMan->isHeapObject(argv[1])) return argv[1]; - return GET_SEL32(s->_segMan, argv[1], SELECTOR(data)); + return readSelector(s->_segMan, argv[1], SELECTOR(data)); default: error("Unknown kArray subop %d", argv[0].toUint16()); } @@ -524,8 +524,12 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) { } case 1: // Size return make_reg(0, s->_segMan->getString(argv[1]).size()); - case 2: // At (return value at an index) + case 2: { // At (return value at an index) + if (argv[1].segment == s->_segMan->getStringSegmentId()) + return make_reg(0, s->_segMan->lookupString(argv[1])->getRawData()[argv[2].toUint16()]); + return make_reg(0, s->_segMan->getString(argv[1])[argv[2].toUint16()]); + } case 3: { // Atput (put value at an index) SciString *string = s->_segMan->lookupString(argv[1]); @@ -563,28 +567,40 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) { return argv[1]; } case 6: { // Cpy - Common::String string2 = s->_segMan->getString(argv[3]); + const char *string2 = 0; + uint32 string2Size = 0; + + if (argv[3].segment == s->_segMan->getStringSegmentId()) { + SciString *string = s->_segMan->lookupString(argv[3]); + string2 = string->getRawData(); + string2Size = string->getSize(); + } else { + Common::String string = s->_segMan->getString(argv[3]); + string2 = string.c_str(); + string2Size = string.size() + 1; + } + uint32 index1 = argv[2].toUint16(); uint32 index2 = argv[4].toUint16(); // The original engine ignores bad copies too - if (index2 > string2.size()) + if (index2 > string2Size) break; // A count of -1 means fill the rest of the array - uint32 count = argv[5].toSint16() == -1 ? string2.size() - index2 + 1 : argv[5].toUint16(); + uint32 count = argv[5].toSint16() == -1 ? string2Size - index2 + 1 : argv[5].toUint16(); // We have a special case here for argv[1] being a system string - if (argv[1].segment == s->sys_strings_segment) { + if (argv[1].segment == s->_segMan->getSysStringsSegment()) { // Resize if necessary - if ((uint32)s->sys_strings->_strings[argv[1].toUint16()]._maxSize < index1 + count) { - delete[] s->sys_strings->_strings[argv[1].toUint16()]._value; - s->sys_strings->_strings[argv[1].toUint16()]._maxSize = index1 + count; - s->sys_strings->_strings[argv[1].toUint16()]._value = new char[index1 + count]; - memset(s->sys_strings->_strings[argv[1].toUint16()]._value, 0, index1 + count); + const uint16 sysStringId = argv[1].toUint16(); + if ((uint32)s->_segMan->sysStrings->_strings[sysStringId]._maxSize < index1 + count) { + free(s->_segMan->sysStrings->_strings[sysStringId]._value); + s->_segMan->sysStrings->_strings[sysStringId]._maxSize = index1 + count; + s->_segMan->sysStrings->_strings[sysStringId]._value = (char *)calloc(index1 + count, sizeof(char)); } - strncpy(s->sys_strings->_strings[argv[1].toUint16()]._value + index1, string2.c_str() + index2, count); + strncpy(s->_segMan->sysStrings->_strings[sysStringId]._value + index1, string2 + index2, count); } else { SciString *string1 = s->_segMan->lookupString(argv[1]); @@ -594,7 +610,7 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) { // Note: We're accessing from c_str() here because the string's size ignores // the trailing 0 and therefore triggers an assert when doing string2[i + index2]. for (uint16 i = 0; i < count; i++) - string1->setValue(i + index1, string2.c_str()[i + index2]); + string1->setValue(i + index1, string2[i + index2]); } } return argv[1]; @@ -608,15 +624,25 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) { return make_reg(0, strcmp(string1.c_str(), string2.c_str())); } case 8: { // Dup - Common::String string = s->_segMan->getString(argv[1]); + const char *rawString = 0; + uint32 size = 0; + + if (argv[1].segment == s->_segMan->getStringSegmentId()) { + SciString *string = s->_segMan->lookupString(argv[1]); + rawString = string->getRawData(); + size = string->getSize(); + } else { + Common::String string = s->_segMan->getString(argv[1]); + rawString = string.c_str(); + size = string.size() + 1; + } + reg_t stringHandle; SciString *dupString = s->_segMan->allocateString(&stringHandle); - dupString->setSize(string.size() + 1); + dupString->setSize(size); - for (uint32 i = 0; i < string.size(); i++) - dupString->setValue(i, string.c_str()[i]); - - dupString->setValue(dupString->getSize() - 1, 0); + for (uint32 i = 0; i < size; i++) + dupString->setValue(i, rawString[i]); return stringHandle; } @@ -624,7 +650,7 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) { if (!s->_segMan->isHeapObject(argv[1])) return argv[1]; - return GET_SEL32(s->_segMan, argv[1], SELECTOR(data)); + return readSelector(s->_segMan, argv[1], SELECTOR(data)); case 10: // Stringlen return make_reg(0, s->_segMan->strlen(argv[1])); case 11: { // Printf @@ -689,12 +715,12 @@ reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv) { /* reg_t viewObj = argv[0]; - uint16 viewId = GET_SEL32V(s->_segMan, viewObj, SELECTOR(view)); - int16 loopNo = GET_SEL32V(s->_segMan, viewObj, SELECTOR(loop)); - int16 celNo = GET_SEL32V(s->_segMan, viewObj, SELECTOR(cel)); + uint16 viewId = readSelectorValue(s->_segMan, viewObj, SELECTOR(view)); + int16 loopNo = readSelectorValue(s->_segMan, viewObj, SELECTOR(loop)); + int16 celNo = readSelectorValue(s->_segMan, viewObj, SELECTOR(cel)); //int16 leftPos = 0; //int16 topPos = 0; - int16 priority = GET_SEL32V(s->_segMan, viewObj, SELECTOR(priority)); + int16 priority = readSelectorValue(s->_segMan, viewObj, SELECTOR(priority)); //int16 control = 0; */ @@ -761,10 +787,10 @@ reg_t kOnMe(EngineState *s, int argc, reg_t *argv) { Common::Rect nsRect; // Get the bounding rectangle of the object - nsRect.left = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsLeft)); - nsRect.top = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsTop)); - nsRect.right = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsRight)); - nsRect.bottom = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsBottom)); + nsRect.left = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsLeft)); + nsRect.top = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsTop)); + nsRect.right = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsRight)); + nsRect.bottom = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsBottom)); //warning("kOnMe: (%d, %d) on object %04x:%04x, parameter %d", argv[0].toUint16(), argv[1].toUint16(), PRINT_REG(argv[2]), argv[3].toUint16()); @@ -778,9 +804,16 @@ reg_t kInPolygon(EngineState *s, int argc, reg_t *argv) { reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) { // TODO: argument 0 is usually 0, and arguments 1 and 2 are usually 1 - reg_t object = argv[3]; - Common::String text = s->_segMan->getString(GET_SEL32(s->_segMan, object, SELECTOR(text))); - debug("kCreateTextBitmap: %s", text.c_str()); + switch (argv[0].toUint16()) { + case 0: + if (argc != 4) { + warning("kCreateTextBitmap(0): expected 4 arguments, got %i", argc); + return NULL_REG; + } + reg_t object = argv[3]; + Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text))); + debug("kCreateTextBitmap: %s", text.c_str()); + } return NULL_REG; } diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp index 156035b30d..fd7711f196 100644 --- a/engines/sci/engine/kevent.cpp +++ b/engines/sci/engine/kevent.cpp @@ -49,16 +49,18 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; Common::Point mousePos; + // Limit the mouse cursor position, if necessary + g_sci->_gfxCursor->refreshPosition(); mousePos = g_sci->_gfxCursor->getPosition(); // If there's a simkey pending, and the game wants a keyboard event, use the // simkey instead of a normal event if (g_debug_simulated_key && (mask & SCI_EVENT_KEYBOARD)) { - PUT_SEL32V(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event - PUT_SEL32V(segMan, obj, SELECTOR(message), g_debug_simulated_key); - PUT_SEL32V(segMan, obj, SELECTOR(modifiers), SCI_KEYMOD_NUMLOCK); // Numlock on - PUT_SEL32V(segMan, obj, SELECTOR(x), mousePos.x); - PUT_SEL32V(segMan, obj, SELECTOR(y), mousePos.y); + writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event + writeSelectorValue(segMan, obj, SELECTOR(message), g_debug_simulated_key); + writeSelectorValue(segMan, obj, SELECTOR(modifiers), SCI_KEYMOD_NUMLOCK); // Numlock on + writeSelectorValue(segMan, obj, SELECTOR(x), mousePos.x); + writeSelectorValue(segMan, obj, SELECTOR(y), mousePos.y); g_debug_simulated_key = 0; return make_reg(0, 1); } @@ -67,26 +69,26 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { oldy = mousePos.y; curEvent = s->_event->get(mask); - if (s->_voc) - s->_voc->parser_event = NULL_REG; // Invalidate parser event + if (g_sci->getVocabulary()) + g_sci->getVocabulary()->parser_event = NULL_REG; // Invalidate parser event - PUT_SEL32V(segMan, obj, SELECTOR(x), mousePos.x); - PUT_SEL32V(segMan, obj, SELECTOR(y), mousePos.y); + writeSelectorValue(segMan, obj, SELECTOR(x), mousePos.x); + writeSelectorValue(segMan, obj, SELECTOR(y), mousePos.y); //s->_gui->moveCursor(s->gfx_state->pointer_pos.x, s->gfx_state->pointer_pos.y); switch (curEvent.type) { case SCI_EVENT_QUIT: - quit_vm(); + quit_vm(s); break; case SCI_EVENT_KEYBOARD: - PUT_SEL32V(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event + writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event s->r_acc = make_reg(0, 1); - PUT_SEL32V(segMan, obj, SELECTOR(message), curEvent.character); + writeSelectorValue(segMan, obj, SELECTOR(message), curEvent.character); // We only care about the translated character - PUT_SEL32V(segMan, obj, SELECTOR(modifiers), curEvent.modifiers & modifier_mask); + writeSelectorValue(segMan, obj, SELECTOR(modifiers), curEvent.modifiers & modifier_mask); break; case SCI_EVENT_MOUSE_RELEASE: @@ -111,9 +113,9 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { break; } - PUT_SEL32V(segMan, obj, SELECTOR(type), curEvent.type); - PUT_SEL32V(segMan, obj, SELECTOR(message), 0); - PUT_SEL32V(segMan, obj, SELECTOR(modifiers), (curEvent.modifiers | extra_bits) & modifier_mask); + writeSelectorValue(segMan, obj, SELECTOR(type), curEvent.type); + writeSelectorValue(segMan, obj, SELECTOR(message), 0); + writeSelectorValue(segMan, obj, SELECTOR(modifiers), (curEvent.modifiers | extra_bits) & modifier_mask); s->r_acc = make_reg(0, 1); } break; @@ -165,9 +167,9 @@ reg_t kMapKeyToDir(EngineState *s, int argc, reg_t *argv) { reg_t obj = argv[0]; SegManager *segMan = s->_segMan; - if (GET_SEL32V(segMan, obj, SELECTOR(type)) == SCI_EVENT_KEYBOARD) { // Keyboard + if (readSelectorValue(segMan, obj, SELECTOR(type)) == SCI_EVENT_KEYBOARD) { // Keyboard int mover = -1; - switch (GET_SEL32V(segMan, obj, SELECTOR(message))) { + switch (readSelectorValue(segMan, obj, SELECTOR(message))) { case SCI_KEY_HOME: mover = 8; break; @@ -201,8 +203,8 @@ reg_t kMapKeyToDir(EngineState *s, int argc, reg_t *argv) { } if (mover >= 0) { - PUT_SEL32V(segMan, obj, SELECTOR(type), SCI_EVENT_JOYSTICK); - PUT_SEL32V(segMan, obj, SELECTOR(message), mover); + writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_JOYSTICK); + writeSelectorValue(segMan, obj, SELECTOR(message), mover); return make_reg(0, 1); } else return NULL_REG; @@ -217,13 +219,13 @@ reg_t kGlobalToLocal(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; if (obj.segment) { - int16 x = GET_SEL32V(segMan, obj, SELECTOR(x)); - int16 y = GET_SEL32V(segMan, obj, SELECTOR(y)); + int16 x = readSelectorValue(segMan, obj, SELECTOR(x)); + int16 y = readSelectorValue(segMan, obj, SELECTOR(y)); g_sci->_gfxCoordAdjuster->kernelGlobalToLocal(x, y, planeObject); - PUT_SEL32V(segMan, obj, SELECTOR(x), x); - PUT_SEL32V(segMan, obj, SELECTOR(y), y); + writeSelectorValue(segMan, obj, SELECTOR(x), x); + writeSelectorValue(segMan, obj, SELECTOR(y), y); } return s->r_acc; @@ -236,13 +238,13 @@ reg_t kLocalToGlobal(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; if (obj.segment) { - int16 x = GET_SEL32V(segMan, obj, SELECTOR(x)); - int16 y = GET_SEL32V(segMan, obj, SELECTOR(y)); + int16 x = readSelectorValue(segMan, obj, SELECTOR(x)); + int16 y = readSelectorValue(segMan, obj, SELECTOR(y)); g_sci->_gfxCoordAdjuster->kernelLocalToGlobal(x, y, planeObject); - PUT_SEL32V(segMan, obj, SELECTOR(x), x); - PUT_SEL32V(segMan, obj, SELECTOR(y), y); + writeSelectorValue(segMan, obj, SELECTOR(x), x); + writeSelectorValue(segMan, obj, SELECTOR(y), y); } return s->r_acc; diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp index e6b9a5388c..3e0ecd1a28 100644 --- a/engines/sci/engine/kfile.cpp +++ b/engines/sci/engine/kfile.cpp @@ -44,6 +44,8 @@ struct SavegameDesc { int id; int date; int time; + int version; + char name[SCI_MAX_SAVENAME_LENGTH]; }; /* @@ -245,13 +247,10 @@ static void fgets_wrapper(EngineState *s, char *dest, int maxsize, int handle) { debugC(2, kDebugLevelFile, "FGets'ed \"%s\"", dest); } -static int _savegame_index_struct_compare(const void *a, const void *b) { - const SavegameDesc *A = (const SavegameDesc *)a; - const SavegameDesc *B = (const SavegameDesc *)b; - - if (B->date != A->date) - return B->date - A->date; - return B->time - A->time; +static bool _savegame_index_struct_compare(const SavegameDesc &l, const SavegameDesc &r) { + if (l.date != r.date) + return (l.date > r.date); + return (l.time > r.time); } void listSavegames(Common::Array<SavegameDesc> &saves) { @@ -265,7 +264,7 @@ void listSavegames(Common::Array<SavegameDesc> &saves) { Common::SeekableReadStream *in; if ((in = saveFileMan->openForLoading(filename))) { SavegameMetadata meta; - if (!get_savegame_metadata(in, &meta)) { + if (!get_savegame_metadata(in, &meta) || meta.savegame_name.empty()) { // invalid delete in; continue; @@ -278,6 +277,13 @@ void listSavegames(Common::Array<SavegameDesc> &saves) { // We need to fix date in here, because we save DDMMYYYY instead of YYYYMMDD, so sorting wouldnt work desc.date = ((desc.date & 0xFFFF) << 16) | ((desc.date & 0xFF0000) >> 8) | ((desc.date & 0xFF000000) >> 24); desc.time = meta.savegame_time; + desc.version = meta.savegame_version; + + if (meta.savegame_name.lastChar() == '\n') + meta.savegame_name.deleteLastChar(); + + Common::strlcpy(desc.name, meta.savegame_name.c_str(), SCI_MAX_SAVENAME_LENGTH); + debug(3, "Savegame in file %s ok, id %d", filename.c_str(), desc.id); saves.push_back(desc); @@ -285,35 +291,18 @@ void listSavegames(Common::Array<SavegameDesc> &saves) { } // Sort the list by creation date of the saves - qsort(saves.begin(), saves.size(), sizeof(SavegameDesc), _savegame_index_struct_compare); + Common::sort(saves.begin(), saves.end(), _savegame_index_struct_compare); } bool Console::cmdListSaves(int argc, const char **argv) { Common::Array<SavegameDesc> saves; listSavegames(saves); - Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager(); - for (uint i = 0; i < saves.size(); i++) { Common::String filename = g_sci->getSavegameName(saves[i].id); - Common::SeekableReadStream *in; - if ((in = saveFileMan->openForLoading(filename))) { - SavegameMetadata meta; - if (!get_savegame_metadata(in, &meta)) { - // invalid - delete in; - continue; - } - - if (!meta.savegame_name.empty()) { - if (meta.savegame_name.lastChar() == '\n') - meta.savegame_name.deleteLastChar(); - - DebugPrintf("%s: '%s'\n", filename.c_str(), meta.savegame_name.c_str()); - } - delete in; - } + DebugPrintf("%s: '%s'\n", filename.c_str(), saves[i].name); } + return true; } @@ -428,7 +417,7 @@ reg_t kGetSaveDir(EngineState *s, int argc, reg_t *argv) { warning("kGetSaveDir called with %d parameter(s): %04x:%04x", argc, PRINT_REG(argv[0])); #endif - return make_reg(s->sys_strings_segment, SYS_STRING_SAVEDIR); + return make_reg(s->_segMan->getSysStringsSegment(), SYS_STRING_SAVEDIR); } reg_t kCheckFreeSpace(EngineState *s, int argc, reg_t *argv) { @@ -449,90 +438,59 @@ reg_t kCheckFreeSpace(EngineState *s, int argc, reg_t *argv) { reg_t kCheckSaveGame(EngineState *s, int argc, reg_t *argv) { Common::String game_id = s->_segMan->getString(argv[0]); - int savedir_nr = argv[1].toUint16(); + uint16 savedir_nr = argv[1].toUint16(); debug(3, "kCheckSaveGame(%s, %d)", game_id.c_str(), savedir_nr); Common::Array<SavegameDesc> saves; listSavegames(saves); - savedir_nr = saves[savedir_nr].id; - - if (savedir_nr > MAX_SAVEGAME_NR - 1) { + // Check for savegame slot being out of range + if (savedir_nr >= saves.size()) return NULL_REG; - } - Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager(); - Common::String filename = g_sci->getSavegameName(savedir_nr); - Common::SeekableReadStream *in; - if ((in = saveFileMan->openForLoading(filename))) { - // found a savegame file - - SavegameMetadata meta; - if (!get_savegame_metadata(in, &meta)) { - // invalid - s->r_acc = make_reg(0, 0); - } else { - s->r_acc = make_reg(0, 1); - } - delete in; - } else { - s->r_acc = make_reg(0, 1); - } + // Check for compatible savegame version + int ver = saves[savedir_nr].version; + if (ver < MINIMUM_SAVEGAME_VERSION || ver > CURRENT_SAVEGAME_VERSION) + return NULL_REG; - return s->r_acc; + // Otherwise we assume the savegame is OK + return make_reg(0, 1); } reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) { Common::String game_id = s->_segMan->getString(argv[0]); - reg_t nametarget = argv[1]; - reg_t *nameoffsets = s->_segMan->derefRegPtr(argv[2], 0); debug(3, "kGetSaveFiles(%s)", game_id.c_str()); Common::Array<SavegameDesc> saves; listSavegames(saves); - s->r_acc = NULL_REG; - Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager(); + uint totalSaves = MIN<uint>(saves.size(), MAX_SAVEGAME_NR); - for (uint i = 0; i < saves.size(); i++) { - Common::String filename = g_sci->getSavegameName(saves[i].id); - Common::SeekableReadStream *in; - if ((in = saveFileMan->openForLoading(filename))) { - // found a savegame file + reg_t *slot = s->_segMan->derefRegPtr(argv[2], totalSaves); - SavegameMetadata meta; - if (!get_savegame_metadata(in, &meta)) { - // invalid - delete in; - continue; - } - - if (!meta.savegame_name.empty()) { - if (meta.savegame_name.lastChar() == '\n') - meta.savegame_name.deleteLastChar(); - - *nameoffsets = s->r_acc; // Store savegame ID - ++s->r_acc.offset; // Increase number of files found + if (!slot) { + warning("kGetSaveFiles: %04X:%04X invalid or too small to hold slot data", PRINT_REG(argv[2])); + totalSaves = 0; + } - nameoffsets++; // Make sure the next ID string address is written to the next pointer - Common::String name = meta.savegame_name; - if (name.size() > SCI_MAX_SAVENAME_LENGTH-1) - name = Common::String(meta.savegame_name.c_str(), SCI_MAX_SAVENAME_LENGTH-1); - s->_segMan->strcpy(nametarget, name.c_str()); + const uint bufSize = (totalSaves * SCI_MAX_SAVENAME_LENGTH) + 1; + char *saveNames = new char[bufSize]; + char *saveNamePtr = saveNames; - // Increase name offset pointer accordingly - nametarget.offset += SCI_MAX_SAVENAME_LENGTH; - } - delete in; - } + for (uint i = 0; i < totalSaves; i++) { + *slot++ = make_reg(0, i); // Store slot + strcpy(saveNamePtr, saves[i].name); + saveNamePtr += SCI_MAX_SAVENAME_LENGTH; } - //free(gfname); - s->_segMan->strcpy(nametarget, ""); // Terminate list + *saveNamePtr = 0; // Terminate list - return s->r_acc; + s->_segMan->memcpy(argv[1], (byte *)saveNames, bufSize); + delete[] saveNames; + + return make_reg(0, totalSaves); } reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) { diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index abc7efd743..abe55455de 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -23,8 +23,10 @@ * */ +#include "engines/util.h" #include "graphics/cursorman.h" #include "graphics/video/avi_decoder.h" +#include "graphics/video/qt_decoder.h" #include "graphics/surface.h" #include "sci/sci.h" @@ -51,8 +53,8 @@ namespace Sci { void _k_dirloop(reg_t object, uint16 angle, EngineState *s, int argc, reg_t *argv) { - GuiResourceId viewId = GET_SEL32V(s->_segMan, object, SELECTOR(view)); - uint16 signal = GET_SEL32V(s->_segMan, object, SELECTOR(signal)); + GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view)); + uint16 signal = readSelectorValue(s->_segMan, object, SELECTOR(signal)); int16 loopNo; int16 maxLoops; bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); @@ -91,7 +93,7 @@ void _k_dirloop(reg_t object, uint16 angle, EngineState *s, int argc, reg_t *arg if ((loopNo > 1) && (maxLoops < 4)) return; - PUT_SEL32V(s->_segMan, object, SELECTOR(loop), loopNo); + writeSelectorValue(s->_segMan, object, SELECTOR(loop), loopNo); } static reg_t kSetCursorSci0(EngineState *s, int argc, reg_t *argv) { @@ -146,6 +148,10 @@ static reg_t kSetCursorSci11(EngineState *s, int argc, reg_t *argv) { int16 bottom = argv[2].toSint16(); int16 right = argv[3].toSint16(); + // In SCI32, the right parameter seems to be divided by 2 + if (getSciVersion() >= SCI_VERSION_2) + right *= 2; + if ((right >= left) && (bottom >= top)) { Common::Rect rect = Common::Rect(left, top, right, bottom); g_sci->_gfxCursor->kernelSetMoveZone(rect); @@ -437,7 +443,7 @@ reg_t kCelWide(EngineState *s, int argc, reg_t *argv) { reg_t kNumLoops(EngineState *s, int argc, reg_t *argv) { reg_t object = argv[0]; - GuiResourceId viewId = GET_SEL32V(s->_segMan, object, SELECTOR(view)); + GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view)); int16 loopCount; loopCount = g_sci->_gfxCache->kernelViewGetLoopCount(viewId); @@ -449,8 +455,8 @@ reg_t kNumLoops(EngineState *s, int argc, reg_t *argv) { reg_t kNumCels(EngineState *s, int argc, reg_t *argv) { reg_t object = argv[0]; - GuiResourceId viewId = GET_SEL32V(s->_segMan, object, SELECTOR(view)); - int16 loopNo = GET_SEL32V(s->_segMan, object, SELECTOR(loop)); + GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view)); + int16 loopNo = readSelectorValue(s->_segMan, object, SELECTOR(loop)); int16 celCount; celCount = g_sci->_gfxCache->kernelViewGetCelCount(viewId, loopNo); @@ -526,8 +532,8 @@ reg_t kBaseSetter(EngineState *s, int argc, reg_t *argv) { // WORKAROUND for a problem in LSL1VGA. This allows the casino door to be opened, // till the actual problem is found if (!strcmp(g_sci->getGameID(), "lsl1sci") && s->currentRoomNumber() == 300) { - int top = GET_SEL32V(s->_segMan, object, SELECTOR(brTop)); - PUT_SEL32V(s->_segMan, object, SELECTOR(brTop), top + 2); + int top = readSelectorValue(s->_segMan, object, SELECTOR(brTop)); + writeSelectorValue(s->_segMan, object, SELECTOR(brTop), top + 2); } return s->r_acc; @@ -741,12 +747,12 @@ Common::Rect kControlCreateRect(int16 x, int16 y, int16 x1, int16 y1) { } void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) { - int16 type = GET_SEL32V(s->_segMan, controlObject, SELECTOR(type)); - int16 style = GET_SEL32V(s->_segMan, controlObject, SELECTOR(state)); - int16 x = GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsLeft)); - int16 y = GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsTop)); - GuiResourceId fontId = GET_SEL32V(s->_segMan, controlObject, SELECTOR(font)); - reg_t textReference = GET_SEL32(s->_segMan, controlObject, SELECTOR(text)); + int16 type = readSelectorValue(s->_segMan, controlObject, SELECTOR(type)); + int16 style = readSelectorValue(s->_segMan, controlObject, SELECTOR(state)); + int16 x = readSelectorValue(s->_segMan, controlObject, SELECTOR(nsLeft)); + int16 y = readSelectorValue(s->_segMan, controlObject, SELECTOR(nsTop)); + GuiResourceId fontId = readSelectorValue(s->_segMan, controlObject, SELECTOR(font)); + reg_t textReference = readSelector(s->_segMan, controlObject, SELECTOR(text)); Common::String text; Common::Rect rect; TextAlignment alignment; @@ -762,8 +768,8 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) { bool isAlias = false; rect = kControlCreateRect(x, y, - GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsRight)), - GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsBottom))); + readSelectorValue(s->_segMan, controlObject, SELECTOR(nsRight)), + readSelectorValue(s->_segMan, controlObject, SELECTOR(nsBottom))); if (!textReference.isNull()) text = s->_segMan->getString(textReference); @@ -775,32 +781,32 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) { return; case SCI_CONTROLS_TYPE_TEXT: - alignment = GET_SEL32V(s->_segMan, controlObject, SELECTOR(mode)); + alignment = readSelectorValue(s->_segMan, controlObject, SELECTOR(mode)); debugC(2, kDebugLevelGraphics, "drawing text %04x:%04x ('%s') to %d,%d, mode=%d", PRINT_REG(controlObject), text.c_str(), x, y, alignment); g_sci->_gfxControls->kernelDrawText(rect, controlObject, g_sci->strSplit(text.c_str()).c_str(), fontId, alignment, style, hilite); return; case SCI_CONTROLS_TYPE_TEXTEDIT: - mode = GET_SEL32V(s->_segMan, controlObject, SELECTOR(mode)); - maxChars = GET_SEL32V(s->_segMan, controlObject, SELECTOR(max)); - cursorPos = GET_SEL32V(s->_segMan, controlObject, SELECTOR(cursor)); + mode = readSelectorValue(s->_segMan, controlObject, SELECTOR(mode)); + maxChars = readSelectorValue(s->_segMan, controlObject, SELECTOR(max)); + cursorPos = readSelectorValue(s->_segMan, controlObject, SELECTOR(cursor)); debugC(2, kDebugLevelGraphics, "drawing edit control %04x:%04x (text %04x:%04x, '%s') to %d,%d", PRINT_REG(controlObject), PRINT_REG(textReference), text.c_str(), x, y); g_sci->_gfxControls->kernelDrawTextEdit(rect, controlObject, g_sci->strSplit(text.c_str(), NULL).c_str(), fontId, mode, style, cursorPos, maxChars, hilite); return; case SCI_CONTROLS_TYPE_ICON: - viewId = GET_SEL32V(s->_segMan, controlObject, SELECTOR(view)); + viewId = readSelectorValue(s->_segMan, controlObject, SELECTOR(view)); { - int l = GET_SEL32V(s->_segMan, controlObject, SELECTOR(loop)); + int l = readSelectorValue(s->_segMan, controlObject, SELECTOR(loop)); loopNo = (l & 0x80) ? l - 256 : l; - int c = GET_SEL32V(s->_segMan, controlObject, SELECTOR(cel)); + int c = readSelectorValue(s->_segMan, controlObject, SELECTOR(cel)); celNo = (c & 0x80) ? c - 256 : c; // Game-specific: *ONLY* the jones EGA/VGA sierra interpreter contain code using priority selector // ALL other games use a hardcoded -1 (madness!) // We are detecting jones/talkie as "jones" as well, but the sierra interpreter of talkie doesnt have this // "hack". Hopefully it wont cause regressions (the code causes regressions if used against kq5/floppy) if (!strcmp(g_sci->getGameID(), "jones")) - priority = GET_SEL32V(s->_segMan, controlObject, SELECTOR(priority)); + priority = readSelectorValue(s->_segMan, controlObject, SELECTOR(priority)); else priority = -1; } @@ -813,17 +819,17 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) { if (type == SCI_CONTROLS_TYPE_LIST_ALIAS) isAlias = true; - maxChars = GET_SEL32V(s->_segMan, controlObject, SELECTOR(x)); // max chars per entry - cursorOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(cursor)); + maxChars = readSelectorValue(s->_segMan, controlObject, SELECTOR(x)); // max chars per entry + cursorOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(cursor)); if (g_sci->getKernel()->_selectorCache.topString != -1) { // Games from early SCI1 onwards use topString - upperOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(topString)); + upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(topString)); } else { // Earlier games use lsTop or brTop - if (lookup_selector(s->_segMan, controlObject, g_sci->getKernel()->_selectorCache.brTop, NULL, NULL) == kSelectorVariable) - upperOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(brTop)); + if (lookupSelector(s->_segMan, controlObject, g_sci->getKernel()->_selectorCache.brTop, NULL, NULL) == kSelectorVariable) + upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(brTop)); else - upperOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(lsTop)); + upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(lsTop)); } // Count string entries in NULL terminated string list @@ -874,8 +880,8 @@ reg_t kDrawControl(EngineState *s, int argc, reg_t *argv) { // Disable the "Change Directory" button, as we don't allow the game engine to // change the directory where saved games are placed if (objName == "changeDirI") { - int state = GET_SEL32V(s->_segMan, controlObject, SELECTOR(state)); - PUT_SEL32V(s->_segMan, controlObject, SELECTOR(state), (state | SCI_CONTROLS_STYLE_DISABLED) & ~SCI_CONTROLS_STYLE_ENABLED); + int state = readSelectorValue(s->_segMan, controlObject, SELECTOR(state)); + writeSelectorValue(s->_segMan, controlObject, SELECTOR(state), (state | SCI_CONTROLS_STYLE_DISABLED) & ~SCI_CONTROLS_STYLE_ENABLED); } _k_GenericDrawControl(s, controlObject, false); @@ -894,7 +900,7 @@ reg_t kEditControl(EngineState *s, int argc, reg_t *argv) { reg_t eventObject = argv[1]; if (!controlObject.isNull()) { - int16 controlType = GET_SEL32V(s->_segMan, controlObject, SELECTOR(type)); + int16 controlType = readSelectorValue(s->_segMan, controlObject, SELECTOR(type)); switch (controlType) { case SCI_CONTROLS_TYPE_TEXTEDIT: @@ -1080,11 +1086,12 @@ reg_t kDisplay(EngineState *s, int argc, reg_t *argv) { reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { // Hide the cursor if it's showing and then show it again if it was // previously visible. - bool reshowCursor; - - reshowCursor = g_sci->_gfxCursor->isVisible(); + bool reshowCursor = g_sci->_gfxCursor->isVisible(); if (reshowCursor) g_sci->_gfxCursor->kernelHide(); + + uint16 screenWidth = g_system->getWidth(); + uint16 screenHeight = g_system->getHeight(); Graphics::VideoDecoder *videoDecoder = 0; @@ -1094,8 +1101,18 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { if (g_sci->getPlatform() == Common::kPlatformMacintosh) { // Mac QuickTime // The only argument is the string for the video - warning("TODO: Play QuickTime movie '%s'", filename.c_str()); - return s->r_acc; + + // HACK: Switch to 16bpp graphics for Cinepak. + initGraphics(screenWidth, screenHeight, screenWidth > 320, NULL); + + if (g_system->getScreenFormat().bytesPerPixel == 1) { + warning("This video requires >8bpp color to be displayed, but could not switch to RGB color mode."); + return NULL_REG; + } + + videoDecoder = new Graphics::QuickTimeDecoder(); + if (!videoDecoder->loadFile(filename)) + error("Could not open '%s'", filename.c_str()); } else { // DOS SEQ // SEQ's are called with no subops, just the string and delay @@ -1110,7 +1127,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { } } } else { - // Windows AVI (Macintosh QuickTime? Need to check KQ6 Macintosh) + // Windows AVI // TODO: This appears to be some sort of subop. case 0 contains the string // for the video, so we'll just play it from there for now. @@ -1142,10 +1159,10 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { } if (videoDecoder) { - uint16 x = (g_system->getWidth() - videoDecoder->getWidth()) / 2; - uint16 y = (g_system->getHeight() - videoDecoder->getHeight()) / 2; + uint16 x = (screenWidth - videoDecoder->getWidth()) / 2; + uint16 y = (screenHeight - videoDecoder->getHeight()) / 2; - while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo()) { + while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo()) { if (videoDecoder->needsUpdate()) { Graphics::Surface *frame = videoDecoder->decodeNextFrame(); if (frame) { @@ -1164,9 +1181,15 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { g_system->delayMillis(10); } + + // HACK: Switch back to 8bpp if we played a QuickTime video. + // We also won't be copying the screen to the SCI screen... + if (g_system->getScreenFormat().bytesPerPixel != 1) + initGraphics(screenWidth, screenHeight, screenWidth > 320); + else + g_sci->_gfxScreen->kernelSyncWithFramebuffer(); delete videoDecoder; - g_sci->_gfxScreen->kernelSyncWithFramebuffer(); } if (reshowCursor) diff --git a/engines/sci/engine/klists.cpp b/engines/sci/engine/klists.cpp index c04454ca3d..f06f3eec77 100644 --- a/engines/sci/engine/klists.cpp +++ b/engines/sci/engine/klists.cpp @@ -155,28 +155,10 @@ reg_t kDisposeList(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } -static reg_t _k_new_node(EngineState *s, reg_t value, reg_t key) { - reg_t nodebase; - Node *n = s->_segMan->allocateNode(&nodebase); - - if (!n) { - error("[Kernel] Out of memory while creating a node"); - return NULL_REG; - } - - n->pred = n->succ = NULL_REG; - n->key = key; - n->value = value; - - return nodebase; -} - reg_t kNewNode(EngineState *s, int argc, reg_t *argv) { - - if (argc == 1) - s->r_acc = _k_new_node(s, argv[0], argv[0]); - else - s->r_acc = _k_new_node(s, argv[0], argv[1]); + reg_t nodeValue = argv[0]; + reg_t nodeKey = (argc == 2) ? argv[1] : NULL_REG; + s->r_acc = s->_segMan->newNode(nodeValue, nodeKey); debugC(2, kDebugLevelNodes, "New nodebase at %04x:%04x", PRINT_REG(s->r_acc)); @@ -415,11 +397,11 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) { reg_t dest = argv[1]; reg_t order_func = argv[2]; - int input_size = (int16)GET_SEL32V(segMan, source, SELECTOR(size)); + int input_size = (int16)readSelectorValue(segMan, source, SELECTOR(size)); int i; - reg_t input_data = GET_SEL32(segMan, source, SELECTOR(elements)); - reg_t output_data = GET_SEL32(segMan, dest, SELECTOR(elements)); + reg_t input_data = readSelector(segMan, source, SELECTOR(elements)); + reg_t output_data = readSelector(segMan, dest, SELECTOR(elements)); List *list; Node *node; @@ -430,10 +412,10 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) { if (output_data.isNull()) { list = s->_segMan->allocateList(&output_data); list->first = list->last = NULL_REG; - PUT_SEL32(segMan, dest, SELECTOR(elements), output_data); + writeSelector(segMan, dest, SELECTOR(elements), output_data); } - PUT_SEL32V(segMan, dest, SELECTOR(size), input_size); + writeSelectorValue(segMan, dest, SELECTOR(size), input_size); list = s->_segMan->lookupList(input_data); node = s->_segMan->lookupNode(list->first); @@ -442,7 +424,7 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) { i = 0; while (node) { - invoke_selector(INV_SEL(s, order_func, doit, kStopOnInvalidSelector), 1, node->value); + invokeSelector(INV_SEL(s, order_func, doit, kStopOnInvalidSelector), 1, node->value); temp_array[i].key = node->key; temp_array[i].value = node->value; temp_array[i].order = s->r_acc; @@ -453,7 +435,7 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) { qsort(temp_array, input_size, sizeof(sort_temp_t), sort_temp_cmp); for (i = 0;i < input_size;i++) { - reg_t lNode = _k_new_node(s, temp_array[i].key, temp_array[i].value); + reg_t lNode = s->_segMan->newNode(temp_array[i].value, temp_array[i].key); _k_add_to_end(s, output_data, lNode); } @@ -533,15 +515,15 @@ reg_t kListEachElementDo(EngineState *s, int argc, reg_t *argv) { curObject = curNode->value; // First, check if the target selector is a variable - if (lookup_selector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { + if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { // This can only happen with 3 params (list, target selector, variable) if (argc != 3) { warning("kListEachElementDo: Attempted to modify a variable selector with %d params", argc); } else { - write_selector(s->_segMan, curObject, slc, argv[2]); + writeSelector(s->_segMan, curObject, slc, argv[2]); } } else { - invoke_selector_argv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2); + invokeSelectorArgv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2); } curNode = s->_segMan->lookupNode(nextNode); @@ -566,11 +548,11 @@ reg_t kListFirstTrue(EngineState *s, int argc, reg_t *argv) { curObject = curNode->value; // First, check if the target selector is a variable - if (lookup_selector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { + if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { // Can this happen with variable selectors? warning("kListFirstTrue: Attempted to access a variable selector"); } else { - invoke_selector_argv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2); + invokeSelectorArgv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2); // Check if the result is true if (!s->r_acc.isNull()) @@ -600,11 +582,11 @@ reg_t kListAllTrue(EngineState *s, int argc, reg_t *argv) { curObject = curNode->value; // First, check if the target selector is a variable - if (lookup_selector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { + if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { // Can this happen with variable selectors? warning("kListAllTrue: Attempted to access a variable selector"); } else { - invoke_selector_argv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2); + invokeSelectorArgv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2); // Check if the result isn't true if (s->r_acc.isNull()) diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp index c05a2bc57b..f91ba0fd82 100644 --- a/engines/sci/engine/kmisc.cpp +++ b/engines/sci/engine/kmisc.cpp @@ -41,7 +41,7 @@ reg_t kRestartGame(EngineState *s, int argc, reg_t *argv) { s->shrinkStackToBase(); - script_abort_flag = 1; // Force vm to abort ASAP + s->script_abort_flag = 1; // Force vm to abort ASAP return NULL_REG; } @@ -252,10 +252,15 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) { break; } case K_MEMORY_PEEK : { + if (!argv[1].segment) { + // This occurs in KQ5CD when interacting with certain objects + warning("Attempt to peek invalid memory at %04x:%04x", PRINT_REG(argv[1])); + return s->r_acc; + } + SegmentRef ref = s->_segMan->dereference(argv[1]); if (!ref.isValid() || ref.maxSize < 2) { - // This occurs in KQ5CD when interacting with certain objects warning("Attempt to peek invalid memory at %04x:%04x", PRINT_REG(argv[1])); return s->r_acc; } diff --git a/engines/sci/engine/kmovement.cpp b/engines/sci/engine/kmovement.cpp index 42e690422c..499aeabcc6 100644 --- a/engines/sci/engine/kmovement.cpp +++ b/engines/sci/engine/kmovement.cpp @@ -158,8 +158,8 @@ reg_t kSetJump(EngineState *s, int argc, reg_t *argv) { debugC(2, kDebugLevelBresen, "SetJump for object at %04x:%04x", PRINT_REG(object)); debugC(2, kDebugLevelBresen, "xStep: %d, yStep: %d", vx, vy); - PUT_SEL32V(segMan, object, SELECTOR(xStep), vx); - PUT_SEL32V(segMan, object, SELECTOR(yStep), vy); + writeSelectorValue(segMan, object, SELECTOR(xStep), vx); + writeSelectorValue(segMan, object, SELECTOR(yStep), vy); return s->r_acc; } @@ -168,9 +168,9 @@ reg_t kSetJump(EngineState *s, int argc, reg_t *argv) { #define _K_BRESEN_AXIS_Y 1 static void initialize_bresen(SegManager *segMan, int argc, reg_t *argv, reg_t mover, int step_factor, int deltax, int deltay) { - reg_t client = GET_SEL32(segMan, mover, SELECTOR(client)); - int stepx = (int16)GET_SEL32V(segMan, client, SELECTOR(xStep)) * step_factor; - int stepy = (int16)GET_SEL32V(segMan, client, SELECTOR(yStep)) * step_factor; + reg_t client = readSelector(segMan, mover, SELECTOR(client)); + int stepx = (int16)readSelectorValue(segMan, client, SELECTOR(xStep)) * step_factor; + int stepy = (int16)readSelectorValue(segMan, client, SELECTOR(yStep)) * step_factor; int numsteps_x = stepx ? (abs(deltax) + stepx - 1) / stepx : 0; int numsteps_y = stepy ? (abs(deltay) + stepy - 1) / stepy : 0; int bdi, i1; @@ -191,15 +191,15 @@ static void initialize_bresen(SegManager *segMan, int argc, reg_t *argv, reg_t m /* if (abs(deltax) > abs(deltay)) {*/ // Bresenham on y if (numsteps_y < numsteps_x) { - PUT_SEL32V(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_Y); - PUT_SEL32V(segMan, mover, SELECTOR(b_incr), (deltay < 0) ? -1 : 1); + writeSelectorValue(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_Y); + writeSelectorValue(segMan, mover, SELECTOR(b_incr), (deltay < 0) ? -1 : 1); //i1 = 2 * (abs(deltay) - abs(deltay_step * numsteps)) * abs(deltax_step); //bdi = -abs(deltax); i1 = 2 * (abs(deltay) - abs(deltay_step * (numsteps - 1))) * abs(deltax_step); bdi = -abs(deltax); } else { // Bresenham on x - PUT_SEL32V(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_X); - PUT_SEL32V(segMan, mover, SELECTOR(b_incr), (deltax < 0) ? -1 : 1); + writeSelectorValue(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_X); + writeSelectorValue(segMan, mover, SELECTOR(b_incr), (deltax < 0) ? -1 : 1); //i1= 2 * (abs(deltax) - abs(deltax_step * numsteps)) * abs(deltay_step); //bdi = -abs(deltay); i1 = 2 * (abs(deltax) - abs(deltax_step * (numsteps - 1))) * abs(deltay_step); @@ -207,26 +207,26 @@ static void initialize_bresen(SegManager *segMan, int argc, reg_t *argv, reg_t m } - PUT_SEL32V(segMan, mover, SELECTOR(dx), deltax_step); - PUT_SEL32V(segMan, mover, SELECTOR(dy), deltay_step); + writeSelectorValue(segMan, mover, SELECTOR(dx), deltax_step); + writeSelectorValue(segMan, mover, SELECTOR(dy), deltay_step); debugC(2, kDebugLevelBresen, "Init bresen for mover %04x:%04x: d=(%d,%d)", PRINT_REG(mover), deltax, deltay); debugC(2, kDebugLevelBresen, " steps=%d, mv=(%d, %d), i1= %d, i2=%d", numsteps, deltax_step, deltay_step, i1, bdi*2); - //PUT_SEL32V(segMan, mover, SELECTOR(b_movCnt), numsteps); // Needed for HQ1/Ogre? - PUT_SEL32V(segMan, mover, SELECTOR(b_di), bdi); - PUT_SEL32V(segMan, mover, SELECTOR(b_i1), i1); - PUT_SEL32V(segMan, mover, SELECTOR(b_i2), bdi * 2); + //writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), numsteps); // Needed for HQ1/Ogre? + writeSelectorValue(segMan, mover, SELECTOR(b_di), bdi); + writeSelectorValue(segMan, mover, SELECTOR(b_i1), i1); + writeSelectorValue(segMan, mover, SELECTOR(b_i2), bdi * 2); } reg_t kInitBresen(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; reg_t mover = argv[0]; - reg_t client = GET_SEL32(segMan, mover, SELECTOR(client)); + reg_t client = readSelector(segMan, mover, SELECTOR(client)); - int deltax = (int16)GET_SEL32V(segMan, mover, SELECTOR(x)) - (int16)GET_SEL32V(segMan, client, SELECTOR(x)); - int deltay = (int16)GET_SEL32V(segMan, mover, SELECTOR(y)) - (int16)GET_SEL32V(segMan, client, SELECTOR(y)); + int deltax = (int16)readSelectorValue(segMan, mover, SELECTOR(x)) - (int16)readSelectorValue(segMan, client, SELECTOR(x)); + int deltay = (int16)readSelectorValue(segMan, mover, SELECTOR(y)) - (int16)readSelectorValue(segMan, client, SELECTOR(y)); int step_factor = (argc < 1) ? argv[1].toUint16() : 1; initialize_bresen(s->_segMan, argc, argv, mover, step_factor, deltax, deltay); @@ -240,42 +240,42 @@ reg_t kInitBresen(EngineState *s, int argc, reg_t *argv) { reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; reg_t mover = argv[0]; - reg_t client = GET_SEL32(segMan, mover, SELECTOR(client)); + reg_t client = readSelector(segMan, mover, SELECTOR(client)); - int x = (int16)GET_SEL32V(segMan, client, SELECTOR(x)); - int y = (int16)GET_SEL32V(segMan, client, SELECTOR(y)); + int x = (int16)readSelectorValue(segMan, client, SELECTOR(x)); + int y = (int16)readSelectorValue(segMan, client, SELECTOR(y)); int oldx, oldy, destx, desty, dx, dy, bdi, bi1, bi2, movcnt, bdelta, axis; - uint16 signal = GET_SEL32V(segMan, client, SELECTOR(signal)); + uint16 signal = readSelectorValue(segMan, client, SELECTOR(signal)); int completed = 0; - int max_movcnt = GET_SEL32V(segMan, client, SELECTOR(moveSpeed)); + int max_movcnt = readSelectorValue(segMan, client, SELECTOR(moveSpeed)); if (getSciVersion() > SCI_VERSION_01) signal &= ~kSignalHitObstacle; - PUT_SEL32(segMan, client, SELECTOR(signal), make_reg(0, signal)); // This is a NOP for SCI0 + writeSelector(segMan, client, SELECTOR(signal), make_reg(0, signal)); // This is a NOP for SCI0 oldx = x; oldy = y; - destx = (int16)GET_SEL32V(segMan, mover, SELECTOR(x)); - desty = (int16)GET_SEL32V(segMan, mover, SELECTOR(y)); - dx = (int16)GET_SEL32V(segMan, mover, SELECTOR(dx)); - dy = (int16)GET_SEL32V(segMan, mover, SELECTOR(dy)); - bdi = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_di)); - bi1 = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_i1)); - bi2 = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_i2)); - movcnt = GET_SEL32V(segMan, mover, SELECTOR(b_movCnt)); - bdelta = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_incr)); - axis = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_xAxis)); + destx = (int16)readSelectorValue(segMan, mover, SELECTOR(x)); + desty = (int16)readSelectorValue(segMan, mover, SELECTOR(y)); + dx = (int16)readSelectorValue(segMan, mover, SELECTOR(dx)); + dy = (int16)readSelectorValue(segMan, mover, SELECTOR(dy)); + bdi = (int16)readSelectorValue(segMan, mover, SELECTOR(b_di)); + bi1 = (int16)readSelectorValue(segMan, mover, SELECTOR(b_i1)); + bi2 = (int16)readSelectorValue(segMan, mover, SELECTOR(b_i2)); + movcnt = readSelectorValue(segMan, mover, SELECTOR(b_movCnt)); + bdelta = (int16)readSelectorValue(segMan, mover, SELECTOR(b_incr)); + axis = (int16)readSelectorValue(segMan, mover, SELECTOR(b_xAxis)); //printf("movecnt %d, move speed %d\n", movcnt, max_movcnt); if (g_sci->_features->handleMoveCount()) { if (max_movcnt > movcnt) { ++movcnt; - PUT_SEL32V(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre? + writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre? return NULL_REG; } else { movcnt = 0; - PUT_SEL32V(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre? + writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre? } } @@ -288,7 +288,7 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { dy += bdelta; } - PUT_SEL32V(segMan, mover, SELECTOR(b_di), bdi); + writeSelectorValue(segMan, mover, SELECTOR(b_di), bdi); x += dx; y += dy; @@ -310,24 +310,24 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { debugC(2, kDebugLevelBresen, "Finished mover %04x:%04x", PRINT_REG(mover)); } - PUT_SEL32V(segMan, client, SELECTOR(x), x); - PUT_SEL32V(segMan, client, SELECTOR(y), y); + writeSelectorValue(segMan, client, SELECTOR(x), x); + writeSelectorValue(segMan, client, SELECTOR(y), y); debugC(2, kDebugLevelBresen, "New data: (x,y)=(%d,%d), di=%d", x, y, bdi); if (g_sci->getKernel()->_selectorCache.cantBeHere != -1) { - invoke_selector(INV_SEL(s, client, cantBeHere, kStopOnInvalidSelector), 0); + invokeSelector(INV_SEL(s, client, cantBeHere, kStopOnInvalidSelector), 0); s->r_acc = make_reg(0, !s->r_acc.offset); } else { - invoke_selector(INV_SEL(s, client, canBeHere, kStopOnInvalidSelector), 0); + invokeSelector(INV_SEL(s, client, canBeHere, kStopOnInvalidSelector), 0); } if (!s->r_acc.offset) { // Contains the return value - signal = GET_SEL32V(segMan, client, SELECTOR(signal)); + signal = readSelectorValue(segMan, client, SELECTOR(signal)); - PUT_SEL32V(segMan, client, SELECTOR(x), oldx); - PUT_SEL32V(segMan, client, SELECTOR(y), oldy); - PUT_SEL32V(segMan, client, SELECTOR(signal), (signal | kSignalHitObstacle)); + writeSelectorValue(segMan, client, SELECTOR(x), oldx); + writeSelectorValue(segMan, client, SELECTOR(y), oldy); + writeSelectorValue(segMan, client, SELECTOR(signal), (signal | kSignalHitObstacle)); debugC(2, kDebugLevelBresen, "Finished mover %04x:%04x by collision", PRINT_REG(mover)); completed = 1; @@ -335,7 +335,7 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { if ((getSciVersion() >= SCI_VERSION_1_EGA)) if (completed) - invoke_selector(INV_SEL(s, mover, moveDone, kStopOnInvalidSelector), 0); + invokeSelector(INV_SEL(s, mover, moveDone, kStopOnInvalidSelector), 0); return make_reg(0, completed); } @@ -377,15 +377,15 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) { return NULL_REG; } - client = GET_SEL32(segMan, avoider, SELECTOR(client)); + client = readSelector(segMan, avoider, SELECTOR(client)); if (!s->_segMan->isHeapObject(client)) { warning("DoAvoider() where client %04x:%04x is not an object", PRINT_REG(client)); return NULL_REG; } - looper = GET_SEL32(segMan, client, SELECTOR(looper)); - mover = GET_SEL32(segMan, client, SELECTOR(mover)); + looper = readSelector(segMan, client, SELECTOR(looper)); + mover = readSelector(segMan, client, SELECTOR(mover)); if (!s->_segMan->isHeapObject(mover)) { if (mover.segment) { @@ -394,38 +394,38 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } - destx = GET_SEL32V(segMan, mover, SELECTOR(x)); - desty = GET_SEL32V(segMan, mover, SELECTOR(y)); + destx = readSelectorValue(segMan, mover, SELECTOR(x)); + desty = readSelectorValue(segMan, mover, SELECTOR(y)); debugC(2, kDebugLevelBresen, "Doing avoider %04x:%04x (dest=%d,%d)", PRINT_REG(avoider), destx, desty); - if (invoke_selector(INV_SEL(s, mover, doit, kContinueOnInvalidSelector) , 0)) { + if (invokeSelector(INV_SEL(s, mover, doit, kContinueOnInvalidSelector) , 0)) { error("Mover %04x:%04x of avoider %04x:%04x doesn't have a doit() funcselector", PRINT_REG(mover), PRINT_REG(avoider)); return NULL_REG; } - mover = GET_SEL32(segMan, client, SELECTOR(mover)); + mover = readSelector(segMan, client, SELECTOR(mover)); if (!mover.segment) // Mover has been disposed? return s->r_acc; // Return gracefully. - if (invoke_selector(INV_SEL(s, client, isBlocked, kContinueOnInvalidSelector) , 0)) { + if (invokeSelector(INV_SEL(s, client, isBlocked, kContinueOnInvalidSelector) , 0)) { error("Client %04x:%04x of avoider %04x:%04x doesn't" " have an isBlocked() funcselector", PRINT_REG(client), PRINT_REG(avoider)); return NULL_REG; } - dx = destx - GET_SEL32V(segMan, client, SELECTOR(x)); - dy = desty - GET_SEL32V(segMan, client, SELECTOR(y)); + dx = destx - readSelectorValue(segMan, client, SELECTOR(x)); + dy = desty - readSelectorValue(segMan, client, SELECTOR(y)); angle = getAngle(dx, dy); debugC(2, kDebugLevelBresen, "Movement (%d,%d), angle %d is %sblocked", dx, dy, angle, (s->r_acc.offset) ? " " : "not "); if (s->r_acc.offset) { // isBlocked() returned non-zero int rotation = (rand() & 1) ? 45 : (360 - 45); // Clockwise/counterclockwise - int oldx = GET_SEL32V(segMan, client, SELECTOR(x)); - int oldy = GET_SEL32V(segMan, client, SELECTOR(y)); - int xstep = GET_SEL32V(segMan, client, SELECTOR(xStep)); - int ystep = GET_SEL32V(segMan, client, SELECTOR(yStep)); + int oldx = readSelectorValue(segMan, client, SELECTOR(x)); + int oldy = readSelectorValue(segMan, client, SELECTOR(y)); + int xstep = readSelectorValue(segMan, client, SELECTOR(xStep)); + int ystep = readSelectorValue(segMan, client, SELECTOR(yStep)); int moves; debugC(2, kDebugLevelBresen, " avoider %04x:%04x", PRINT_REG(avoider)); @@ -434,23 +434,23 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) { int move_x = (int)(sin(angle * PI / 180.0) * (xstep)); int move_y = (int)(-cos(angle * PI / 180.0) * (ystep)); - PUT_SEL32V(segMan, client, SELECTOR(x), oldx + move_x); - PUT_SEL32V(segMan, client, SELECTOR(y), oldy + move_y); + writeSelectorValue(segMan, client, SELECTOR(x), oldx + move_x); + writeSelectorValue(segMan, client, SELECTOR(y), oldy + move_y); debugC(2, kDebugLevelBresen, "Pos (%d,%d): Trying angle %d; delta=(%d,%d)", oldx, oldy, angle, move_x, move_y); - if (invoke_selector(INV_SEL(s, client, canBeHere, kContinueOnInvalidSelector) , 0)) { + if (invokeSelector(INV_SEL(s, client, canBeHere, kContinueOnInvalidSelector) , 0)) { error("Client %04x:%04x of avoider %04x:%04x doesn't" " have a canBeHere() funcselector", PRINT_REG(client), PRINT_REG(avoider)); return NULL_REG; } - PUT_SEL32V(segMan, client, SELECTOR(x), oldx); - PUT_SEL32V(segMan, client, SELECTOR(y), oldy); + writeSelectorValue(segMan, client, SELECTOR(x), oldx); + writeSelectorValue(segMan, client, SELECTOR(y), oldy); if (s->r_acc.offset) { // We can be here debugC(2, kDebugLevelBresen, "Success"); - PUT_SEL32V(segMan, client, SELECTOR(heading), angle); + writeSelectorValue(segMan, client, SELECTOR(heading), angle); return make_reg(0, angle); } @@ -463,17 +463,17 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) { warning("DoAvoider failed for avoider %04x:%04x", PRINT_REG(avoider)); } else { - int heading = GET_SEL32V(segMan, client, SELECTOR(heading)); + int heading = readSelectorValue(segMan, client, SELECTOR(heading)); if (heading == -1) return s->r_acc; // No change - PUT_SEL32V(segMan, client, SELECTOR(heading), angle); + writeSelectorValue(segMan, client, SELECTOR(heading), angle); s->r_acc = make_reg(0, angle); if (looper.segment) { - if (invoke_selector(INV_SEL(s, looper, doit, kContinueOnInvalidSelector), 2, angle, client)) { + if (invokeSelector(INV_SEL(s, looper, doit, kContinueOnInvalidSelector), 2, angle, client)) { error("Looper %04x:%04x of avoider %04x:%04x doesn't" " have a doit() funcselector", PRINT_REG(looper), PRINT_REG(avoider)); } else diff --git a/engines/sci/engine/kparse.cpp b/engines/sci/engine/kparse.cpp index 0254d21642..785ff39d22 100644 --- a/engines/sci/engine/kparse.cpp +++ b/engines/sci/engine/kparse.cpp @@ -42,6 +42,7 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) { reg_t heap_said_block = argv[0]; byte *said_block; int new_lastmatch; + Vocabulary *voc = g_sci->getVocabulary(); #ifdef DEBUG_PARSER const int debug_parser = 1; #else @@ -63,7 +64,7 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) { s->_voc->decipherSaidBlock(said_block); #endif - if (s->_voc->parser_event.isNull() || (GET_SEL32V(s->_segMan, s->_voc->parser_event, SELECTOR(claimed)))) { + if (voc->parser_event.isNull() || (readSelectorValue(s->_segMan, voc->parser_event, SELECTOR(claimed)))) { return NULL_REG; } @@ -77,7 +78,7 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) { s->r_acc = make_reg(0, 1); if (new_lastmatch != SAID_PARTIAL_MATCH) - PUT_SEL32V(s->_segMan, s->_voc->parser_event, SELECTOR(claimed), 1); + writeSelectorValue(s->_segMan, voc->parser_event, SELECTOR(claimed), 1); } else { return NULL_REG; @@ -92,15 +93,15 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) { char *error; ResultWordList words; reg_t event = argv[1]; - Vocabulary *voc = s->_voc; + Vocabulary *voc = g_sci->getVocabulary(); - s->_voc->parser_event = event; + voc->parser_event = event; bool res = voc->tokenizeString(words, string.c_str(), &error); - s->_voc->parserIsValid = false; /* not valid */ + voc->parserIsValid = false; /* not valid */ if (res && !words.empty()) { - s->_voc->synonymizeTokens(words); + voc->synonymizeTokens(words); s->r_acc = make_reg(0, 1); @@ -115,32 +116,32 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) { if (syntax_fail) { s->r_acc = make_reg(0, 1); - PUT_SEL32V(segMan, event, SELECTOR(claimed), 1); + writeSelectorValue(segMan, event, SELECTOR(claimed), 1); - invoke_selector(INV_SEL(s, s->_gameObj, syntaxFail, kStopOnInvalidSelector), 2, s->_voc->parser_base, stringpos); + invokeSelector(INV_SEL(s, s->_gameObj, syntaxFail, kStopOnInvalidSelector), 2, voc->parser_base, stringpos); /* Issue warning */ debugC(2, kDebugLevelParser, "Tree building failed"); } else { - s->_voc->parserIsValid = true; - PUT_SEL32V(segMan, event, SELECTOR(claimed), 0); + voc->parserIsValid = true; + writeSelectorValue(segMan, event, SELECTOR(claimed), 0); #ifdef DEBUG_PARSER - s->_voc->dumpParseTree(); + voc->dumpParseTree(); #endif } } else { s->r_acc = make_reg(0, 0); - PUT_SEL32V(segMan, event, SELECTOR(claimed), 1); + writeSelectorValue(segMan, event, SELECTOR(claimed), 1); if (error) { - s->_segMan->strcpy(s->_voc->parser_base, error); + s->_segMan->strcpy(voc->parser_base, error); debugC(2, kDebugLevelParser, "Word unknown: %s", error); /* Issue warning: */ - invoke_selector(INV_SEL(s, s->_gameObj, wordFail, kStopOnInvalidSelector), 2, s->_voc->parser_base, stringpos); + invokeSelector(INV_SEL(s, s->_gameObj, wordFail, kStopOnInvalidSelector), 2, voc->parser_base, stringpos); free(error); return make_reg(0, 1); /* Tell them that it didn't work */ } @@ -156,28 +157,29 @@ reg_t kSetSynonyms(EngineState *s, int argc, reg_t *argv) { Node *node; int script; int numSynonyms = 0; + Vocabulary *voc = g_sci->getVocabulary(); // Only SCI0-SCI1 EGA games had a parser. In newer versions, this is a stub if (getSciVersion() > SCI_VERSION_1_EGA) return s->r_acc; - s->_voc->clearSynonyms(); + voc->clearSynonyms(); - list = s->_segMan->lookupList(GET_SEL32(segMan, object, SELECTOR(elements))); + list = s->_segMan->lookupList(readSelector(segMan, object, SELECTOR(elements))); node = s->_segMan->lookupNode(list->first); while (node) { reg_t objpos = node->value; int seg; - script = GET_SEL32V(segMan, objpos, SELECTOR(number)); + script = readSelectorValue(segMan, objpos, SELECTOR(number)); seg = s->_segMan->getScriptSegment(script); if (seg > 0) numSynonyms = s->_segMan->getScript(seg)->getSynonymsNr(); if (numSynonyms) { - byte *synonyms = s->_segMan->getScript(seg)->getSynonyms(); + const byte *synonyms = s->_segMan->getScript(seg)->getSynonyms(); if (synonyms) { debugC(2, kDebugLevelParser, "Setting %d synonyms for script.%d", @@ -193,7 +195,7 @@ reg_t kSetSynonyms(EngineState *s, int argc, reg_t *argv) { synonym_t tmp; tmp.replaceant = (int16)READ_LE_UINT16(synonyms + i * 4); tmp.replacement = (int16)READ_LE_UINT16(synonyms + i * 4 + 2); - s->_voc->addSynonym(tmp); + voc->addSynonym(tmp); } } else warning("Synonyms of script.%03d were requested, but script is not available", script); diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp index 1152addeba..857ccc2a08 100644 --- a/engines/sci/engine/kpathing.cpp +++ b/engines/sci/engine/kpathing.cpp @@ -337,15 +337,15 @@ static void draw_point(EngineState *s, Common::Point p, int start, int width, in static void draw_polygon(EngineState *s, reg_t polygon, int width, int height) { SegManager *segMan = s->_segMan; - reg_t points = GET_SEL32(segMan, polygon, SELECTOR(points)); + reg_t points = readSelector(segMan, polygon, SELECTOR(points)); #ifdef ENABLE_SCI32 if (segMan->isHeapObject(points)) - points = GET_SEL32(segMan, points, SELECTOR(data)); + points = readSelector(segMan, points, SELECTOR(data)); #endif - int size = GET_SEL32V(segMan, polygon, SELECTOR(size)); - int type = GET_SEL32V(segMan, polygon, SELECTOR(type)); + int size = readSelectorValue(segMan, polygon, SELECTOR(size)); + int type = readSelectorValue(segMan, polygon, SELECTOR(type)); Common::Point first, prev; int i; @@ -386,15 +386,15 @@ static void draw_input(EngineState *s, reg_t poly_list, Common::Point start, Com } static void print_polygon(SegManager *segMan, reg_t polygon) { - reg_t points = GET_SEL32(segMan, polygon, SELECTOR(points)); + reg_t points = readSelector(segMan, polygon, SELECTOR(points)); #ifdef ENABLE_SCI32 if (segMan->isHeapObject(points)) - points = GET_SEL32(segMan, points, SELECTOR(data)); + points = readSelector(segMan, points, SELECTOR(data)); #endif - int size = GET_SEL32V(segMan, polygon, SELECTOR(size)); - int type = GET_SEL32V(segMan, polygon, SELECTOR(type)); + int size = readSelectorValue(segMan, polygon, SELECTOR(size)); + int type = readSelectorValue(segMan, polygon, SELECTOR(type)); int i; Common::Point point; @@ -1036,13 +1036,13 @@ static Polygon *convert_polygon(EngineState *s, reg_t polygon) { // Returns : (Polygon *) The converted polygon, or NULL on error SegManager *segMan = s->_segMan; int i; - reg_t points = GET_SEL32(segMan, polygon, SELECTOR(points)); - int size = GET_SEL32V(segMan, polygon, SELECTOR(size)); + reg_t points = readSelector(segMan, polygon, SELECTOR(points)); + int size = readSelectorValue(segMan, polygon, SELECTOR(size)); #ifdef ENABLE_SCI32 // SCI32 stores the actual points in the data property of points (in a new array) if (segMan->isHeapObject(points)) - points = GET_SEL32(segMan, points, SELECTOR(data)); + points = readSelector(segMan, points, SELECTOR(data)); #endif if (size == 0) { @@ -1050,7 +1050,7 @@ static Polygon *convert_polygon(EngineState *s, reg_t polygon) { return NULL; } - Polygon *poly = new Polygon(GET_SEL32V(segMan, polygon, SELECTOR(type))); + Polygon *poly = new Polygon(readSelectorValue(segMan, polygon, SELECTOR(type))); int skip = 0; @@ -1121,7 +1121,7 @@ static PathfindingState *convert_polygon_set(EngineState *s, reg_t poly_list, Co if (polygon) { pf_s->polygons.push_back(polygon); - count += GET_SEL32V(segMan, node->value, SELECTOR(size)); + count += readSelectorValue(segMan, node->value, SELECTOR(size)); } node = s->_segMan->lookupNode(node->succ); @@ -1394,7 +1394,7 @@ reg_t kAvoidPath(EngineState *s, int argc, reg_t *argv) { if (argc < 7) error("[avoidpath] Not enough arguments"); - poly_list = (!argv[4].isNull() ? GET_SEL32(s->_segMan, argv[4], SELECTOR(elements)) : NULL_REG); + poly_list = (!argv[4].isNull() ? readSelector(s->_segMan, argv[4], SELECTOR(elements)) : NULL_REG); width = argv[5].toUint16(); height = argv[6].toUint16(); if (argc > 7) @@ -1694,4 +1694,38 @@ reg_t kIntersections(EngineState *s, int argc, reg_t *argv) { } } +// This is a quite rare kernel function. An example of when it's called +// is in QFG1VGA, after killing any monster. +reg_t kMergePoly(EngineState *s, int argc, reg_t *argv) { + // 3 parameters: raw polygon data, polygon list, list size + reg_t polygonData = argv[0]; + + // TODO: actually merge the polygon + // In QFG1VGA, there are no immediately visible side-effects + // of this being a stub. + +#if 0 + List *list = s->_segMan->lookupList(argv[1]); + Node *node = s->_segMan->lookupNode(list->first); + // List size is not needed + + Polygon *polygon; + int count = 0; + + while (node) { + polygon = convert_polygon(s, node->value); + + if (polygon) { + count += readSelectorValue(s->_segMan, node->value, SELECTOR(size)); + } + + node = s->_segMan->lookupNode(node->succ); + } +#endif + + warning("Stub: kMergePoly"); + + return polygonData; +} + } // End of namespace Sci diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp index ba29f64966..722d0175d1 100644 --- a/engines/sci/engine/kscripts.cpp +++ b/engines/sci/engine/kscripts.cpp @@ -111,7 +111,7 @@ reg_t kResCheck(EngineState *s, int argc, reg_t *argv) { reg_t kClone(EngineState *s, int argc, reg_t *argv) { reg_t parent_addr = argv[0]; - Object *parent_obj = s->_segMan->getObject(parent_addr); + const Object *parent_obj = s->_segMan->getObject(parent_addr); reg_t clone_addr; Clone *clone_obj; // same as Object* @@ -132,7 +132,7 @@ reg_t kClone(EngineState *s, int argc, reg_t *argv) { *clone_obj = *parent_obj; // Mark as clone - clone_obj->setInfoSelector(make_reg(0, SCRIPT_INFO_CLONE)); + clone_obj->markAsClone(); clone_obj->setSpeciesSelector(clone_obj->getPos()); if (parent_obj->isClass()) clone_obj->setSuperClassSelector(parent_obj->getPos()); @@ -154,14 +154,14 @@ reg_t kDisposeClone(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } - if (victim_obj->getInfoSelector().offset != SCRIPT_INFO_CLONE) { + if (!victim_obj->isClone()) { //warning("Attempt to dispose something other than a clone at %04x", offset); // SCI silently ignores this behaviour; some games actually depend on it return s->r_acc; } // QFG3 clears clones with underbits set - //if (GET_SEL32V(victim_addr, underBits)) + //if (readSelectorValue(victim_addr, underBits)) // warning("Clone %04x:%04x was cleared with underBits set", PRINT_REG(victim_addr)); #if 0 @@ -181,7 +181,7 @@ reg_t kDisposeClone(EngineState *s, int argc, reg_t *argv) { // Returns script dispatch address index in the supplied script reg_t kScriptID(EngineState *s, int argc, reg_t *argv) { int script = argv[0].toUint16(); - int index = (argc > 1) ? argv[1].toUint16() : 0; + uint16 index = (argc > 1) ? argv[1].toUint16() : 0; if (argv[0].segment) return argv[0]; @@ -193,18 +193,30 @@ reg_t kScriptID(EngineState *s, int argc, reg_t *argv) { Script *scr = s->_segMan->getScript(scriptSeg); - if (!scr->_numExports) { - // FIXME: Is this fatal? This occurs in SQ4CD - warning("Script 0x%x does not have a dispatch table", script); + if (!scr->getExportsNr()) { + // This is normal. Some scripts don't have a dispatch (exports) table, + // and this call is probably used to load them in memory, ignoring + // the return value. If only one argument is passed, this call is done + // only to load the script in memory. Thus, don't show any warning, + // as no return value is expected + if (argc == 2) + warning("Script 0x%x does not have a dispatch table and export %d " + "was requested from it", script, index); return NULL_REG; } - if (index > scr->_numExports) { - error("Dispatch index too big: %d > %d", index, scr->_numExports); + if (index > scr->getExportsNr()) { + error("Dispatch index too big: %d > %d", index, scr->getExportsNr()); return NULL_REG; } - return make_reg(scriptSeg, scr->validateExportFunc(index)); + uint16 address = scr->validateExportFunc(index); + + // Point to the heap for SCI1.1+ games + if (getSciVersion() >= SCI_VERSION_1_1) + address += scr->getScriptSize(); + + return make_reg(scriptSeg, address); } reg_t kDisposeScript(EngineState *s, int argc, reg_t *argv) { @@ -244,7 +256,7 @@ reg_t kRespondsTo(EngineState *s, int argc, reg_t *argv) { reg_t obj = argv[0]; int selector = argv[1].toUint16(); - return make_reg(0, s->_segMan->isHeapObject(obj) && lookup_selector(s->_segMan, obj, selector, NULL, NULL) != kSelectorNone); + return make_reg(0, s->_segMan->isHeapObject(obj) && lookupSelector(s->_segMan, obj, selector, NULL, NULL) != kSelectorNone); } } // End of namespace Sci diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp index 426c682e11..2681b612e9 100644 --- a/engines/sci/engine/kstring.cpp +++ b/engines/sci/engine/kstring.cpp @@ -138,10 +138,36 @@ reg_t kReadNumber(EngineState *s, int argc, reg_t *argv) { while (isspace((unsigned char)*source)) source++; /* Skip whitespace */ - if (*source == '$') /* SCI uses this for hex numbers */ - return make_reg(0, (int16)strtol(source + 1, NULL, 16)); /* Hex */ - else - return make_reg(0, (int16)strtol(source, NULL, 10)); /* Force decimal */ + int16 result = 0; + + if (*source == '$') { + // hexadecimal input + result = (int16)strtol(source + 1, NULL, 16); + } else { + // decimal input, we can not use strtol/atoi in here, because sierra used atoi BUT it was a non standard compliant + // atoi, that didnt do clipping. In SQ4 we get the door code in here and that's even larger than uint32! + if (*source == '-') { + result = -1; + source++; + } + while (*source) { + if ((*source < '0') || (*source > '9')) { + // Sierras atoi stopped processing at anything different than number + // Sometimes the input has a trailing space, that's fine (example: lsl3) + if (*source != ' ') { + // TODO: this happens in lsl5 right in the intro -> we get '1' '3' 0xCD 0xCD 0xCD 0xCD 0xCD + // find out why this happens and fix it + warning("Invalid character in kReadNumber input"); + } + break; + } + result *= 10; + result += *source - 0x30; + source++; + } + } + + return make_reg(0, result); } @@ -241,7 +267,7 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) { #ifdef ENABLE_SCI32 // If the string is a string object, get to the actual string in the data selector if (s->_segMan->isObject(reg)) - reg = GET_SEL32(s->_segMan, reg, SELECTOR(data)); + reg = readSelector(s->_segMan, reg, SELECTOR(data)); #endif Common::String tempsource = (reg == NULL_REG) ? "" : g_sci->getKernel()->lookupText(reg, diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index 8dc06dab01..9bf23dedf5 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -383,7 +383,7 @@ void EngineState::saveLoadWithSerializer(Common::Serializer &s) { sync_SegManagerPtr(s, _segMan); - syncArray<Class>(s, _segMan->_classtable); + syncArray<Class>(s, _segMan->_classTable); #ifdef USE_OLD_MUSIC_FUNCTIONS sync_songlib(s, _sound._songlib); @@ -541,8 +541,8 @@ void Script::saveLoadWithSerializer(Common::Serializer &s) { } } - s.syncAsSint32LE(_numExports); - s.syncAsSint32LE(_numSynonyms); + s.skip(4, VER(9), VER(19)); // OBSOLETE: Used to be _numExports + s.skip(4, VER(9), VER(19)); // OBSOLETE: Used to be _numSynonyms s.syncAsSint32LE(_lockers); // Sync _objects. This is a hashmap, and we use the following on disk format: @@ -615,7 +615,7 @@ void DynMem::saveLoadWithSerializer(Common::Serializer &s) { void DataStack::saveLoadWithSerializer(Common::Serializer &s) { s.syncAsUint32LE(_capacity); if (s.isLoading()) { - //free(entries); + free(_entries); _entries = (reg_t *)calloc(_capacity, sizeof(reg_t)); } } @@ -731,15 +731,6 @@ int gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savename return 1; } -/* - if (s->sound_server) { - if ((s->sound_server->save)(s, dirname)) { - warning("Saving failed for the sound subsystem"); - //chdir(".."); - return 1; - } - } -*/ Common::Serializer ser(0, fh); sync_SavegameMetadata(ser, meta); Graphics::saveThumbnail(*fh); @@ -748,26 +739,6 @@ int gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savename return 0; } -static byte *find_unique_script_block(EngineState *s, byte *buf, int type) { - bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); - - if (oldScriptHeader) - buf += 2; - - do { - int seeker_type = READ_LE_UINT16(buf); - - if (seeker_type == 0) break; - if (seeker_type == type) return buf; - - int seeker_size = READ_LE_UINT16(buf + 2); - assert(seeker_size > 0); - buf += seeker_size; - } while (1); - - return NULL; -} - // TODO: This should probably be turned into an EngineState or DataStack method. static void reconstruct_stack(EngineState *retval) { SegmentId stack_seg = retval->_segMan->findSegmentByType(SEG_TYPE_STACK); @@ -777,99 +748,37 @@ static void reconstruct_stack(EngineState *retval) { retval->stack_top = stack->_entries + stack->_capacity; } -static void load_script(EngineState *s, Script *scr) { - scr->_buf = (byte *)malloc(scr->_bufSize); - assert(scr->_buf); - - Resource *script = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, scr->_nr), 0); - assert(script != 0); - - assert(scr->_bufSize >= script->size); - memcpy(scr->_buf, script->data, script->size); - - if (getSciVersion() >= SCI_VERSION_1_1) { - Resource *heap = g_sci->getResMan()->findResource(ResourceId(kResourceTypeHeap, scr->_nr), 0); - assert(heap != 0); - - scr->_heapStart = scr->_buf + scr->_scriptSize; - - assert(scr->_bufSize - scr->_scriptSize <= heap->size); - memcpy(scr->_heapStart, heap->data, heap->size); - } -} - // TODO: Move thie function to a more appropriate place, such as vm.cpp or script.cpp void SegManager::reconstructScripts(EngineState *s) { uint i; - SegmentObj *mobj; for (i = 0; i < _heap.size(); i++) { - mobj = _heap[i]; - if (!mobj || mobj->getType() != SEG_TYPE_SCRIPT) + if (!_heap[i] || _heap[i]->getType() != SEG_TYPE_SCRIPT) continue; - Script *scr = (Script *)mobj; - - // FIXME: Unify this code with script_instantiate_* ? - load_script(s, scr); + Script *scr = (Script *)_heap[i]; + scr->load(g_sci->getResMan()); scr->_localsBlock = (scr->_localsSegment == 0) ? NULL : (LocalVariables *)(_heap[scr->_localsSegment]); - if (getSciVersion() >= SCI_VERSION_1_1) { - scr->_exportTable = 0; - scr->_synonyms = 0; - if (READ_LE_UINT16(scr->_buf + 6) > 0) { - scr->setExportTableOffset(6); - s->_segMan->scriptRelocateExportsSci11(i); - } - } else { - scr->_exportTable = (uint16 *) find_unique_script_block(s, scr->_buf, SCI_OBJ_EXPORTS); - scr->_synonyms = find_unique_script_block(s, scr->_buf, SCI_OBJ_SYNONYMS); - scr->_exportTable += 3; - } - scr->_codeBlocks.clear(); - ObjMap::iterator it; - const ObjMap::iterator end = scr->_objects.end(); - for (it = scr->_objects.begin(); it != end; ++it) { - byte *data = scr->_buf + it->_value.getPos().offset; - it->_value._baseObj = data; - } + for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it) + it->_value._baseObj = scr->_buf + it->_value.getPos().offset; } for (i = 0; i < _heap.size(); i++) { - mobj = _heap[i]; - if (!mobj || mobj->getType() != SEG_TYPE_SCRIPT) + if (!_heap[i] || _heap[i]->getType() != SEG_TYPE_SCRIPT) continue; - Script *scr = (Script *)mobj; + Script *scr = (Script *)_heap[i]; - // FIXME: Unify this code with Script::scriptObjInit ? - ObjMap::iterator it; - const ObjMap::iterator end = scr->_objects.end(); - for (it = scr->_objects.begin(); it != end; ++it) { - byte *data = scr->_buf + it->_value.getPos().offset; - - if (getSciVersion() >= SCI_VERSION_1_1) { - uint16 *funct_area = (uint16 *)(scr->_buf + READ_LE_UINT16( data + 6 )); - uint16 *prop_area = (uint16 *)(scr->_buf + READ_LE_UINT16( data + 4 )); - - it->_value._baseMethod = funct_area; - it->_value._baseVars = prop_area; - } else { - int funct_area = READ_LE_UINT16(data + SCRIPT_FUNCTAREAPTR_OFFSET); - Object *_baseObj; - - _baseObj = s->_segMan->getObject(it->_value.getSpeciesSelector()); - - if (!_baseObj) { - warning("Object without a base class: Script %d, index %d (reg address %04x:%04x", - scr->_nr, i, PRINT_REG(it->_value.getSpeciesSelector())); - continue; - } - it->_value.setVarCount(_baseObj->getVarCount()); - it->_value._baseObj = _baseObj->_baseObj; + for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it) { + reg_t addr = it->_value.getPos(); + Object *obj = scr->scriptObjInit(addr, false); - it->_value._baseMethod = (uint16 *)(data + funct_area); - it->_value._baseVars = (uint16 *)(data + it->_value.getVarCount() * 2 + SCRIPT_SELECTOR_OFFSET); + if (getSciVersion() < SCI_VERSION_1_1) { + if (!obj->initBaseObject(this, addr, false)) { + warning("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr)); + scr->scriptObjRemove(addr); + } } } } @@ -912,7 +821,6 @@ static void reconstruct_sounds(EngineState *s) { #endif void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) { - EngineState *retval; #ifdef USE_OLD_MUSIC_FUNCTIONS SongLibrary temp; #endif @@ -947,84 +855,67 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) { thumbnail = 0; } - // Create a new EngineState object - retval = new EngineState(s->_voc, s->_segMan); - retval->_event = s->_event; - - // Copy some old data - retval->_soundCmd = s->_soundCmd; - - // Copy memory segment - retval->_memorySegmentSize = s->_memorySegmentSize; - memcpy(retval->_memorySegment, s->_memorySegment, s->_memorySegmentSize); - - retval->saveLoadWithSerializer(ser); // FIXME: Error handling? + s->reset(true); + s->saveLoadWithSerializer(ser); // FIXME: Error handling? #ifdef USE_OLD_MUSIC_FUNCTIONS s->_sound.sfx_exit(); #endif // Set exec stack base to zero - retval->execution_stack_base = 0; + s->execution_stack_base = 0; // Now copy all current state information #ifdef USE_OLD_MUSIC_FUNCTIONS - temp = retval->_sound._songlib; - retval->_sound.sfx_init(g_sci->getResMan(), s->sfx_init_flags, g_sci->_features->detectDoSoundType()); - retval->sfx_init_flags = s->sfx_init_flags; - retval->_sound._songlib.freeSounds(); - retval->_sound._songlib = temp; - retval->_soundCmd->updateSfxState(&retval->_sound); + temp = s->_sound._songlib; + s->_sound.sfx_init(g_sci->getResMan(), s->sfx_init_flags, g_sci->_features->detectDoSoundType()); + s->sfx_init_flags = s->sfx_init_flags; + s->_sound._songlib.freeSounds(); + s->_sound._songlib = temp; + s->_soundCmd->updateSfxState(&retval->_sound); #endif - reconstruct_stack(retval); - retval->_segMan->reconstructScripts(retval); - retval->_segMan->reconstructClones(); - retval->_gameObj = s->_gameObj; - retval->script_000 = retval->_segMan->getScript(retval->_segMan->getScriptSegment(0, SCRIPT_GET_DONT_LOAD)); - retval->gc_countdown = GC_INTERVAL - 1; - retval->sys_strings_segment = retval->_segMan->findSegmentByType(SEG_TYPE_SYS_STRINGS); - retval->sys_strings = (SystemStrings *)(retval->_segMan->_heap[retval->sys_strings_segment]); + reconstruct_stack(s); + s->_segMan->reconstructScripts(s); + s->_segMan->reconstructClones(); + s->_gameObj = s->_gameObj; + s->script_000 = s->_segMan->getScript(s->_segMan->getScriptSegment(0, SCRIPT_GET_DONT_LOAD)); + s->gc_countdown = GC_INTERVAL - 1; // Time state: - retval->last_wait_time = g_system->getMillis(); - retval->game_start_time = g_system->getMillis(); - - // static parser information: - - if (retval->_voc) - retval->_voc->parser_base = make_reg(s->sys_strings_segment, SYS_STRING_PARSER_BASE); + s->last_wait_time = g_system->getMillis(); + s->game_start_time = g_system->getMillis(); - retval->successor = NULL; + s->restoring = false; #ifdef USE_OLD_MUSIC_FUNCTIONS - retval->_sound._it = NULL; - retval->_sound._flags = s->_sound._flags; - retval->_sound._song = NULL; - retval->_sound._suspended = s->_sound._suspended; - reconstruct_sounds(retval); + s->_sound._it = NULL; + s->_sound._flags = s->_sound._flags; + s->_sound._song = NULL; + s->_sound._suspended = s->_sound._suspended; + reconstruct_sounds(s); #else - retval->_soundCmd->reconstructPlayList(meta.savegame_version); + s->_soundCmd->reconstructPlayList(meta.savegame_version); #endif // Message state: - retval->_msgState = new MessageState(retval->_segMan); + s->_msgState = new MessageState(s->_segMan); #ifdef ENABLE_SCI32 if (g_sci->_gui32) { g_sci->_gui32->init(); } else { #endif - g_sci->_gui->resetEngineState(retval); + g_sci->_gui->resetEngineState(s); g_sci->_gui->init(g_sci->_features->usesOldGfxFunctions()); #ifdef ENABLE_SCI32 } #endif - s->successor = retval; // Set successor - script_abort_flag = 2; // Abort current game with replay + s->restoring = true; + s->script_abort_flag = 2; // Abort current game with replay s->shrinkStackToBase(); } diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h index bad79fca27..7be05381da 100644 --- a/engines/sci/engine/savegame.h +++ b/engines/sci/engine/savegame.h @@ -36,7 +36,7 @@ namespace Sci { struct EngineState; enum { - CURRENT_SAVEGAME_VERSION = 19, + CURRENT_SAVEGAME_VERSION = 20, MINIMUM_SAVEGAME_VERSION = 9 }; diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index e9b1ce3f28..1f32e50b67 100644 --- a/engines/sci/engine/script.cpp +++ b/engines/sci/engine/script.cpp @@ -123,13 +123,13 @@ void SegManager::createClassTable() { error("SegManager: failed to open vocab 996"); int totalClasses = vocab996->size >> 2; - _classtable.resize(totalClasses); + _classTable.resize(totalClasses); for (uint16 classNr = 0; classNr < totalClasses; classNr++) { uint16 scriptNr = READ_SCI11ENDIAN_UINT16(vocab996->data + classNr * 4 + 2); - _classtable[classNr].reg = NULL_REG; - _classtable[classNr].script = scriptNr; + _classTable[classNr].reg = NULL_REG; + _classTable[classNr].script = scriptNr; } _resMan->unlockResource(vocab996); @@ -139,11 +139,11 @@ reg_t SegManager::getClassAddress(int classnr, ScriptLoadType lock, reg_t caller if (classnr == 0xffff) return NULL_REG; - if (classnr < 0 || (int)_classtable.size() <= classnr || _classtable[classnr].script < 0) { - error("[VM] Attempt to dereference class %x, which doesn't exist (max %x)", classnr, _classtable.size()); + if (classnr < 0 || (int)_classTable.size() <= classnr || _classTable[classnr].script < 0) { + error("[VM] Attempt to dereference class %x, which doesn't exist (max %x)", classnr, _classTable.size()); return NULL_REG; } else { - Class *the_class = &_classtable[classnr]; + Class *the_class = &_classTable[classnr]; if (!the_class->reg.segment) { getScriptSegment(the_class->script, lock); @@ -175,7 +175,7 @@ void SegManager::scriptInitialiseLocals(reg_t location) { Script *scr = getScript(location.segment); unsigned int count; - VERIFY(location.offset + 1 < (uint16)scr->_bufSize, "Locals beyond end of script\n"); + VERIFY(location.offset + 1 < (uint16)scr->getBufSize(), "Locals beyond end of script\n"); if (getSciVersion() >= SCI_VERSION_1_1) count = READ_SCI11ENDIAN_UINT16(scr->_buf + location.offset - 2); @@ -185,55 +185,38 @@ void SegManager::scriptInitialiseLocals(reg_t location) { scr->_localsOffset = location.offset; - if (!(location.offset + count * 2 + 1 < scr->_bufSize)) { - warning("Locals extend beyond end of script: offset %04x, count %x vs size %x", location.offset, count, (uint)scr->_bufSize); - count = (scr->_bufSize - location.offset) >> 1; + if (!(location.offset + count * 2 + 1 < scr->getBufSize())) { + warning("Locals extend beyond end of script: offset %04x, count %x vs size %x", location.offset, count, (uint)scr->getBufSize()); + count = (scr->getBufSize() - location.offset) >> 1; } LocalVariables *locals = allocLocalsSegment(scr, count); if (locals) { uint i; - byte *base = (byte *)(scr->_buf + location.offset); + const byte *base = (const byte *)(scr->_buf + location.offset); for (i = 0; i < count; i++) locals->_locals[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(base + i * 2)); } } -void SegManager::scriptRelocateExportsSci11(SegmentId seg) { - Script *scr = getScript(seg); - for (int i = 0; i < scr->_numExports; i++) { - /* We are forced to use an ugly heuristic here to distinguish function - exports from object/class exports. The former kind points into the - script resource, the latter into the heap resource. */ - uint16 location = READ_SCI11ENDIAN_UINT16((byte *)(scr->_exportTable + i)); - - if ((location < scr->_heapSize - 1) && (READ_SCI11ENDIAN_UINT16(scr->_heapStart + location) == SCRIPT_OBJECT_MAGIC_NUMBER)) { - WRITE_SCI11ENDIAN_UINT16((byte *)(scr->_exportTable + i), location + scr->_heapStart - scr->_buf); - } else { - // Otherwise it's probably a function export, - // and we don't need to do anything. - } - } -} - void SegManager::scriptInitialiseObjectsSci11(SegmentId seg) { Script *scr = getScript(seg); - byte *seeker = scr->_heapStart + 4 + READ_SCI11ENDIAN_UINT16(scr->_heapStart + 2) * 2; + const byte *seeker = scr->_heapStart + 4 + READ_SCI11ENDIAN_UINT16(scr->_heapStart + 2) * 2; while (READ_SCI11ENDIAN_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) { - if (READ_SCI11ENDIAN_UINT16(seeker + 14) & SCRIPT_INFO_CLASS) { + if (READ_SCI11ENDIAN_UINT16(seeker + 14) & kInfoFlagClass) { // -info- selector int classpos = seeker - scr->_buf; int species = READ_SCI11ENDIAN_UINT16(seeker + 10); - if (species < 0 || species >= (int)_classtable.size()) { + if (species < 0 || species >= (int)_classTable.size()) { error("Invalid species %d(0x%x) not in interval [0,%d) while instantiating script %d", - species, species, _classtable.size(), scr->_nr); + species, species, _classTable.size(), scr->_nr); return; } - _classtable[species].reg.segment = seg; - _classtable[species].reg.offset = classpos; + _classTable[species].reg.segment = seg; + _classTable[species].reg.offset = classpos; } seeker += READ_SCI11ENDIAN_UINT16(seeker + 2) * 2; } @@ -243,19 +226,20 @@ void SegManager::scriptInitialiseObjectsSci11(SegmentId seg) { reg_t reg = make_reg(seg, seeker - scr->_buf); Object *obj = scr->scriptObjInit(reg); -#if 0 - if (obj->_variables[5].offset != 0xffff) { - obj->_variables[5] = INST_LOOKUP_CLASS(obj->_variables[5].offset); - baseObj = getObject(obj->_variables[5]); - obj->variable_names_nr = baseObj->variables_nr; - obj->_baseObj = baseObj->_baseObj; - } -#endif - // Copy base from species class, as we need its selector IDs obj->setSuperClassSelector( getClassAddress(obj->getSuperClassSelector().offset, SCRIPT_GET_LOCK, NULL_REG)); + // If object is instance, get -propDict- from class and set it for this object + // This is needed for ::isMemberOf() to work. + // Example testcase - room 381 of sq4cd - if isMemberOf() doesn't work, talk-clicks on the robot will act like + // clicking on ego + if (!obj->isClass()) { + reg_t classObject = obj->getSuperClassSelector(); + Object *classObj = getObject(classObject); + obj->setPropDictSelector(classObj->getPropDictSelector()); + } + // Set the -classScript- selector to the script number. // FIXME: As this selector is filled in at run-time, it is likely // that it is supposed to hold a pointer. The Obj::isKindOf method @@ -268,86 +252,24 @@ void SegManager::scriptInitialiseObjectsSci11(SegmentId seg) { } } - - -int script_instantiate_common(ResourceManager *resMan, SegManager *segMan, int script_nr, Resource **script, Resource **heap, int *was_new) { - *was_new = 1; - - *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0); - if (getSciVersion() >= SCI_VERSION_1_1) - *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0); - - if (!*script || (getSciVersion() >= SCI_VERSION_1_1 && !heap)) { - warning("Script 0x%x requested but not found", script_nr); - if (getSciVersion() >= SCI_VERSION_1_1) { - if (*heap) - warning("Inconsistency: heap resource WAS found"); - else if (*script) - warning("Inconsistency: script resource WAS found"); - } - return 0; - } - - SegmentId seg_id = segMan->getScriptSegment(script_nr); - Script *scr = segMan->getScriptIfLoaded(seg_id); - if (scr) { - if (!scr->isMarkedAsDeleted()) { - scr->incrementLockers(); - return seg_id; - } else { - scr->freeScript(); - } - } else { - scr = segMan->allocateScript(script_nr, &seg_id); - if (!scr) { // ALL YOUR SCRIPT BASE ARE BELONG TO US - error("Not enough heap space for script size 0x%x of script 0x%x (Should this happen?)", (*script)->size, script_nr); - return 0; - } - } - - scr->init(script_nr, resMan); - - // Set heap position (beyond the size word) - scr->setLockers(1); - scr->setExportTableOffset(0); - scr->setSynonymsOffset(0); - scr->setSynonymsNr(0); - - *was_new = 0; - - return seg_id; -} - -#define INST_LOOKUP_CLASS(id) ((id == 0xffff)? NULL_REG : segMan->getClassAddress(id, SCRIPT_GET_LOCK, addr)) - -int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int script_nr) { +void script_instantiate_sci0(Script *scr, int segmentId, SegManager *segMan) { int objType; uint32 objLength = 0; - int relocation = -1; - Resource *script; - int was_new; bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); - const int seg_id = script_instantiate_common(resMan, segMan, script_nr, &script, NULL, &was_new); uint16 curOffset = oldScriptHeader ? 2 : 0; - if (was_new) - return seg_id; - - Script *scr = segMan->getScript(seg_id); - scr->mcpyInOut(0, script->data, script->size); - if (oldScriptHeader) { // Old script block // There won't be a localvar block in this case // Instead, the script starts with a 16 bit int specifying the // number of locals we need; these are then allocated and zeroed. - int locals_nr = READ_LE_UINT16(script->data); - if (locals_nr) - segMan->scriptInitialiseLocalsZero(seg_id, locals_nr); + int localsCount = READ_LE_UINT16(scr->_buf); + if (localsCount) + segMan->scriptInitialiseLocalsZero(segmentId, localsCount); } // Now do a first pass through the script objects to find the - // export table and local variable block + // local variable blocks do { objType = scr->getHeap(curOffset); @@ -355,48 +277,30 @@ int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int scr break; objLength = scr->getHeap(curOffset + 2); - - // This happens in some demos (e.g. the EcoQuest 1 demo). Not sure what is the - // actual cause of it, but the scripts of these demos can't be loaded properly - // and we're stuck forever in this loop, as objLength never changes - if (!objLength) { - warning("script_instantiate_sci0: objLength is 0, unable to parse script"); - return 0; - } - curOffset += 4; // skip header switch (objType) { - case SCI_OBJ_EXPORTS: - scr->setExportTableOffset(curOffset); - break; - case SCI_OBJ_SYNONYMS: - scr->setSynonymsOffset(curOffset); - scr->setSynonymsNr((objLength) / 4); - break; case SCI_OBJ_LOCALVARS: - segMan->scriptInitialiseLocals(make_reg(seg_id, curOffset)); + segMan->scriptInitialiseLocals(make_reg(segmentId, curOffset)); break; - case SCI_OBJ_CLASS: { int classpos = curOffset - SCRIPT_OBJECT_MAGIC_OFFSET; int species = scr->getHeap(curOffset - SCRIPT_OBJECT_MAGIC_OFFSET + SCRIPT_SPECIES_OFFSET); - if (species < 0 || species >= (int)segMan->_classtable.size()) { - if (species == (int)segMan->_classtable.size()) { + if (species < 0 || species >= (int)segMan->classTableSize()) { + if (species == (int)segMan->classTableSize()) { // Happens in the LSL2 demo warning("Applying workaround for an off-by-one invalid species access"); - segMan->_classtable.resize(segMan->_classtable.size() + 1); + segMan->resizeClassTable(segMan->classTableSize() + 1); } else { - warning("Invalid species %d(0x%x) not in interval " - "[0,%d) while instantiating script %d\n", - species, species, segMan->_classtable.size(), - script_nr); - return 0; + error("Invalid species %d(0x%x) not in interval " + "[0,%d) while instantiating script at segment %d\n", + species, species, segMan->classTableSize(), + segmentId); + return; } } - segMan->_classtable[species].reg.segment = seg_id; - segMan->_classtable[species].reg.offset = classpos; + segMan->setClassOffset(species, make_reg(segmentId, classpos)); // Set technical class position-- into the block allocated for it } break; @@ -406,7 +310,7 @@ int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int scr } curOffset += objLength - 4; - } while (objType != 0 && curOffset < script->size - 2); + } while (objType != 0 && curOffset < scr->getScriptSize() - 2); // And now a second pass to adjust objects and class pointers, and the general pointers objLength = 0; @@ -420,7 +324,7 @@ int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int scr objLength = scr->getHeap(curOffset + 2); curOffset += 4; // skip header - reg_t addr = make_reg(seg_id, curOffset); + reg_t addr = make_reg(segmentId, curOffset); switch (objType) { case SCI_OBJ_CODE: @@ -429,77 +333,52 @@ int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int scr case SCI_OBJ_OBJECT: case SCI_OBJ_CLASS: { // object or class? Object *obj = scr->scriptObjInit(addr); + obj->initSpecies(segMan, addr); - // Instantiate the superclass, if neccessary - obj->setSpeciesSelector(INST_LOOKUP_CLASS(obj->getSpeciesSelector().offset)); - - Object *baseObj = segMan->getObject(obj->getSpeciesSelector()); - - if (baseObj) { - obj->setVarCount(baseObj->getVarCount()); - // Copy base from species class, as we need its selector IDs - obj->_baseObj = baseObj->_baseObj; - - obj->setSuperClassSelector(INST_LOOKUP_CLASS(obj->getSuperClassSelector().offset)); - } else { + if (!obj->initBaseObject(segMan, addr)) { warning("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr)); - scr->scriptObjRemove(addr); } } // if object or class break; - case SCI_OBJ_POINTERS: // A relocation table - relocation = addr.offset; - break; - default: break; } curOffset += objLength - 4; - } while (objType != 0 && curOffset < script->size - 2); - - if (relocation >= 0) - scr->scriptRelocate(make_reg(seg_id, relocation)); - - return seg_id; // instantiation successful + } while (objType != 0 && curOffset < scr->getScriptSize() - 2); } -int script_instantiate_sci11(ResourceManager *resMan, SegManager *segMan, int script_nr) { - Resource *script, *heap; - int was_new; - const int seg_id = script_instantiate_common(resMan, segMan, script_nr, &script, &heap, &was_new); - - if (was_new) - return seg_id; - - Script *scr = segMan->getScript(seg_id); - int _heapStart = script->size; - - if (script->size & 2) - _heapStart++; - - scr->mcpyInOut(0, script->data, script->size); - scr->mcpyInOut(_heapStart, heap->data, heap->size); - - if (READ_SCI11ENDIAN_UINT16(script->data + 6) > 0) - scr->setExportTableOffset(6); - - segMan->scriptInitialiseLocals(make_reg(seg_id, _heapStart + 4)); - - segMan->scriptRelocateExportsSci11(seg_id); - segMan->scriptInitialiseObjectsSci11(seg_id); +int script_instantiate(ResourceManager *resMan, SegManager *segMan, int scriptNum) { + SegmentId segmentId = segMan->getScriptSegment(scriptNum); + Script *scr = segMan->getScriptIfLoaded(segmentId); + if (scr) { + if (!scr->isMarkedAsDeleted()) { + scr->incrementLockers(); + return segmentId; + } else { + scr->freeScript(); + } + } else { + scr = segMan->allocateScript(scriptNum, &segmentId); + } - scr->heapRelocate(make_reg(seg_id, READ_SCI11ENDIAN_UINT16(heap->data))); + scr->init(scriptNum, resMan); + scr->load(resMan); - return seg_id; -} + if (getSciVersion() >= SCI_VERSION_1_1) { + int heapStart = scr->getScriptSize(); + segMan->scriptInitialiseLocals(make_reg(segmentId, heapStart + 4)); + segMan->scriptInitialiseObjectsSci11(segmentId); + scr->relocate(make_reg(segmentId, READ_SCI11ENDIAN_UINT16(scr->_heapStart))); + } else { + script_instantiate_sci0(scr, segmentId, segMan); + byte *relocationBlock = scr->findBlock(SCI_OBJ_POINTERS); + if (relocationBlock) + scr->relocate(make_reg(segmentId, relocationBlock - scr->_buf + 4)); + } -int script_instantiate(ResourceManager *resMan, SegManager *segMan, int script_nr) { - if (getSciVersion() >= SCI_VERSION_1_1) - return script_instantiate_sci11(resMan, segMan, script_nr); - else - return script_instantiate_sci0(resMan, segMan, script_nr); + return segmentId; } void script_uninstantiate_sci0(SegManager *segMan, int script_nr, SegmentId seg) { @@ -528,7 +407,7 @@ void script_uninstantiate_sci0(SegManager *segMan, int script_nr, SegmentId seg) superclass = scr->getHeap(reg.offset + SCRIPT_SUPERCLASS_OFFSET); // Get superclass... if (superclass >= 0) { - int superclass_script = segMan->_classtable[superclass].script; + int superclass_script = segMan->getClass(superclass).script; if (superclass_script == script_nr) { if (scr->getLockers()) @@ -562,9 +441,9 @@ void script_uninstantiate(SegManager *segMan, int script_nr) { return; // Free all classtable references to this script - for (uint i = 0; i < segMan->_classtable.size(); i++) - if (segMan->_classtable[i].reg.segment == segment) - segMan->_classtable[i].reg = NULL_REG; + for (uint i = 0; i < segMan->classTableSize(); i++) + if (segMan->getClass(i).reg.segment == segment) + segMan->setClassOffset(i, NULL_REG); if (getSciVersion() < SCI_VERSION_1_1) script_uninstantiate_sci0(segMan, script_nr, segment); diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp index 4b60626b2e..b465ab3d4e 100644 --- a/engines/sci/engine/scriptdebug.cpp +++ b/engines/sci/engine/scriptdebug.cpp @@ -67,37 +67,6 @@ extern const char *selector_name(EngineState *s, int selector); DebugState g_debugState; -int propertyOffsetToId(SegManager *segMan, int prop_ofs, reg_t objp) { - Object *obj = segMan->getObject(objp); - byte *selectoroffset; - int selectors; - - if (!obj) { - warning("Applied propertyOffsetToId on non-object at %04x:%04x", PRINT_REG(objp)); - return -1; - } - - selectors = obj->getVarCount(); - - if (getSciVersion() < SCI_VERSION_1_1) - selectoroffset = ((byte *)(obj->_baseObj)) + SCRIPT_SELECTOR_OFFSET + selectors * 2; - else { - if (!(obj->getInfoSelector().offset & SCRIPT_INFO_CLASS)) { - obj = segMan->getObject(obj->getSuperClassSelector()); - selectoroffset = (byte *)obj->_baseVars; - } else - selectoroffset = (byte *)obj->_baseVars; - } - - if (prop_ofs < 0 || (prop_ofs >> 1) >= selectors) { - warning("Applied propertyOffsetToId to invalid property offset %x (property #%d not in [0..%d]) on object at %04x:%04x", - prop_ofs, prop_ofs >> 1, selectors - 1, PRINT_REG(objp)); - return -1; - } - - return READ_SCI11ENDIAN_UINT16(selectoroffset + prop_ofs); -} - // Disassembles one command from the heap, returns address of next command or 0 if a ret was encountered. reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecode) { SegmentObj *mobj = s->_segMan->getSegment(pos.segment, SEG_TYPE_SCRIPT); @@ -116,7 +85,7 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod script_entity = (Script *)mobj; scr = script_entity->_buf; - scr_size = script_entity->_bufSize; + scr_size = script_entity->getBufSize(); if (pos.offset >= scr_size) { warning("Trying to disassemble beyond end of script"); @@ -221,51 +190,52 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod } } - if (pos == scriptState.xs->addr.pc) { // Extra information if debugging the current opcode + if (pos == s->xs->addr.pc) { // Extra information if debugging the current opcode if ((opcode == op_pTos) || (opcode == op_sTop) || (opcode == op_pToa) || (opcode == op_aTop) || (opcode == op_dpToa) || (opcode == op_ipToa) || (opcode == op_dpTos) || (opcode == op_ipTos)) { - int prop_ofs = scr[pos.offset + 1]; - int prop_id = propertyOffsetToId(s->_segMan, prop_ofs, scriptState.xs->objp); - - printf(" (%s)", selector_name(s, prop_id)); + const Object *obj = s->_segMan->getObject(s->xs->objp); + if (!obj) + warning("Attempted to reference on non-object at %04x:%04x", PRINT_REG(s->xs->objp)); + else + printf(" (%s)", selector_name(s, obj->propertyOffsetToId(s->_segMan, scr[pos.offset + 1]))); } } printf("\n"); - if (pos == scriptState.xs->addr.pc) { // Extra information if debugging the current opcode + if (pos == s->xs->addr.pc) { // Extra information if debugging the current opcode if (opcode == op_callk) { - int stackframe = (scr[pos.offset + 2] >> 1) + (scriptState.restAdjust); - int argc = ((scriptState.xs->sp)[- stackframe - 1]).offset; + int stackframe = (scr[pos.offset + 2] >> 1) + (s->restAdjustCur); + int argc = ((s->xs->sp)[- stackframe - 1]).offset; bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); if (!oldScriptHeader) - argc += (scriptState.restAdjust); + argc += (s->restAdjustCur); printf(" Kernel params: ("); for (int j = 0; j < argc; j++) { - printf("%04x:%04x", PRINT_REG((scriptState.xs->sp)[j - stackframe])); + printf("%04x:%04x", PRINT_REG((s->xs->sp)[j - stackframe])); if (j + 1 < argc) printf(", "); } printf(")\n"); } else if ((opcode == op_send) || (opcode == op_self)) { - int restmod = scriptState.restAdjust; + int restmod = s->restAdjustCur; int stackframe = (scr[pos.offset + 1] >> 1) + restmod; - reg_t *sb = scriptState.xs->sp; + reg_t *sb = s->xs->sp; uint16 selector; reg_t fun_ref; while (stackframe > 0) { int argc = sb[- stackframe + 1].offset; const char *name = NULL; - reg_t called_obj_addr = scriptState.xs->objp; + reg_t called_obj_addr = s->xs->objp; if (opcode == op_send) called_obj_addr = s->r_acc; else if (opcode == op_self) - called_obj_addr = scriptState.xs->objp; + called_obj_addr = s->xs->objp; selector = sb[- stackframe].offset; @@ -276,7 +246,7 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod printf(" %s::%s[", name, (selector > kernel->getSelectorNamesSize()) ? "<invalid>" : selector_name(s, selector)); - switch (lookup_selector(s->_segMan, called_obj_addr, selector, 0, &fun_ref)) { + switch (lookupSelector(s->_segMan, called_obj_addr, selector, 0, &fun_ref)) { case kSelectorMethod: printf("FUNCT"); argc += restmod; @@ -315,10 +285,10 @@ void script_debug(EngineState *s) { #if 0 if (sci_debug_flags & _DEBUG_FLAG_LOGGING) { printf("%d: acc=%04x:%04x ", script_step_counter, PRINT_REG(s->r_acc)); - disassemble(s, scriptState.xs->addr.pc, 0, 1); - if (scriptState.seeking == kDebugSeekGlobal) - printf("Global %d (0x%x) = %04x:%04x\n", scriptState.seekSpecial, - scriptState.seekSpecial, PRINT_REG(s->script_000->_localsBlock->_locals[scriptState.seekSpecial])); + disassemble(s, s->xs->addr.pc, 0, 1); + if (s->seeking == kDebugSeekGlobal) + printf("Global %d (0x%x) = %04x:%04x\n", s->seekSpecial, + s->seekSpecial, PRINT_REG(s->script_000->_localsBlock->_locals[s->seekSpecial])); } #endif @@ -328,16 +298,16 @@ void script_debug(EngineState *s) { #endif if (g_debugState.seeking && !g_debugState.breakpointWasHit) { // Are we looking for something special? - SegmentObj *mobj = s->_segMan->getSegment(scriptState.xs->addr.pc.segment, SEG_TYPE_SCRIPT); + SegmentObj *mobj = s->_segMan->getSegment(s->xs->addr.pc.segment, SEG_TYPE_SCRIPT); if (mobj) { Script *scr = (Script *)mobj; byte *code_buf = scr->_buf; - int code_buf_size = scr->_bufSize; - int opcode = scriptState.xs->addr.pc.offset >= code_buf_size ? 0 : code_buf[scriptState.xs->addr.pc.offset]; + int code_buf_size = scr->getBufSize(); + int opcode = s->xs->addr.pc.offset >= code_buf_size ? 0 : code_buf[s->xs->addr.pc.offset]; int op = opcode >> 1; - int paramb1 = scriptState.xs->addr.pc.offset + 1 >= code_buf_size ? 0 : code_buf[scriptState.xs->addr.pc.offset + 1]; - int paramf1 = (opcode & 1) ? paramb1 : (scriptState.xs->addr.pc.offset + 2 >= code_buf_size ? 0 : (int16)READ_SCI11ENDIAN_UINT16(code_buf + scriptState.xs->addr.pc.offset + 1)); + int paramb1 = s->xs->addr.pc.offset + 1 >= code_buf_size ? 0 : code_buf[s->xs->addr.pc.offset + 1]; + int paramf1 = (opcode & 1) ? paramb1 : (s->xs->addr.pc.offset + 2 >= code_buf_size ? 0 : (int16)READ_SCI11ENDIAN_UINT16(code_buf + s->xs->addr.pc.offset + 1)); switch (g_debugState.seeking) { case kDebugSeekSpecialCallk: @@ -381,8 +351,8 @@ void script_debug(EngineState *s) { } } - printf("Step #%d\n", script_step_counter); - disassemble(s, scriptState.xs->addr.pc, 0, 1); + printf("Step #%d\n", s->script_step_counter); + disassemble(s, s->xs->addr.pc, 0, 1); if (g_debugState.runningStep) { g_debugState.runningStep--; diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp index b18d76e1a7..4d3e6f754e 100644 --- a/engines/sci/engine/seg_manager.cpp +++ b/engines/sci/engine/seg_manager.cpp @@ -54,7 +54,6 @@ SegManager::SegManager(ResourceManager *resMan) { createClassTable(); } -// Destroy the object, free the memorys if allocated before SegManager::~SegManager() { resetSegMan(); } @@ -77,10 +76,29 @@ void SegManager::resetSegMan() { Hunks_seg_id = 0; // Reinitialize class table - _classtable.clear(); + _classTable.clear(); createClassTable(); } +void SegManager::initSysStrings() { + sysStrings = (SystemStrings *)allocSegment(new SystemStrings(), &sysStringsSegment); + + // Allocate static buffer for savegame and CWD directories + SystemString *strSaveDir = &sysStrings->_strings[SYS_STRING_SAVEDIR]; + strSaveDir->_name = "savedir"; + strSaveDir->_maxSize = MAX_SAVE_DIR_SIZE; + strSaveDir->_value = (char *)calloc(MAX_SAVE_DIR_SIZE, sizeof(char)); + // Set the savegame dir (actually, we set it to a fake value, + // since we cannot let the game control where saves are stored) + ::strcpy(strSaveDir->_value, ""); + + // Allocate static buffer for the parser base + SystemString *strParserBase = &sysStrings->_strings[SYS_STRING_PARSER_BASE]; + strParserBase->_name = "parser-base"; + strParserBase->_maxSize = MAX_PARSER_BASE; + strParserBase->_value = (char *)calloc(MAX_PARSER_BASE, sizeof(char)); +} + SegmentId SegManager::findFreeSegment() const { // FIXME: This is a very crude approach: We find a free segment id by scanning // from the start. This can be slow if the number of segments becomes large. @@ -156,7 +174,7 @@ int SegManager::deallocate(SegmentId seg, bool recursive) { } bool SegManager::isHeapObject(reg_t pos) { - Object *obj = getObject(pos); + const Object *obj = getObject(pos); if (obj == NULL || (obj && obj->isFreed())) return false; Script *scr = getScriptIfLoaded(pos.segment); @@ -223,7 +241,7 @@ Object *SegManager::getObject(reg_t pos) { warning("getObject(): Trying to get an invalid object"); } else if (mobj->getType() == SEG_TYPE_SCRIPT) { Script *scr = (Script *)mobj; - if (pos.offset <= scr->_bufSize && pos.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET + if (pos.offset <= scr->getBufSize() && pos.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET && RAW_IS_OBJECT(scr->_buf + pos.offset)) { obj = scr->getObject(pos.offset); } @@ -234,7 +252,7 @@ Object *SegManager::getObject(reg_t pos) { } const char *SegManager::getObjectName(reg_t pos) { - Object *obj = getObject(pos); + const Object *obj = getObject(pos); if (!obj) return "<no such object>"; @@ -275,7 +293,7 @@ reg_t SegManager::findObjectByName(const Common::String &name, int index) { // It's a script or a clone table, scan all objects in it for (; idx < max_index; ++idx) { - Object *obj = NULL; + const Object *obj = NULL; reg_t objpos; objpos.offset = 0; objpos.segment = i; @@ -393,10 +411,6 @@ DataStack *SegManager::allocateStack(int size, SegmentId *segid) { return retval; } -SystemStrings *SegManager::allocateSysStrings(SegmentId *segid) { - return (SystemStrings *)allocSegment(new SystemStrings(), segid); -} - void SegManager::freeHunkEntry(reg_t addr) { if (addr.isNull()) { warning("Attempt to free a Hunk from a null address"); @@ -485,7 +499,7 @@ void SegManager::reconstructClones() { continue; CloneTable::Entry &seeker = ct->_table[j]; - Object *baseObj = getObject(seeker.getSpeciesSelector()); + const Object *baseObj = getObject(seeker.getSpeciesSelector()); seeker.cloneFromObject(baseObj); if (!baseObj) warning("Clone entry without a base class: %d", j); @@ -523,6 +537,16 @@ Node *SegManager::allocateNode(reg_t *addr) { return &(table->_table[offset]); } +reg_t SegManager::newNode(reg_t value, reg_t key) { + reg_t nodebase; + Node *n = allocateNode(&nodebase); + n->pred = n->succ = NULL_REG; + n->key = key; + n->value = value; + + return nodebase; +} + List *SegManager::lookupList(reg_t addr) { if (getSegmentType(addr.segment) != SEG_TYPE_LISTS) { warning("Attempt to use non-list %04x:%04x as list", PRINT_REG(addr)); diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h index e8bbdbdb3f..9312f51f9d 100644 --- a/engines/sci/engine/seg_manager.h +++ b/engines/sci/engine/seg_manager.h @@ -112,12 +112,6 @@ public: SegmentId getScriptSegment(int script_nr, ScriptLoadType load); // TODO: document this - reg_t lookupScriptExport(int script_nr, int export_index) { - SegmentId seg = getScriptSegment(script_nr, SCRIPT_GET_DONT_LOAD); - return make_reg(seg, getScript(seg)->validateExportFunc(export_index)); - } - - // TODO: document this reg_t getClassAddress(int classnr, ScriptLoadType lock, reg_t caller); /** @@ -188,15 +182,10 @@ public: // 5. System Strings /** - * Allocates a system string table - * See also sys_string_acquire(); - * @param[in] segid Segment ID of the stack - * @returns The physical stack + * Initializes the system string table. */ - SystemStrings *allocateSysStrings(SegmentId *segid); - + void initSysStrings(); - // 5. System Strings // 6, 7. Lists and Nodes @@ -215,6 +204,14 @@ public: Node *allocateNode(reg_t *addr); /** + * Allocate and initialize a new list node. + * @param[in] value The value to set the node to + * @param[in] key The key to set + * @return Pointer to the newly initialized list node + */ + reg_t newNode(reg_t value, reg_t key); + + /** * Resolves a list pointer to a list. * @param addr The address to resolve * @return The list referenced, or NULL on error @@ -432,12 +429,22 @@ public: */ reg_t findObjectByName(const Common::String &name, int index = -1); - void scriptRelocateExportsSci11(SegmentId seg); void scriptInitialiseObjectsSci11(SegmentId seg); + uint32 classTableSize() { return _classTable.size(); } + Class getClass(int index) { return _classTable[index]; } + void setClassOffset(int index, reg_t offset) { _classTable[index].reg = offset; } + void resizeClassTable(uint32 size) { _classTable.resize(size); } + + /** + * Obtains the system strings segment ID + */ + SegmentId getSysStringsSegment() { return sysStringsSegment; } + public: // TODO: make private Common::Array<SegmentObj *> _heap; - Common::Array<Class> _classtable; /**< Table of all classes */ + // Only accessible from saveLoadWithSerializer() + Common::Array<Class> _classTable; /**< Table of all classes */ #ifdef ENABLE_SCI32 SciArray<reg_t> *allocateArray(reg_t *addr); @@ -460,6 +467,13 @@ private: SegmentId Nodes_seg_id; ///< ID of the (a) node segment SegmentId Hunks_seg_id; ///< ID of the (a) hunk segment + /* System strings */ + SegmentId sysStringsSegment; +public: // TODO: make private. Only kString() needs direct access + SystemStrings *sysStrings; + +private: + #ifdef ENABLE_SCI32 SegmentId Arrays_seg_id; SegmentId String_seg_id; diff --git a/engines/sci/engine/segment.cpp b/engines/sci/engine/segment.cpp index ab1a68d165..0e0a759d4b 100644 --- a/engines/sci/engine/segment.cpp +++ b/engines/sci/engine/segment.cpp @@ -27,6 +27,7 @@ #include "sci/sci.h" #include "sci/engine/features.h" +#include "sci/engine/script.h" // for SCI_OBJ_EXPORTS and SCI_OBJ_SYNONYMS #include "sci/engine/segment.h" #include "sci/engine/seg_manager.h" #include "sci/engine/state.h" @@ -100,8 +101,7 @@ Script::Script() : SegmentObj(SEG_TYPE_SCRIPT) { _localsSegment = 0; _localsBlock = NULL; - _relocated = false; - _markedAsDeleted = 0; + _markedAsDeleted = false; } Script::~Script() { @@ -117,54 +117,39 @@ void Script::freeScript() { _codeBlocks.clear(); } -bool Script::init(int script_nr, ResourceManager *resMan) { - setScriptSize(script_nr, resMan); - - _buf = (byte *)malloc(_bufSize); - - if (!_buf) { - freeScript(); - warning("Not enough memory space for script size"); - _bufSize = 0; - return false; - } +void Script::init(int script_nr, ResourceManager *resMan) { + Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0); _localsOffset = 0; _localsBlock = NULL; _codeBlocks.clear(); - _relocated = false; _markedAsDeleted = false; _nr = script_nr; - - if (getSciVersion() >= SCI_VERSION_1_1) - _heapStart = _buf + _scriptSize; - else - _heapStart = _buf; - - return true; -} - -void Script::setScriptSize(int script_nr, ResourceManager *resMan) { - Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0); - Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0); - bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); + _buf = 0; + _heapStart = 0; _scriptSize = script->size; - _heapSize = 0; // Set later + _bufSize = script->size; + _heapSize = 0; - if (!script || (getSciVersion() >= SCI_VERSION_1_1 && !heap)) { - error("SegManager::setScriptSize: failed to load %s", !script ? "script" : "heap"); - } - if (oldScriptHeader) { - _bufSize = script->size + READ_LE_UINT16(script->data) * 2; - //locals_size = READ_LE_UINT16(script->data) * 2; - } else if (getSciVersion() < SCI_VERSION_1_1) { - _bufSize = script->size; - } else { - _bufSize = script->size + heap->size; + _lockers = 1; + + if (getSciVersion() == SCI_VERSION_0_EARLY) { + _bufSize += READ_LE_UINT16(script->data) * 2; + } else if (getSciVersion() >= SCI_VERSION_1_1) { + /** + * In SCI11, the heap was in a separate space from the script. + * We append it to the end of the script, and adjust addressing accordingly. + * However, since we address the heap with a 16-bit pointer, the combined + * size of the stack and the heap must be 64KB. So far this has worked + * for SCI11, SCI2 and SCI21 games. SCI3 games use a different script format, + * and theoretically they can exceed the 64KB boundary using relocation. + */ + Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0); + _bufSize += heap->size; _heapSize = heap->size; // Ensure that the start of the heap resource can be word-aligned. @@ -173,12 +158,56 @@ void Script::setScriptSize(int script_nr, ResourceManager *resMan) { _scriptSize++; } - if (_bufSize > 65535) { - error("Script and heap sizes combined exceed 64K." - "This means a fundamental design bug was made in SCI\n" - "regarding SCI1.1 games.\nPlease report this so it can be" - "fixed in the next major version"); - return; + // As mentioned above, the script and the heap together should not exceed 64KB + if (_bufSize > 65535) + error("Script and heap sizes combined exceed 64K. This means a fundamental " + "design bug was made regarding SCI1.1 and newer games.\nPlease " + "report this error to the ScummVM team"); + } +} + +void Script::load(ResourceManager *resMan) { + Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, _nr), 0); + assert(script != 0); + + _buf = (byte *)malloc(_bufSize); + assert(_buf); + + assert(_bufSize >= script->size); + memcpy(_buf, script->data, script->size); + + if (getSciVersion() >= SCI_VERSION_1_1) { + Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0); + assert(heap != 0); + + _heapStart = _buf + _scriptSize; + + assert(_bufSize - _scriptSize <= heap->size); + memcpy(_heapStart, heap->data, heap->size); + } + + _codeBlocks.clear(); + + _exportTable = 0; + _numExports = 0; + _synonyms = 0; + _numSynonyms = 0; + + if (getSciVersion() >= SCI_VERSION_1_1) { + if (READ_LE_UINT16(_buf + 1 + 5) > 0) { + _exportTable = (const uint16 *)(_buf + 1 + 5 + 2); + _numExports = READ_SCI11ENDIAN_UINT16(_exportTable - 1); + } + } else { + _exportTable = (const uint16 *)findBlock(SCI_OBJ_EXPORTS); + if (_exportTable) { + _numExports = READ_SCI11ENDIAN_UINT16(_exportTable + 1); + _exportTable += 3; // skip header plus 2 bytes (_exportTable is a uint16 pointer) + } + _synonyms = findBlock(SCI_OBJ_SYNONYMS); + if (_synonyms) { + _numSynonyms = READ_SCI11ENDIAN_UINT16(_synonyms + 2) / 4; + _synonyms += 4; // skip header } } } @@ -194,19 +223,26 @@ Object *Script::getObject(uint16 offset) { return 0; } -Object *Script::scriptObjInit(reg_t obj_pos) { +const Object *Script::getObject(uint16 offset) const { + if (_objects.contains(offset)) + return &_objects[offset]; + else + return 0; +} + +Object *Script::scriptObjInit(reg_t obj_pos, bool fullObjectInit) { Object *obj; - if (getSciVersion() < SCI_VERSION_1_1) + if (getSciVersion() < SCI_VERSION_1_1 && fullObjectInit) obj_pos.offset += 8; // magic offset (SCRIPT_OBJECT_MAGIC_OFFSET) VERIFY(obj_pos.offset < _bufSize, "Attempt to initialize object beyond end of script\n"); obj = allocateObject(obj_pos.offset); - VERIFY(obj_pos.offset + SCRIPT_FUNCTAREAPTR_OFFSET < (int)_bufSize, "Function area pointer stored beyond end of script\n"); + VERIFY(obj_pos.offset + kOffsetFunctionArea < (int)_bufSize, "Function area pointer stored beyond end of script\n"); - obj->init(_buf, obj_pos); + obj->init(_buf, obj_pos, fullObjectInit); return obj; } @@ -218,37 +254,34 @@ void Script::scriptObjRemove(reg_t obj_pos) { _objects.erase(obj_pos.toUint16()); } -int Script::relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location) { +// This helper function is used by Script::relocateLocal and Object::relocate +static bool relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location, size_t scriptSize) { int rel = location - block_location; if (rel < 0) - return 0; + return false; uint idx = rel >> 1; if (idx >= block.size()) - return 0; + return false; if (rel & 1) { warning("Attempt to relocate odd variable #%d.5e (relative to %04x)\n", idx, block_location); - return 0; + return false; } block[idx].segment = segment; // Perform relocation if (getSciVersion() >= SCI_VERSION_1_1) - block[idx].offset += _scriptSize; + block[idx].offset += scriptSize; - return 1; + return true; } -int Script::relocateLocal(SegmentId segment, int location) { +bool Script::relocateLocal(SegmentId segment, int location) { if (_localsBlock) - return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location); + return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location, _scriptSize); else - return 0; // No hands, no cookies -} - -int Script::relocateObject(Object &obj, SegmentId segment, int location) { - return relocateBlock(obj._variables, obj.getPos().offset, segment, location); + return false; } void Script::scriptAddCodeBlock(reg_t location) { @@ -258,61 +291,35 @@ void Script::scriptAddCodeBlock(reg_t location) { _codeBlocks.push_back(cb); } -void Script::scriptRelocate(reg_t block) { - VERIFY(block.offset < (uint16)_bufSize && READ_SCI11ENDIAN_UINT16(_buf + block.offset) * 2 + block.offset < (uint16)_bufSize, - "Relocation block outside of script\n"); - - int count = READ_SCI11ENDIAN_UINT16(_buf + block.offset); - - for (int i = 0; i <= count; i++) { - int pos = READ_SCI11ENDIAN_UINT16(_buf + block.offset + 2 + (i * 2)); - if (!pos) - continue; // FIXME: A hack pending investigation - - if (!relocateLocal(block.segment, pos)) { - bool done = false; - uint k; - - ObjMap::iterator it; - const ObjMap::iterator end = _objects.end(); - for (it = _objects.begin(); !done && it != end; ++it) { - if (relocateObject(it->_value, block.segment, pos)) - done = true; - } - - for (k = 0; !done && k < _codeBlocks.size(); k++) { - if (pos >= _codeBlocks[k].pos.offset && - pos < _codeBlocks[k].pos.offset + _codeBlocks[k].size) - done = true; - } +void Script::relocate(reg_t block) { + byte *heap = _buf; + uint16 heapSize = (uint16)_bufSize; + uint16 heapOffset = 0; - if (!done) { - printf("While processing relocation block %04x:%04x:\n", PRINT_REG(block)); - printf("Relocation failed for index %04x (%d/%d)\n", pos, i + 1, count); - if (_localsBlock) - printf("- locals: %d at %04x\n", _localsBlock->_locals.size(), _localsOffset); - else - printf("- No locals\n"); - for (it = _objects.begin(), k = 0; it != end; ++it, ++k) - printf("- obj#%d at %04x w/ %d vars\n", k, it->_value.getPos().offset, it->_value.getVarCount()); - // SQ3 script 71 has broken relocation entries. - printf("Trying to continue anyway...\n"); - } - } + if (getSciVersion() >= SCI_VERSION_1_1) { + heap = _heapStart; + heapSize = (uint16)_heapSize; + heapOffset = _scriptSize; } -} -void Script::heapRelocate(reg_t block) { - VERIFY(block.offset < (uint16)_heapSize && READ_SCI11ENDIAN_UINT16(_heapStart + block.offset) * 2 + block.offset < (uint16)_bufSize, + VERIFY(block.offset < (uint16)heapSize && READ_SCI11ENDIAN_UINT16(heap + block.offset) * 2 + block.offset < (uint16)heapSize, "Relocation block outside of script\n"); - if (_relocated) - return; - _relocated = true; - int count = READ_SCI11ENDIAN_UINT16(_heapStart + block.offset); + int count = READ_SCI11ENDIAN_UINT16(heap + block.offset); + int exportIndex = 0; for (int i = 0; i < count; i++) { - int pos = READ_SCI11ENDIAN_UINT16(_heapStart + block.offset + 2 + (i * 2)) + _scriptSize; + int pos = READ_SCI11ENDIAN_UINT16(heap + block.offset + 2 + (exportIndex * 2)) + heapOffset; + // This occurs in SCI01/SCI1 games where every usually one export + // value is zero. It seems that in this situation, we should skip + // the export and move to the next one, though the total count + // of valid exports remains the same + if (!pos) { + exportIndex++; + pos = READ_SCI11ENDIAN_UINT16(heap + block.offset + 2 + (exportIndex * 2)) + heapOffset; + if (!pos) + error("Script::relocate(): Consecutive zero exports found"); + } if (!relocateLocal(block.segment, pos)) { bool done = false; @@ -321,22 +328,33 @@ void Script::heapRelocate(reg_t block) { ObjMap::iterator it; const ObjMap::iterator end = _objects.end(); for (it = _objects.begin(); !done && it != end; ++it) { - if (relocateObject(it->_value, block.segment, pos)) + if (it->_value.relocate(block.segment, pos, _scriptSize)) done = true; } + // Sanity check for SCI0-SCI1 + if (getSciVersion() < SCI_VERSION_1_1) { + for (k = 0; !done && k < _codeBlocks.size(); k++) { + if (pos >= _codeBlocks[k].pos.offset && + pos < _codeBlocks[k].pos.offset + _codeBlocks[k].size) + done = true; + } + } + if (!done) { - printf("While processing relocation block %04x:%04x:\n", PRINT_REG(block)); - printf("Relocation failed for index %04x (%d/%d)\n", pos, i + 1, count); + debug("While processing relocation block %04x:%04x:\n", PRINT_REG(block)); + debug("Relocation failed for index %04x (%d/%d)\n", pos, exportIndex + 1, count); if (_localsBlock) - printf("- locals: %d at %04x\n", _localsBlock->_locals.size(), _localsOffset); + debug("- locals: %d at %04x\n", _localsBlock->_locals.size(), _localsOffset); else - printf("- No locals\n"); + debug("- No locals\n"); for (it = _objects.begin(), k = 0; it != end; ++it, ++k) - printf("- obj#%d at %04x w/ %d vars\n", k, it->_value.getPos().offset, it->_value.getVarCount()); - error("Breakpoint in %s, line %d", __FILE__, __LINE__); + debug("- obj#%d at %04x w/ %d vars\n", k, it->_value.getPos().offset, it->_value.getVarCount()); + debug("Trying to continue anyway...\n"); } } + + exportIndex++; } } @@ -357,16 +375,6 @@ void Script::setLockers(int lockers) { _lockers = lockers; } -void Script::setExportTableOffset(int offset) { - if (offset) { - _exportTable = (uint16 *)(_buf + offset + 2); - _numExports = READ_SCI11ENDIAN_UINT16((byte *)(_exportTable - 1)); - } else { - _exportTable = NULL; - _numExports = 0; - } -} - uint16 Script::validateExportFunc(int pubfunct) { bool exportsAreWide = (g_sci->_features->detectLofsType() == SCI_VERSION_1_MIDDLE); @@ -377,28 +385,36 @@ uint16 Script::validateExportFunc(int pubfunct) { if (exportsAreWide) pubfunct *= 2; - uint16 offset = READ_SCI11ENDIAN_UINT16((byte *)(_exportTable + pubfunct)); + uint16 offset = READ_SCI11ENDIAN_UINT16(_exportTable + pubfunct); VERIFY(offset < _bufSize, "invalid export function pointer"); return offset; } -void Script::setSynonymsOffset(int offset) { - _synonyms = _buf + offset; -} +byte *Script::findBlock(int type) { + byte *buf = _buf; + bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); -byte *Script::getSynonyms() const { - return _synonyms; -} + if (oldScriptHeader) + buf += 2; -void Script::setSynonymsNr(int n) { - _numSynonyms = n; -} + do { + int seekerType = READ_LE_UINT16(buf); + + if (seekerType == 0) + break; + if (seekerType == type) + return buf; -int Script::getSynonymsNr() const { - return _numSynonyms; + int seekerSize = READ_LE_UINT16(buf + 2); + assert(seekerSize > 0); + buf += seekerSize; + } while (1); + + return NULL; } + // memory operations void Script::mcpyInOut(int dst, const void *src, size_t n) { @@ -511,7 +527,7 @@ SegmentRef SystemStrings::dereference(reg_t pointer) { //-------------------- script -------------------- -reg_t Script::findCanonicAddress(SegManager *segMan, reg_t addr) { +reg_t Script::findCanonicAddress(SegManager *segMan, reg_t addr) const { addr.offset = 0; return addr; } @@ -527,13 +543,13 @@ void Script::freeAtAddress(SegManager *segMan, reg_t addr) { segMan->deallocateScript(_nr); } -void Script::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) { +void Script::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const { (*note)(param, make_reg(segId, 0)); } -void Script::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { +void Script::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { if (addr.offset <= _bufSize && addr.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET && RAW_IS_OBJECT(_buf + addr.offset)) { - Object *obj = getObject(addr.offset); + const Object *obj = getObject(addr.offset); if (obj) { // Note all local variables, if we have a local variable environment if (_localsSegment) @@ -553,16 +569,14 @@ void Script::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback not //-------------------- clones -------------------- -void CloneTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { - Clone *clone; - +void CloneTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { // assert(addr.segment == _segId); if (!isValidEntry(addr.offset)) { error("Unexpected request for outgoing references from clone at %04x:%04x", PRINT_REG(addr)); } - clone = &(_table[addr.offset]); + const Clone *clone = &(_table[addr.offset]); // Emit all member variables (including references to the 'super' delegate) for (uint i = 0; i < clone->getVarCount(); i++) @@ -597,7 +611,7 @@ void CloneTable::freeAtAddress(SegManager *segMan, reg_t addr) { //-------------------- locals -------------------- -reg_t LocalVariables::findCanonicAddress(SegManager *segMan, reg_t addr) { +reg_t LocalVariables::findCanonicAddress(SegManager *segMan, reg_t addr) const { // Reference the owning script SegmentId owner_seg = segMan->getScriptSegment(script_id); @@ -606,7 +620,7 @@ reg_t LocalVariables::findCanonicAddress(SegManager *segMan, reg_t addr) { return make_reg(owner_seg, 0); } -void LocalVariables::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { +void LocalVariables::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { // assert(addr.segment == _segId); for (uint i = 0; i < _locals.size(); i++) @@ -615,12 +629,12 @@ void LocalVariables::listAllOutgoingReferences(reg_t addr, void *param, NoteCall //-------------------- stack -------------------- -reg_t DataStack::findCanonicAddress(SegManager *segMan, reg_t addr) { +reg_t DataStack::findCanonicAddress(SegManager *segMan, reg_t addr) const { addr.offset = 0; return addr; } -void DataStack::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { +void DataStack::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { fprintf(stderr, "Emitting %d stack entries\n", _capacity); for (int i = 0; i < _capacity; i++) (*note)(param, _entries[i]); @@ -633,13 +647,13 @@ void ListTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) { freeEntry(sub_addr.offset); } -void ListTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { +void ListTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { if (!isValidEntry(addr.offset)) { warning("Invalid list referenced for outgoing references: %04x:%04x", PRINT_REG(addr)); return; } - List *list = &(_table[addr.offset]); + const List *list = &(_table[addr.offset]); note(param, list->first); note(param, list->last); @@ -653,12 +667,12 @@ void NodeTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) { freeEntry(sub_addr.offset); } -void NodeTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { +void NodeTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { if (!isValidEntry(addr.offset)) { warning("Invalid node referenced for outgoing references: %04x:%04x", PRINT_REG(addr)); return; } - Node *node = &(_table[addr.offset]); + const Node *node = &(_table[addr.offset]); // We need all four here. Can't just stick with 'pred' OR 'succ' because node operations allow us // to walk around from any given node @@ -673,20 +687,43 @@ void NodeTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback //-------------------- object ---------------------------- -Object *Object::getClass(SegManager *segMan) { +void Object::init(byte *buf, reg_t obj_pos, bool initVariables) { + byte *data = buf + obj_pos.offset; + _baseObj = data; + _pos = obj_pos; + + if (getSciVersion() < SCI_VERSION_1_1) { + _variables.resize(READ_LE_UINT16(data + kOffsetSelectorCounter)); + _baseVars = (const uint16 *)(_baseObj + _variables.size() * 2); + _baseMethod = (const uint16 *)(data + READ_LE_UINT16(data + kOffsetFunctionArea)); + _methodCount = READ_LE_UINT16(_baseMethod - 1); + } else { + _variables.resize(READ_SCI11ENDIAN_UINT16(data + 2)); + _baseVars = (const uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 4)); + _baseMethod = (const uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 6)); + _methodCount = READ_SCI11ENDIAN_UINT16(_baseMethod); + } + + if (initVariables) { + for (uint i = 0; i < _variables.size(); i++) + _variables[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(data + (i * 2))); + } +} + +const Object *Object::getClass(SegManager *segMan) const { return isClass() ? this : segMan->getObject(getSuperClassSelector()); } -int Object::locateVarSelector(SegManager *segMan, Selector slc) { - byte *buf; +int Object::locateVarSelector(SegManager *segMan, Selector slc) const { + const byte *buf; uint varnum; if (getSciVersion() < SCI_VERSION_1_1) { varnum = getVarCount(); - int selector_name_offset = varnum * 2 + SCRIPT_SELECTOR_OFFSET; + int selector_name_offset = varnum * 2 + kOffsetSelectorSegment; buf = _baseObj + selector_name_offset; } else { - Object *obj = getClass(segMan); + const Object *obj = getClass(segMan); varnum = obj->getVariable(1).toUint16(); buf = (byte *)obj->_baseVars; } @@ -698,14 +735,72 @@ int Object::locateVarSelector(SegManager *segMan, Selector slc) { return -1; // Failed } +bool Object::relocate(SegmentId segment, int location, size_t scriptSize) { + return relocateBlock(_variables, getPos().offset, segment, location, scriptSize); +} + +int Object::propertyOffsetToId(SegManager *segMan, int propertyOffset) const { + int selectors = getVarCount(); + + if (propertyOffset < 0 || (propertyOffset >> 1) >= selectors) { + warning("Applied propertyOffsetToId to invalid property offset %x (property #%d not in [0..%d])", + propertyOffset, propertyOffset >> 1, selectors - 1); + return -1; + } + + if (getSciVersion() < SCI_VERSION_1_1) { + const byte *selectoroffset = ((const byte *)(_baseObj)) + kOffsetSelectorSegment + selectors * 2; + return READ_SCI11ENDIAN_UINT16(selectoroffset + propertyOffset); + } else { + const Object *obj = this; + if (!isClass()) + obj = segMan->getObject(getSuperClassSelector()); + + return READ_SCI11ENDIAN_UINT16((const byte *)obj->_baseVars + propertyOffset); + } +} + +void Object::initSpecies(SegManager *segMan, reg_t addr) { + uint16 speciesOffset = getSpeciesSelector().offset; + + if (speciesOffset == 0xffff) // -1 + setSpeciesSelector(NULL_REG); // no species + else + setSpeciesSelector(segMan->getClassAddress(speciesOffset, SCRIPT_GET_LOCK, addr)); +} + +void Object::initSuperClass(SegManager *segMan, reg_t addr) { + uint16 superClassOffset = getSuperClassSelector().offset; + + if (superClassOffset == 0xffff) // -1 + setSuperClassSelector(NULL_REG); // no superclass + else + setSuperClassSelector(segMan->getClassAddress(superClassOffset, SCRIPT_GET_LOCK, addr)); +} + +bool Object::initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass) { + const Object *baseObj = segMan->getObject(getSpeciesSelector()); + + if (baseObj) { + _variables.resize(baseObj->getVarCount()); + // Copy base from species class, as we need its selector IDs + _baseObj = baseObj->_baseObj; + if (doInitSuperClass) + initSuperClass(segMan, addr); + return true; + } + + return false; +} + //-------------------- dynamic memory -------------------- -reg_t DynMem::findCanonicAddress(SegManager *segMan, reg_t addr) { +reg_t DynMem::findCanonicAddress(SegManager *segMan, reg_t addr) const { addr.offset = 0; return addr; } -void DynMem::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) { +void DynMem::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const { (*note)(param, make_reg(segId, 0)); } @@ -724,13 +819,13 @@ void ArrayTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) { freeEntry(sub_addr.offset); } -void ArrayTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { +void ArrayTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { if (!isValidEntry(addr.offset)) { warning("Invalid array referenced for outgoing references: %04x:%04x", PRINT_REG(addr)); return; } - SciArray<reg_t> *array = &(_table[addr.offset]); + const SciArray<reg_t> *array = &(_table[addr.offset]); for (uint32 i = 0; i < array->getSize(); i++) { reg_t value = array->getValue(i); @@ -739,7 +834,7 @@ void ArrayTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback } } -Common::String SciString::toString() { +Common::String SciString::toString() const { if (_type != 3) error("SciString::toString(): Array is not a string"); @@ -750,7 +845,7 @@ Common::String SciString::toString() { return string; } -void SciString::fromString(Common::String string) { +void SciString::fromString(const Common::String &string) { if (_type != 3) error("SciString::fromString(): Array is not a string"); diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h index 1089ada475..f1b6dccaa2 100644 --- a/engines/sci/engine/segment.h +++ b/engines/sci/engine/segment.h @@ -112,7 +112,7 @@ public: * * @param sub_addr base address whose canonic address is to be found */ - virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) { return sub_addr; } + virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const { return sub_addr; } /** * Deallocates all memory associated with the specified address. @@ -125,7 +125,7 @@ public: * @param note Invoked for each address on which free_at_address() makes sense * @param param parameter passed to 'note' */ - virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) {} + virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const {} /** * Iterates over all references reachable from the specified object. @@ -134,7 +134,7 @@ public: * @param note Invoked for each outgoing reference within the object * Note: This function may also choose to report numbers (segment 0) as adresses */ - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) {} + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const {} }; @@ -194,8 +194,8 @@ public: virtual bool isValidOffset(uint16 offset) const; virtual SegmentRef dereference(reg_t pointer); - virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); + virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const; + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -205,6 +205,22 @@ enum { OBJECT_FLAG_FREED = (1 << 0) }; +enum infoSelectorFlags { + kInfoFlagClone = 0x0001, + kInfoFlagClass = 0x8000 +}; + +enum ObjectOffsets { + kOffsetLocalVariables = -6, + kOffsetFunctionArea = -4, + kOffsetSelectorCounter = -2, + kOffsetSelectorSegment = 0, + kOffsetInfoSelectorSci0 = 4, + kOffsetNamePointerSci0 = 6, + kOffsetInfoSelectorSci11 = 14, + kOffsetNamePointerSci11 = 16 +}; + class Object { public: Object() { @@ -214,31 +230,34 @@ public: ~Object() { } - reg_t getSpeciesSelector() { return _variables[_offset]; } + reg_t getSpeciesSelector() const { return _variables[_offset]; } void setSpeciesSelector(reg_t value) { _variables[_offset] = value; } - reg_t getSuperClassSelector() { return _variables[_offset + 1]; } + reg_t getSuperClassSelector() const { return _variables[_offset + 1]; } void setSuperClassSelector(reg_t value) { _variables[_offset + 1] = value; } - reg_t getInfoSelector() { return _variables[_offset + 2]; } - void setInfoSelector(reg_t value) { _variables[_offset + 2] = value; } + reg_t getInfoSelector() const { return _variables[_offset + 2]; } + void setInfoSelector(reg_t value) { _variables[_offset + 2] = value; } - reg_t getNameSelector() { return _variables[_offset + 3]; } - void setNameSelector(reg_t value) { _variables[_offset + 3] = value; } + reg_t getNameSelector() const { return _variables[_offset + 3]; } + void setNameSelector(reg_t value) { _variables[_offset + 3] = value; } - reg_t getClassScriptSelector() { return _variables[4]; } + reg_t getPropDictSelector() const { return _variables[2]; } + void setPropDictSelector(reg_t value) { _variables[2] = value; } + + reg_t getClassScriptSelector() const { return _variables[4]; } void setClassScriptSelector(reg_t value) { _variables[4] = value; } - Selector getVarSelector(uint16 i) { return READ_SCI11ENDIAN_UINT16(_baseVars + i); } + Selector getVarSelector(uint16 i) const { return READ_SCI11ENDIAN_UINT16(_baseVars + i); } - reg_t getFunction(uint16 i) { + reg_t getFunction(uint16 i) const { uint16 offset = (getSciVersion() < SCI_VERSION_1_1) ? _methodCount + 1 + i : i * 2 + 2; - return make_reg(_pos.segment, READ_SCI11ENDIAN_UINT16((byte *) (_baseMethod + offset))); + return make_reg(_pos.segment, READ_SCI11ENDIAN_UINT16(_baseMethod + offset)); } - Selector getFuncSelector(uint16 i) { + Selector getFuncSelector(uint16 i) const { uint16 offset = (getSciVersion() < SCI_VERSION_1_1) ? i : i * 2 + 1; - return READ_SCI11ENDIAN_UINT16((byte *) (_baseMethod + offset)); + return READ_SCI11ENDIAN_UINT16(_baseMethod + offset); } /** @@ -247,7 +266,7 @@ public: * superclasses, i.e. failure may be returned even if one of the * superclasses defines the funcselector */ - int funcSelectorPosition(Selector sel) { + int funcSelectorPosition(Selector sel) const { for (uint i = 0; i < _methodCount; i++) if (getFuncSelector(i) == sel) return i; @@ -256,61 +275,56 @@ public: } /** - * Determines if the object explicitly defines slc as a varselector - * Returns -1 if not found + * Determines if the object explicitly defines slc as a varselector. + * Returns -1 if not found. */ - int locateVarSelector(SegManager *segMan, Selector slc); - - bool isClass() { return (getInfoSelector().offset & SCRIPT_INFO_CLASS); } - Object *getClass(SegManager *segMan); + int locateVarSelector(SegManager *segMan, Selector slc) const; - void markAsFreed() { _flags |= OBJECT_FLAG_FREED; } - bool isFreed() { return _flags & OBJECT_FLAG_FREED; } + bool isClass() const { return (getInfoSelector().offset & kInfoFlagClass); } + const Object *getClass(SegManager *segMan) const; - void setVarCount(uint size) { _variables.resize(size); } - uint getVarCount() { return _variables.size(); } + void markAsClone() { setInfoSelector(make_reg(0, kInfoFlagClone)); } + bool isClone() const { return (getInfoSelector().offset & kInfoFlagClone); } - void init(byte *buf, reg_t obj_pos) { - byte *data = (byte *)(buf + obj_pos.offset); - _baseObj = data; - _pos = obj_pos; + void markAsFreed() { _flags |= OBJECT_FLAG_FREED; } + bool isFreed() const { return _flags & OBJECT_FLAG_FREED; } - if (getSciVersion() < SCI_VERSION_1_1) { - _variables.resize(READ_LE_UINT16(data + SCRIPT_SELECTORCTR_OFFSET)); - _baseVars = (uint16 *)(_baseObj + _variables.size() * 2); - _baseMethod = (uint16 *)(data + READ_LE_UINT16(data + SCRIPT_FUNCTAREAPTR_OFFSET)); - _methodCount = READ_LE_UINT16(_baseMethod - 1); - } else { - _variables.resize(READ_SCI11ENDIAN_UINT16(data + 2)); - _baseVars = (uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 4)); - _baseMethod = (uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 6)); - _methodCount = READ_SCI11ENDIAN_UINT16(_baseMethod); - } + uint getVarCount() const { return _variables.size(); } - for (uint i = 0; i < _variables.size(); i++) - _variables[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(data + (i * 2))); - } + void init(byte *buf, reg_t obj_pos, bool initVariables = true); - reg_t getVariable(uint var) { return _variables[var]; } + reg_t getVariable(uint var) const { return _variables[var]; } + reg_t &getVariableRef(uint var) { return _variables[var]; } - uint16 getMethodCount() { return _methodCount; } - reg_t getPos() { return _pos; } + uint16 getMethodCount() const { return _methodCount; } + reg_t getPos() const { return _pos; } void saveLoadWithSerializer(Common::Serializer &ser); - void cloneFromObject(Object *obj) { + void cloneFromObject(const Object *obj) { _baseObj = obj ? obj->_baseObj : NULL; _baseMethod = obj ? obj->_baseMethod : NULL; _baseVars = obj ? obj->_baseVars : NULL; } + bool relocate(SegmentId segment, int location, size_t scriptSize); + + int propertyOffsetToId(SegManager *segMan, int propertyOffset) const; + + void initSpecies(SegManager *segMan, reg_t addr); + void initSuperClass(SegManager *segMan, reg_t addr); + bool initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass = true); + // TODO: make private - Common::Array<reg_t> _variables; - byte *_baseObj; /**< base + object offset within base */ - uint16 *_baseVars; /**< Pointer to the varselector area for this object */ - uint16 *_baseMethod; /**< Pointer to the method selector area for this object */ + // Only SegManager::reconstructScripts() is left needing direct access to these +public: + const byte *_baseObj; /**< base + object offset within base */ private: + const uint16 *_baseVars; /**< Pointer to the varselector area for this object */ + const uint16 *_baseMethod; /**< Pointer to the method selector area for this object */ + + Common::Array<reg_t> _variables; uint16 _methodCount; int _flags; uint16 _offset; @@ -328,21 +342,28 @@ class Script : public SegmentObj { public: int _nr; /**< Script number */ byte *_buf; /**< Static data buffer, or NULL if not used */ - size_t _bufSize; - size_t _scriptSize; - size_t _heapSize; - byte *_heapStart; /**< Start of heap if SCI1.1, NULL otherwise */ - uint16 *_exportTable; /**< Abs. offset of the export table or 0 if not present */ - int _numExports; /**< Number of entries in the exports table */ - - byte *_synonyms; /**< Synonyms block or 0 if not present*/ - int _numSynonyms; /**< Number of entries in the synonyms block */ + uint32 getScriptSize() { return _scriptSize; } + uint32 getHeapSize() { return _heapSize; } + uint32 getBufSize() { return _bufSize; } protected: int _lockers; /**< Number of classes and objects that require this script */ +private: + size_t _scriptSize; + size_t _heapSize; + size_t _bufSize; + + const uint16 *_exportTable; /**< Abs. offset of the export table or 0 if not present */ + uint16 _numExports; /**< Number of entries in the exports table */ + + const byte *_synonyms; /**< Synonyms block or 0 if not present*/ + uint16 _numSynonyms; /**< Number of entries in the synonyms block */ + + Common::Array<CodeBlock> _codeBlocks; + public: /** * Table for objects, contains property variables. @@ -354,8 +375,6 @@ public: SegmentId _localsSegment; /**< The local variable segment */ LocalVariables *_localsBlock; - Common::Array<CodeBlock> _codeBlocks; - bool _relocated; bool _markedAsDeleted; public: @@ -363,19 +382,21 @@ public: ~Script(); void freeScript(); - bool init(int script_nr, ResourceManager *resMan); + void init(int script_nr, ResourceManager *resMan); + void load(ResourceManager *resMan); virtual bool isValidOffset(uint16 offset) const; virtual SegmentRef dereference(reg_t pointer); - virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr); + virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const; virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note); - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); + virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const; + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; virtual void saveLoadWithSerializer(Common::Serializer &ser); Object *allocateObject(uint16 offset); Object *getObject(uint16 offset); + const Object *getObject(uint16 offset) const; /** * Informs the segment manager that a code block must be relocated @@ -392,7 +413,7 @@ public: * @returns A newly created Object describing the object, * stored within the relevant script */ - Object *scriptObjInit(reg_t obj_pos); + Object *scriptObjInit(reg_t obj_pos, bool fullObjectInit = true); /** * Removes a script object @@ -407,14 +428,10 @@ public: * @param obj_pos Location (segment, offset) of the block * @return Location of the relocation block */ - void scriptRelocate(reg_t block); - - void heapRelocate(reg_t block); + void relocate(reg_t block); private: - int relocateLocal(SegmentId segment, int location); - int relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location); - int relocateObject(Object &obj, SegmentId segment, int location); + bool relocateLocal(SegmentId segment, int location); public: // script lock operations @@ -435,22 +452,28 @@ public: void setLockers(int lockers); /** + * Retrieves a pointer to the exports of this script + * @return pointer to the exports. + */ + const uint16 *getExportTable() const { return _exportTable; } + + /** + * Retrieves the number of exports of script. + * @return the number of exports of this script + */ + uint16 getExportsNr() const { return _numExports; } + + /** * Retrieves a pointer to the synonyms associated with this script * @return pointer to the synonyms, in non-parsed format. */ - byte *getSynonyms() const; + const byte *getSynonyms() const { return _synonyms; } /** * Retrieves the number of synonyms associated with this script. * @return the number of synonyms associated with this script */ - int getSynonymsNr() const; - - /** - * Sets the script-relative offset of the exports table. - * @param offset script-relative exports table offset - */ - void setExportTableOffset(int offset); + uint16 getSynonymsNr() const { return _numSynonyms; } /** * Validate whether the specified public function is exported by @@ -462,19 +485,6 @@ public: uint16 validateExportFunc(int pubfunct); /** - * Sets the script-relative offset of the synonyms associated with this script. - * @param offset script-relative offset of the synonyms block - */ - void setSynonymsOffset(int offset); - - /** - * Sets the number of synonyms associated with this script, - * @param nr number of synonyms, as to be stored within the script - */ - void setSynonymsNr(int nr); - - - /** * Marks the script as deleted. * This will not actually delete the script. If references remain present on the * heap or the stack, the script will stay in memory in a quasi-deleted state until @@ -508,8 +518,10 @@ public: */ int16 getHeap(uint16 offset) const; -private: - void setScriptSize(int script_nr, ResourceManager *resMan); + /** + * Finds the pointer where a block of a specific type starts from + */ + byte *findBlock(int type); }; /** Data stack */ @@ -529,8 +541,8 @@ public: virtual bool isValidOffset(uint16 offset) const; virtual SegmentRef dereference(reg_t pointer); - virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); + virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const; + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -618,7 +630,7 @@ public: entries_used--; } - virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) { + virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const { for (uint i = 0; i < _table.size(); i++) if (isValidEntry(i)) (*note)(param, make_reg(segId, i)); @@ -631,7 +643,7 @@ struct CloneTable : public Table<Clone> { CloneTable() : Table<Clone>(SEG_TYPE_CLONES) {} virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -642,7 +654,7 @@ struct NodeTable : public Table<Node> { NodeTable() : Table<Node>(SEG_TYPE_NODES) {} virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -653,7 +665,7 @@ struct ListTable : public Table<List> { ListTable() : Table<List>(SEG_TYPE_LISTS) {} virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -688,8 +700,8 @@ public: virtual bool isValidOffset(uint16 offset) const; virtual SegmentRef dereference(reg_t pointer); - virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note); + virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const; + virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const; virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -782,7 +794,7 @@ public: _size = _actualSize = size; } - T getValue(uint16 index) { + T getValue(uint16 index) const { if (index >= _size) error("SciArray::getValue(): %d is out of bounds (%d)", index, _size); @@ -796,8 +808,8 @@ public: _data[index] = value; } - byte getType() { return _type; } - uint32 getSize() { return _size; } + byte getType() const { return _type; } + uint32 getSize() const { return _size; } T *getRawData() { return _data; } protected: @@ -814,15 +826,15 @@ public: // We overload destroy to ensure the string type is 3 after destroying void destroy() { SciArray<char>::destroy(); _type = 3; } - Common::String toString(); - void fromString(Common::String string); + Common::String toString() const; + void fromString(const Common::String &string); }; struct ArrayTable : public Table<SciArray<reg_t> > { ArrayTable() : Table<SciArray<reg_t> >(SEG_TYPE_ARRAY) {} virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; void saveLoadWithSerializer(Common::Serializer &ser); SegmentRef dereference(reg_t pointer); diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp index aba134818b..04a1b8fbba 100644 --- a/engines/sci/engine/selector.cpp +++ b/engines/sci/engine/selector.cpp @@ -153,7 +153,6 @@ void Kernel::mapSelectors() { FIND_SELECTOR(subtitleLang); FIND_SELECTOR(parseLang); FIND_SELECTOR(overlay); - FIND_SELECTOR(setCursor); FIND_SELECTOR(topString); FIND_SELECTOR(scaleSignal); FIND_SELECTOR(scaleX); @@ -176,32 +175,32 @@ void Kernel::mapSelectors() { #endif } -reg_t read_selector(SegManager *segMan, reg_t object, Selector selector_id) { +reg_t readSelector(SegManager *segMan, reg_t object, Selector selectorId) { ObjVarRef address; - if (lookup_selector(segMan, object, selector_id, &address, NULL) != kSelectorVariable) + if (lookupSelector(segMan, object, selectorId, &address, NULL) != kSelectorVariable) return NULL_REG; else return *address.getPointer(segMan); } -void write_selector(SegManager *segMan, reg_t object, Selector selector_id, reg_t value) { +void writeSelector(SegManager *segMan, reg_t object, Selector selectorId, reg_t value) { ObjVarRef address; - if ((selector_id < 0) || (selector_id > (int)g_sci->getKernel()->getSelectorNamesSize())) { + if ((selectorId < 0) || (selectorId > (int)g_sci->getKernel()->getSelectorNamesSize())) { warning("Attempt to write to invalid selector %d of" - " object at %04x:%04x.", selector_id, PRINT_REG(object)); + " object at %04x:%04x.", selectorId, PRINT_REG(object)); return; } - if (lookup_selector(segMan, object, selector_id, &address, NULL) != kSelectorVariable) + if (lookupSelector(segMan, object, selectorId, &address, NULL) != kSelectorVariable) warning("Selector '%s' of object at %04x:%04x could not be" - " written to", g_sci->getKernel()->getSelectorName(selector_id).c_str(), PRINT_REG(object)); + " written to", g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object)); else *address.getPointer(segMan) = value; } -int invoke_selector_argv(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid, +int invokeSelectorArgv(EngineState *s, reg_t object, int selectorId, SelectorInvocation noinvalid, int k_argc, StackPtr k_argp, int argc, const reg_t *argv) { int i; int framesize = 2 + 1 * argc; @@ -209,21 +208,21 @@ int invoke_selector_argv(EngineState *s, reg_t object, int selector_id, Selector int slc_type; StackPtr stackframe = k_argp + k_argc; - stackframe[0] = make_reg(0, selector_id); // The selector we want to call + stackframe[0] = make_reg(0, selectorId); // The selector we want to call stackframe[1] = make_reg(0, argc); // Argument count - slc_type = lookup_selector(s->_segMan, object, selector_id, NULL, &address); + slc_type = lookupSelector(s->_segMan, object, selectorId, NULL, &address); if (slc_type == kSelectorNone) { warning("Selector '%s' of object at %04x:%04x could not be invoked", - g_sci->getKernel()->getSelectorName(selector_id).c_str(), PRINT_REG(object)); + g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object)); if (noinvalid == kStopOnInvalidSelector) error("[Kernel] Not recoverable: VM was halted"); return 1; } if (slc_type == kSelectorVariable) { warning("Attempting to invoke variable selector %s of object %04x:%04x", - g_sci->getKernel()->getSelectorName(selector_id).c_str(), PRINT_REG(object)); + g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object)); return 0; } @@ -243,7 +242,7 @@ int invoke_selector_argv(EngineState *s, reg_t object, int selector_id, Selector return 0; } -int invoke_selector(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid, +int invokeSelector(EngineState *s, reg_t object, int selectorId, SelectorInvocation noinvalid, int k_argc, StackPtr k_argp, int argc, ...) { va_list argp; reg_t *args = new reg_t[argc]; @@ -253,28 +252,28 @@ int invoke_selector(EngineState *s, reg_t object, int selector_id, SelectorInvoc args[i] = va_arg(argp, reg_t); va_end(argp); - int retval = invoke_selector_argv(s, object, selector_id, noinvalid, k_argc, k_argp, argc, args); + int retval = invokeSelectorArgv(s, object, selectorId, noinvalid, k_argc, k_argp, argc, args); delete[] args; return retval; } -SelectorType lookup_selector(SegManager *segMan, reg_t obj_location, Selector selector_id, ObjVarRef *varp, reg_t *fptr) { - Object *obj = segMan->getObject(obj_location); +SelectorType lookupSelector(SegManager *segMan, reg_t obj_location, Selector selectorId, ObjVarRef *varp, reg_t *fptr) { + const Object *obj = segMan->getObject(obj_location); int index; bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); // Early SCI versions used the LSB in the selector ID as a read/write // toggle, meaning that we must remove it for selector lookup. if (oldScriptHeader) - selector_id &= ~1; + selectorId &= ~1; if (!obj) { - error("lookup_selector(): Attempt to send to non-object or invalid script. Address was %04x:%04x", + error("lookupSelector(): Attempt to send to non-object or invalid script. Address was %04x:%04x", PRINT_REG(obj_location)); } - index = obj->locateVarSelector(segMan, selector_id); + index = obj->locateVarSelector(segMan, selectorId); if (index >= 0) { // Found it as a variable @@ -286,7 +285,7 @@ SelectorType lookup_selector(SegManager *segMan, reg_t obj_location, Selector se } else { // Check if it's a method, with recursive lookup in superclasses while (obj) { - index = obj->funcSelectorPosition(selector_id); + index = obj->funcSelectorPosition(selectorId); if (index >= 0) { if (fptr) *fptr = obj->getFunction(index); @@ -301,7 +300,7 @@ SelectorType lookup_selector(SegManager *segMan, reg_t obj_location, Selector se } -// return _lookup_selector_function(segMan, obj, selector_id, fptr); +// return _lookupSelector_function(segMan, obj, selectorId, fptr); } } // End of namespace Sci diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h index 70eeb34d93..f50b9ab1b3 100644 --- a/engines/sci/engine/selector.h +++ b/engines/sci/engine/selector.h @@ -34,20 +34,15 @@ namespace Sci { - -/******************** Selector functionality ********************/ - enum SelectorInvocation { kStopOnInvalidSelector = 0, kContinueOnInvalidSelector = 1 }; - /** * Map a selector name to a selector id. Shortcut for accessing the selector cache. */ #define SELECTOR(_slc_) (g_sci->getKernel()->_selectorCache._slc_) -//#define SELECTOR(_slc_) _slc_ /** * Retrieves a selector from an object. @@ -58,8 +53,8 @@ enum SelectorInvocation { * This macro halts on error. 'selector' must be a selector name registered in vm.h's * SelectorCache and mapped in script.cpp. */ -#define GET_SEL32(segMan, _obj_, _slc_) read_selector(segMan, _obj_, _slc_) -#define GET_SEL32V(segMan, _obj_, _slc_) (GET_SEL32(segMan, _obj_, _slc_).offset) +reg_t readSelector(SegManager *segMan, reg_t object, Selector selectorId); +#define readSelectorValue(segMan, _obj_, _slc_) (readSelector(segMan, _obj_, _slc_).offset) /** * Writes a selector value to an object. @@ -70,27 +65,25 @@ enum SelectorInvocation { * This macro halts on error. 'selector' must be a selector name registered in vm.h's * SelectorCache and mapped in script.cpp. */ -#define PUT_SEL32(segMan, _obj_, _slc_, _val_) write_selector(segMan, _obj_, _slc_, _val_) -#define PUT_SEL32V(segMan, _obj_, _slc_, _val_) PUT_SEL32(segMan, _obj_, _slc_, make_reg(0, _val_)) +void writeSelector(SegManager *segMan, reg_t object, Selector selectorId, reg_t value); +#define writeSelectorValue(segMan, _obj_, _slc_, _val_) writeSelector(segMan, _obj_, _slc_, make_reg(0, _val_)) +/** + * Invokes a selector from an object. + */ +int invokeSelector(EngineState *s, reg_t object, int selectorId, SelectorInvocation noinvalid, + int k_argc, StackPtr k_argp, int argc, ...); +int invokeSelectorArgv(EngineState *s, reg_t object, int selectorId, SelectorInvocation noinvalid, + int k_argc, StackPtr k_argp, int argc, const reg_t *argv); /** - * Kludge for use with invoke_selector(). Used for compatibility with compilers + * Kludge for use with invokeSelector(). Used for compatibility with compilers * that cannot handle vararg macros. */ #define INV_SEL(s, _object_, _selector_, _noinvalid_) \ s, _object_, g_sci->getKernel()->_selectorCache._selector_, _noinvalid_, argc, argv -reg_t read_selector(SegManager *segMan, reg_t object, Selector selector_id); -void write_selector(SegManager *segMan, reg_t object, Selector selector_id, reg_t value); -int invoke_selector(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid, - int k_argc, StackPtr k_argp, int argc, ...); -int invoke_selector_argv(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid, - int k_argc, StackPtr k_argp, int argc, const reg_t *argv); - - - } // End of namespace Sci #endif // SCI_ENGINE_KERNEL_H diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp index dfc4c39464..b4a04f8826 100644 --- a/engines/sci/engine/state.cpp +++ b/engines/sci/engine/state.cpp @@ -69,51 +69,56 @@ static const uint16 s_halfWidthSJISMap[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -EngineState::EngineState(Vocabulary *voc, SegManager *segMan) -: _voc(voc), _segMan(segMan), _dirseeker() { +EngineState::EngineState(SegManager *segMan) +: _segMan(segMan), _dirseeker() { + reset(false); +} + +EngineState::~EngineState() { + delete _msgState; +} + +void EngineState::reset(bool isRestoring) { #ifdef USE_OLD_MUSIC_FUNCTIONS sfx_init_flags = 0; #endif - restarting_flags = 0; - - last_wait_time = 0; + if (!isRestoring) { + script_000 = 0; + _gameObj = NULL_REG; - _fileHandles.resize(5); + _memorySegmentSize = 0; + _soundCmd = 0; - execution_stack_base = 0; - _executionStackPosChanged = false; + restarting_flags = 0; - r_acc = NULL_REG; - restAdjust = 0; - r_prev = NULL_REG; + execution_stack_base = 0; + _executionStackPosChanged = false; - stack_base = 0; - stack_top = 0; + _fileHandles.resize(5); - script_000 = 0; + r_acc = NULL_REG; + restAdjust = 0; + r_prev = NULL_REG; - sys_strings_segment = 0; - sys_strings = 0; + stack_base = 0; + stack_top = 0; + } - _gameObj = NULL_REG; + last_wait_time = 0; gc_countdown = 0; - successor = 0; - _throttleCounter = 0; _throttleLastTime = 0; _throttleTrigger = false; - _memorySegmentSize = 0; + script_abort_flag = 0; + script_step_counter = 0; + script_gc_interval = GC_INTERVAL; - _soundCmd = 0; -} - -EngineState::~EngineState() { - delete _msgState; + restoring = false; } void EngineState::wait(int16 ticks) { @@ -227,7 +232,7 @@ kLanguage SciEngine::getSciLanguage() { lang = K_LANG_ENGLISH; if (_kernel->_selectorCache.printLang != -1) { - lang = (kLanguage)GET_SEL32V(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang)); + lang = (kLanguage)readSelectorValue(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang)); if ((getSciVersion() >= SCI_VERSION_1_1) || (lang == K_LANG_NONE)) { // If language is set to none, we use the language from the game detector. @@ -262,7 +267,7 @@ kLanguage SciEngine::getSciLanguage() { } // Store language in printLang selector - PUT_SEL32V(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang), lang); + writeSelectorValue(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang), lang); } } @@ -274,7 +279,7 @@ Common::String SciEngine::strSplit(const char *str, const char *sep) { kLanguage subLang = K_LANG_NONE; if (_kernel->_selectorCache.subtitleLang != -1) { - subLang = (kLanguage)GET_SEL32V(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(subtitleLang)); + subLang = (kLanguage)readSelectorValue(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(subtitleLang)); } kLanguage secondLang; diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h index ad9de9e13e..bcdf66d6ef 100644 --- a/engines/sci/engine/state.h +++ b/engines/sci/engine/state.h @@ -95,14 +95,13 @@ public: struct EngineState : public Common::Serializable { public: - EngineState(Vocabulary *voc, SegManager *segMan); + EngineState(SegManager *segMan); virtual ~EngineState(); virtual void saveLoadWithSerializer(Common::Serializer &ser); public: SegManager *_segMan; /**< The segment manager */ - Vocabulary *_voc; /* Non-VM information */ @@ -144,13 +143,32 @@ public: reg_t r_acc; /**< Accumulator */ int16 restAdjust; /**< &rest register (only used for save games) */ + int16 restAdjustCur; /**< current &rest register (only used for save games) */ reg_t r_prev; /**< previous comparison result */ StackPtr stack_base; /**< Pointer to the least stack element */ StackPtr stack_top; /**< First invalid stack element */ + // Script state + ExecStack *xs; + reg_t *variables[4]; ///< global, local, temp, param, as immediate pointers + reg_t *variables_base[4]; ///< Used for referencing VM ops + SegmentId variables_seg[4]; ///< Same as above, contains segment IDs + int variables_max[4]; ///< Max. values for all variables + Script *script_000; /**< script 000, e.g. for globals */ + int loadFromLauncher; + + /** + * Set this to 1 to abort script execution immediately. Aborting will + * leave the debug exec stack intact. + * Set it to 2 to force a replay afterwards. + */ + int script_abort_flag; // Set to 1 to abort execution. Set to 2 to force a replay afterwards + int script_step_counter; // Counts the number of steps executed + int script_gc_interval; // Number of steps in between gcs + uint16 currentRoomNumber() const; void setRoomNumber(uint16 roomNumber); @@ -160,10 +178,6 @@ public: */ void shrinkStackToBase(); - /* System strings */ - SegmentId sys_strings_segment; - SystemStrings *sys_strings; - reg_t _gameObj; /**< Pointer to the game object */ int gc_countdown; /**< Number of kernel calls until next gc */ @@ -179,7 +193,12 @@ public: uint _memorySegmentSize; byte _memorySegment[kMemorySegmentMax]; - EngineState *successor; /**< Successor of this state: Used for restoring */ + /** + * Resets the engine state. + */ + void reset(bool isRestoring); + + bool restoring; /**< A flag to indicate if a game is being restored */ }; } // End of namespace Sci diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index 7b6763f8f2..1dcdf450ba 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -46,13 +46,6 @@ const reg_t SIGNAL_REG = {0, SIGNAL_OFFSET}; //#define VM_DEBUG_SEND -ScriptState scriptState; // FIXME: Avoid non-const global vars -int g_loadFromLauncher; // FIXME: Avoid non-const global vars - -int script_abort_flag = 0; // Set to 1 to abort execution. Set to 2 to force a replay afterwards // FIXME: Avoid non-const global vars -int script_step_counter = 0; // Counts the number of steps executed // FIXME: Avoid non-const global vars -int script_gc_interval = GC_INTERVAL; // Number of steps in between gcs // FIXME: Avoid non-const global vars - #define SCI_XS_CALLEE_LOCALS ((SegmentId)-1) /** @@ -120,7 +113,7 @@ static reg_t &validate_property(Object *obj, int index) { return dummyReg; } - return obj->_variables[index]; + return obj->getVariableRef(index); } static StackPtr validate_stack_addr(EngineState *s, StackPtr sp) { @@ -212,7 +205,7 @@ static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, i if (!stopGroopPos.isNull()) { // does the game have a stopGroop object? // Find the "client" member variable of the stopGroop object, and update it ObjVarRef varp; - if (lookup_selector(segMan, stopGroopPos, kernel->_selectorCache.client, &varp, NULL) == kSelectorVariable) { + if (lookupSelector(segMan, stopGroopPos, kernel->_selectorCache.client, &varp, NULL) == kSelectorVariable) { reg_t *clientVar = varp.getPointer(segMan); *clientVar = value; } @@ -236,8 +229,8 @@ static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, i #endif -#define READ_VAR(type, index, def) validate_read_var(scriptState.variables[type], s->stack_base, type, scriptState.variables_max[type], index, __LINE__, def) -#define WRITE_VAR(type, index, value) validate_write_var(scriptState.variables[type], s->stack_base, type, scriptState.variables_max[type], index, __LINE__, value, s->_segMan, g_sci->getKernel()) +#define READ_VAR(type, index, def) validate_read_var(s->variables[type], s->stack_base, type, s->variables_max[type], index, __LINE__, def) +#define WRITE_VAR(type, index, value) validate_write_var(s->variables[type], s->stack_base, type, s->variables_max[type], index, __LINE__, value, s->_segMan, g_sci->getKernel()) #define WRITE_VAR16(type, index, value) WRITE_VAR(type, index, make_reg(0, value)); #define ACC_ARITHMETIC_L(op) make_reg(0, (op validate_arithmetic(s->r_acc))) @@ -251,8 +244,8 @@ static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, i #define PUSH(v) PUSH32(make_reg(0, v)) #define POP() (validate_arithmetic(POP32())) // 32 bit: -#define PUSH32(a) (*(validate_stack_addr(s, (scriptState.xs->sp)++)) = (a)) -#define POP32() (*(validate_stack_addr(s, --(scriptState.xs->sp)))) +#define PUSH32(a) (*(validate_stack_addr(s, (s->xs->sp)++)) = (a)) +#define POP32() (*(validate_stack_addr(s, --(s->xs->sp)))) ExecStack *execute_method(EngineState *s, uint16 script, uint16 pubfunct, StackPtr sp, reg_t calling_obj, uint16 argc, StackPtr argp) { int seg = s->_segMan->getScriptSegment(script); @@ -341,7 +334,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt int selector; int argc; int origin = s->_executionStack.size()-1; // Origin: Used for debugging - int print_send_action = 0; + bool printSendActions = false; // We return a pointer to the new active ExecStack // The selector calls we catch are stored below: @@ -370,7 +363,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt if (bp->type == BREAK_SELECTOR && !strncmp(bp->name.c_str(), method_name, cmplen)) { Console *con = g_sci->getSciDebugger(); con->DebugPrintf("Break on %s (in [%04x:%04x])\n", method_name, PRINT_REG(send_obj)); - print_send_action = 1; + printSendActions = true; g_debugState.debugging = true; g_debugState.breakpointWasHit = true; break; @@ -383,7 +376,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt #endif // VM_DEBUG_SEND ObjVarRef varp; - switch (lookup_selector(s->_segMan, send_obj, selector, &varp, &funcp)) { + switch (lookupSelector(s->_segMan, send_obj, selector, &varp, &funcp)) { case kSelectorNone: error("Send to invalid selector 0x%x of object at %04x:%04x", 0xffff & selector, PRINT_REG(send_obj)); break; @@ -398,22 +391,34 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt #endif // VM_DEBUG_SEND // argc == 0: read selector - // argc == 1: write selector - // argc > 1: write selector? - if (print_send_action && argc == 0) { // read selector - printf("[read selector]\n"); - print_send_action = 0; + // argc != 0: write selector + if (printSendActions && !argc) { // read selector + debug("[read selector]\n"); + printSendActions = false; } - if (print_send_action && argc > 0) { + if (printSendActions && argc) { reg_t oldReg = *varp.getPointer(s->_segMan); reg_t newReg = argp[1]; - printf("[write to selector: change %04x:%04x to %04x:%04x]\n", PRINT_REG(oldReg), PRINT_REG(newReg)); - print_send_action = 0; + debug("[write to selector: change %04x:%04x to %04x:%04x]\n", PRINT_REG(oldReg), PRINT_REG(newReg)); + printSendActions = false; } - if (argc > 1) - warning("send_selector(): more than 1 parameter (%d) while modifying a variable selector", argc); + if (argc > 1) { + // argc can indeed be bigger than 1 in some cases, and it seems correct + // (i.e. we should skip that many bytes later on)... question is, why + // does this occur? Could such calls be used to point to data after X + // bytes in the heap? What are the skipped bytes in this case? + // In SQ4CD, this occurs with the returnVal selector of object + // Sq4GlobalNarrator when the game starts, and right after the narrator + // is heard (e.g. after he talks when examining something) + reg_t oldReg = *varp.getPointer(s->_segMan); + reg_t newReg = argp[1]; + warning("send_selector(): argc = %d while modifying variable selector " + "%x (%s) of object %04x:%04x (%s) from %04x:%04x to %04x:%04x", + argc, selector, g_sci->getKernel()->getSelectorName(selector).c_str(), PRINT_REG(send_obj), + s->_segMan->getObjectName(send_obj), PRINT_REG(oldReg), PRINT_REG(newReg)); + } { CallsStruct call; @@ -438,9 +443,9 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt } printf(") at %04x:%04x\n", PRINT_REG(funcp)); #endif // VM_DEBUG_SEND - if (print_send_action) { - printf("[invoke selector]\n"); - print_send_action = 0; + if (printSendActions) { + debug("[invoke selector]\n"); + printSendActions = false; } { @@ -456,7 +461,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt } break; - } // switch (lookup_selector()) + } // switch (lookupSelector()) framesize -= (2 + argc); argp += argc + 1; @@ -477,9 +482,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt _exec_varselectors(s); - if (s->_executionStack.empty()) - return NULL; - return &(s->_executionStack.back()); + return s->_executionStack.empty() ? NULL : &(s->_executionStack.back()); } static ExecStack *add_exec_stack_varselector(Common::List<ExecStack> &execStack, reg_t objp, int argc, StackPtr argp, Selector selector, const ObjVarRef& address, int origin) { @@ -565,11 +568,11 @@ static void callKernelFunc(EngineState *s, int kernelFuncNum, int argc) { const KernelFuncWithSignature &kernelFunc = g_sci->getKernel()->_kernelFuncs[kernelFuncNum]; if (kernelFunc.signature - && !g_sci->getKernel()->signatureMatch(kernelFunc.signature, argc, scriptState.xs->sp + 1)) { + && !g_sci->getKernel()->signatureMatch(kernelFunc.signature, argc, s->xs->sp + 1)) { error("[VM] Invalid arguments to kernel call %x", kernelFuncNum); } - reg_t *argv = scriptState.xs->sp + 1; + reg_t *argv = s->xs->sp + 1; if (!kernelFunc.isDummy) { // Add stack frame to indicate we're executing a callk. @@ -583,19 +586,26 @@ static void callKernelFunc(EngineState *s, int kernelFuncNum, int argc) { //warning("callk %s", kernelFunc.orig_name.c_str()); - // TODO: SCI2/SCI2.1+ equivalent, once saving/loading works in SCI2/SCI2.1+ - if (g_loadFromLauncher >= 0 && kernelFuncNum == 0x8) { - // A game is being loaded from the launcher, and kDisplay is called, all initialization has taken - // place (i.e. menus have been constructed etc). Therefore, inject a kRestoreGame call - // here, instead of the requested function. - int saveSlot = g_loadFromLauncher; - g_loadFromLauncher = -1; // invalidate slot, so that we don't load again - - if (saveSlot < 0) - error("Requested to load invalid save slot"); // should never happen, really - - reg_t restoreArgv[2] = { NULL_REG, make_reg(0, saveSlot) }; // special call (argv[0] is NULL) - kRestoreGame(s, 2, restoreArgv); + // TODO: SCI2.1 equivalent + if (s->loadFromLauncher >= 0 && ( + (kernelFuncNum == 0x8 && getSciVersion() <= SCI_VERSION_1_1) || // DrawPic + (kernelFuncNum == 0x3d && getSciVersion() == SCI_VERSION_2) // GetSaveDir + //(kernelFuncNum == 0x28 && getSciVersion() == SCI_VERSION_2_1) // AddPlane + )) { + + // A game is being loaded from the launcher, and the game is about to draw something on + // screen, hence all initialization has taken place (i.e. menus have been constructed etc). + // Therefore, inject a kRestoreGame call here, instead of the requested function. + // The restore call is injected here mainly for games which have a menu, as the menu is + // constructed when the game starts and is not reconstructed when a saved game is loaded. + int saveSlot = s->loadFromLauncher; + s->loadFromLauncher = -1; // invalidate slot, so that we don't load again + + if (saveSlot < 0) + error("Requested to load invalid save slot"); // should never happen, really + + reg_t restoreArgv[2] = { NULL_REG, make_reg(0, saveSlot) }; // special call (argv[0] is NULL) + kRestoreGame(s, 2, restoreArgv); } else { // Call kernel function s->r_acc = kernelFunc.fun(s, argc, argv); @@ -620,7 +630,7 @@ static void callKernelFunc(EngineState *s, int kernelFuncNum, int argc) { static void gc_countdown(EngineState *s) { if (s->gc_countdown-- <= 0) { - s->gc_countdown = script_gc_interval; + s->gc_countdown = s->script_gc_interval; run_gc(s); } } @@ -708,13 +718,13 @@ void run_vm(EngineState *s, bool restoring) { StackPtr s_temp; // Temporary stack pointer int16 opparams[4]; // opcode parameters - scriptState.restAdjust = s->restAdjust; + s->restAdjustCur = s->restAdjust; // &rest adjusts the parameter count by this value // Current execution data: - scriptState.xs = &(s->_executionStack.back()); + s->xs = &(s->_executionStack.back()); ExecStack *xs_new = NULL; - Object *obj = s->_segMan->getObject(scriptState.xs->objp); - Script *local_script = s->_segMan->getScriptIfLoaded(scriptState.xs->local_segment); + Object *obj = s->_segMan->getObject(s->xs->objp); + Script *local_script = s->_segMan->getScriptIfLoaded(s->xs->local_segment); int old_execution_stack_base = s->execution_stack_base; // Used to detect the stack bottom, for "physical" returns const byte *code_buf = NULL; // (Avoid spurious warning) @@ -729,20 +739,20 @@ void run_vm(EngineState *s, bool restoring) { #ifndef DISABLE_VALIDATIONS // Initialize maximum variable count if (s->script_000->_localsBlock) - scriptState.variables_max[VAR_GLOBAL] = s->script_000->_localsBlock->_locals.size(); + s->variables_max[VAR_GLOBAL] = s->script_000->_localsBlock->_locals.size(); else - scriptState.variables_max[VAR_GLOBAL] = 0; + s->variables_max[VAR_GLOBAL] = 0; #endif - scriptState.variables_seg[VAR_GLOBAL] = s->script_000->_localsSegment; - scriptState.variables_seg[VAR_TEMP] = scriptState.variables_seg[VAR_PARAM] = s->_segMan->findSegmentByType(SEG_TYPE_STACK); - scriptState.variables_base[VAR_TEMP] = scriptState.variables_base[VAR_PARAM] = s->stack_base; + s->variables_seg[VAR_GLOBAL] = s->script_000->_localsSegment; + s->variables_seg[VAR_TEMP] = s->variables_seg[VAR_PARAM] = s->_segMan->findSegmentByType(SEG_TYPE_STACK); + s->variables_base[VAR_TEMP] = s->variables_base[VAR_PARAM] = s->stack_base; // SCI code reads the zeroth argument to determine argc if (s->script_000->_localsBlock) - scriptState.variables_base[VAR_GLOBAL] = scriptState.variables[VAR_GLOBAL] = s->script_000->_localsBlock->_locals.begin(); + s->variables_base[VAR_GLOBAL] = s->variables[VAR_GLOBAL] = s->script_000->_localsBlock->_locals.begin(); else - scriptState.variables_base[VAR_GLOBAL] = scriptState.variables[VAR_GLOBAL] = NULL; + s->variables_base[VAR_GLOBAL] = s->variables[VAR_GLOBAL] = NULL; s->_executionStackPosChanged = true; // Force initialization @@ -750,63 +760,63 @@ void run_vm(EngineState *s, bool restoring) { int var_type; // See description below int var_number; - g_debugState.old_pc_offset = scriptState.xs->addr.pc.offset; - g_debugState.old_sp = scriptState.xs->sp; + g_debugState.old_pc_offset = s->xs->addr.pc.offset; + g_debugState.old_sp = s->xs->sp; if (s->_executionStackPosChanged) { Script *scr; - scriptState.xs = &(s->_executionStack.back()); + s->xs = &(s->_executionStack.back()); s->_executionStackPosChanged = false; - scr = s->_segMan->getScriptIfLoaded(scriptState.xs->addr.pc.segment); + scr = s->_segMan->getScriptIfLoaded(s->xs->addr.pc.segment); if (!scr) { // No script? Implicit return via fake instruction buffer - warning("Running on non-existant script in segment %x", scriptState.xs->addr.pc.segment); + warning("Running on non-existant script in segment %x", s->xs->addr.pc.segment); code_buf = _fake_return_buffer; #ifndef DISABLE_VALIDATIONS code_buf_size = 2; #endif - scriptState.xs->addr.pc.offset = 1; + s->xs->addr.pc.offset = 1; scr = NULL; obj = NULL; } else { - obj = s->_segMan->getObject(scriptState.xs->objp); + obj = s->_segMan->getObject(s->xs->objp); code_buf = scr->_buf; #ifndef DISABLE_VALIDATIONS - code_buf_size = scr->_bufSize; + code_buf_size = scr->getBufSize(); #endif - local_script = s->_segMan->getScriptIfLoaded(scriptState.xs->local_segment); + local_script = s->_segMan->getScriptIfLoaded(s->xs->local_segment); if (!local_script) { - warning("Could not find local script from segment %x", scriptState.xs->local_segment); + warning("Could not find local script from segment %x", s->xs->local_segment); local_script = NULL; - scriptState.variables_base[VAR_LOCAL] = scriptState.variables[VAR_LOCAL] = NULL; + s->variables_base[VAR_LOCAL] = s->variables[VAR_LOCAL] = NULL; #ifndef DISABLE_VALIDATIONS - scriptState.variables_max[VAR_LOCAL] = 0; + s->variables_max[VAR_LOCAL] = 0; #endif } else { - scriptState.variables_seg[VAR_LOCAL] = local_script->_localsSegment; + s->variables_seg[VAR_LOCAL] = local_script->_localsSegment; if (local_script->_localsBlock) - scriptState.variables_base[VAR_LOCAL] = scriptState.variables[VAR_LOCAL] = local_script->_localsBlock->_locals.begin(); + s->variables_base[VAR_LOCAL] = s->variables[VAR_LOCAL] = local_script->_localsBlock->_locals.begin(); else - scriptState.variables_base[VAR_LOCAL] = scriptState.variables[VAR_LOCAL] = NULL; + s->variables_base[VAR_LOCAL] = s->variables[VAR_LOCAL] = NULL; #ifndef DISABLE_VALIDATIONS if (local_script->_localsBlock) - scriptState.variables_max[VAR_LOCAL] = local_script->_localsBlock->_locals.size(); + s->variables_max[VAR_LOCAL] = local_script->_localsBlock->_locals.size(); else - scriptState.variables_max[VAR_LOCAL] = 0; - scriptState.variables_max[VAR_TEMP] = scriptState.xs->sp - scriptState.xs->fp; - scriptState.variables_max[VAR_PARAM] = scriptState.xs->argc + 1; + s->variables_max[VAR_LOCAL] = 0; + s->variables_max[VAR_TEMP] = s->xs->sp - s->xs->fp; + s->variables_max[VAR_PARAM] = s->xs->argc + 1; #endif } - scriptState.variables[VAR_TEMP] = scriptState.xs->fp; - scriptState.variables[VAR_PARAM] = scriptState.xs->variables_argp; + s->variables[VAR_TEMP] = s->xs->fp; + s->variables[VAR_PARAM] = s->xs->variables_argp; } } - if (script_abort_flag || g_engine->shouldQuit()) + if (s->script_abort_flag || g_engine->shouldQuit()) return; // Emergency // Debug if this has been requested: @@ -821,18 +831,20 @@ void run_vm(EngineState *s, bool restoring) { } #ifndef DISABLE_VALIDATIONS - if (scriptState.xs->sp < scriptState.xs->fp) - error("run_vm(): stack underflow"); + if (s->xs->sp < s->xs->fp) + error("run_vm(): stack underflow, sp: %04x:%04x, fp: %04x:%04x", + PRINT_REG(*s->xs->sp), PRINT_REG(*s->xs->fp)); - scriptState.variables_max[VAR_TEMP] = scriptState.xs->sp - scriptState.xs->fp; + s->variables_max[VAR_TEMP] = s->xs->sp - s->xs->fp; - if (scriptState.xs->addr.pc.offset >= code_buf_size) - error("run_vm(): program counter gone astray"); + if (s->xs->addr.pc.offset >= code_buf_size) + error("run_vm(): program counter gone astray, addr: %d, code buffer size: %d", + s->xs->addr.pc.offset, code_buf_size); #endif // Get opcode byte extOpcode; - scriptState.xs->addr.pc.offset += readPMachineInstruction(code_buf + scriptState.xs->addr.pc.offset, extOpcode, opparams); + s->xs->addr.pc.offset += readPMachineInstruction(code_buf + s->xs->addr.pc.offset, extOpcode, opparams); const byte opcode = extOpcode >> 1; switch (opcode) { @@ -1063,16 +1075,16 @@ void run_vm(EngineState *s, bool restoring) { case op_bt: // 0x17 (23) if (s->r_acc.offset || s->r_acc.segment) - scriptState.xs->addr.pc.offset += opparams[0]; + s->xs->addr.pc.offset += opparams[0]; break; case op_bnt: // 0x18 (24) if (!(s->r_acc.offset || s->r_acc.segment)) - scriptState.xs->addr.pc.offset += opparams[0]; + s->xs->addr.pc.offset += opparams[0]; break; case op_jmp: // 0x19 (25) - scriptState.xs->addr.pc.offset += opparams[0]; + s->xs->addr.pc.offset += opparams[0]; break; case op_ldi: // 0x1a (26) @@ -1088,34 +1100,34 @@ void run_vm(EngineState *s, bool restoring) { break; case op_toss: // 0x1d (29) - scriptState.xs->sp--; + s->xs->sp--; break; case op_dup: // 0x1e (30) - r_temp = scriptState.xs->sp[-1]; + r_temp = s->xs->sp[-1]; PUSH32(r_temp); break; case op_link: // 0x1f (31) for (int i = 0; i < opparams[0]; i++) - scriptState.xs->sp[i] = NULL_REG; - scriptState.xs->sp += opparams[0]; + s->xs->sp[i] = NULL_REG; + s->xs->sp += opparams[0]; break; case op_call: { // 0x20 (32) int argc = (opparams[1] >> 1) // Given as offset, but we need count - + 1 + scriptState.restAdjust; - StackPtr call_base = scriptState.xs->sp - argc; - scriptState.xs->sp[1].offset += scriptState.restAdjust; - - xs_new = add_exec_stack_entry(s->_executionStack, make_reg(scriptState.xs->addr.pc.segment, - scriptState.xs->addr.pc.offset + opparams[0]), - scriptState.xs->sp, scriptState.xs->objp, - (validate_arithmetic(*call_base)) + scriptState.restAdjust, - call_base, NULL_SELECTOR, scriptState.xs->objp, - s->_executionStack.size()-1, scriptState.xs->local_segment); - scriptState.restAdjust = 0; // Used up the &rest adjustment - scriptState.xs->sp = call_base; + + 1 + s->restAdjustCur; + StackPtr call_base = s->xs->sp - argc; + s->xs->sp[1].offset += s->restAdjustCur; + + xs_new = add_exec_stack_entry(s->_executionStack, make_reg(s->xs->addr.pc.segment, + s->xs->addr.pc.offset + opparams[0]), + s->xs->sp, s->xs->objp, + (validate_arithmetic(*call_base)) + s->restAdjustCur, + call_base, NULL_SELECTOR, s->xs->objp, + s->_executionStack.size()-1, s->xs->local_segment); + s->restAdjustCur = 0; // Used up the &rest adjustment + s->xs->sp = call_base; s->_executionStackPosChanged = true; break; @@ -1124,23 +1136,23 @@ void run_vm(EngineState *s, bool restoring) { case op_callk: { // 0x21 (33) gc_countdown(s); - scriptState.xs->sp -= (opparams[1] >> 1) + 1; + s->xs->sp -= (opparams[1] >> 1) + 1; bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); if (!oldScriptHeader) { - scriptState.xs->sp -= scriptState.restAdjust; - s->restAdjust = 0; // We just used up the scriptState.restAdjust, remember? + s->xs->sp -= s->restAdjustCur; + s->restAdjust = 0; // We just used up the s->restAdjustCur, remember? } - int argc = validate_arithmetic(scriptState.xs->sp[0]); + int argc = validate_arithmetic(s->xs->sp[0]); if (!oldScriptHeader) - argc += scriptState.restAdjust; + argc += s->restAdjustCur; callKernelFunc(s, opparams[0], argc); if (!oldScriptHeader) - scriptState.restAdjust = s->restAdjust; + s->restAdjustCur = s->restAdjust; // Calculate xs again: The kernel function might // have spawned a new VM @@ -1151,27 +1163,27 @@ void run_vm(EngineState *s, bool restoring) { } case op_callb: // 0x22 (34) - temp = ((opparams[1] >> 1) + scriptState.restAdjust + 1); - s_temp = scriptState.xs->sp; - scriptState.xs->sp -= temp; - - scriptState.xs->sp[0].offset += scriptState.restAdjust; - xs_new = execute_method(s, 0, opparams[0], s_temp, scriptState.xs->objp, - scriptState.xs->sp[0].offset, scriptState.xs->sp); - scriptState.restAdjust = 0; // Used up the &rest adjustment + temp = ((opparams[1] >> 1) + s->restAdjustCur + 1); + s_temp = s->xs->sp; + s->xs->sp -= temp; + + s->xs->sp[0].offset += s->restAdjustCur; + xs_new = execute_method(s, 0, opparams[0], s_temp, s->xs->objp, + s->xs->sp[0].offset, s->xs->sp); + s->restAdjustCur = 0; // Used up the &rest adjustment if (xs_new) // in case of error, keep old stack s->_executionStackPosChanged = true; break; case op_calle: // 0x23 (35) - temp = ((opparams[2] >> 1) + scriptState.restAdjust + 1); - s_temp = scriptState.xs->sp; - scriptState.xs->sp -= temp; + temp = ((opparams[2] >> 1) + s->restAdjustCur + 1); + s_temp = s->xs->sp; + s->xs->sp -= temp; - scriptState.xs->sp[0].offset += scriptState.restAdjust; - xs_new = execute_method(s, opparams[0], opparams[1], s_temp, scriptState.xs->objp, - scriptState.xs->sp[0].offset, scriptState.xs->sp); - scriptState.restAdjust = 0; // Used up the &rest adjustment + s->xs->sp[0].offset += s->restAdjustCur; + xs_new = execute_method(s, opparams[0], opparams[1], s_temp, s->xs->objp, + s->xs->sp[0].offset, s->xs->sp); + s->restAdjustCur = 0; // Used up the &rest adjustment if (xs_new) // in case of error, keep old stack s->_executionStackPosChanged = true; @@ -1179,8 +1191,8 @@ void run_vm(EngineState *s, bool restoring) { case op_ret: // 0x24 (36) do { - StackPtr old_sp2 = scriptState.xs->sp; - StackPtr old_fp = scriptState.xs->fp; + StackPtr old_sp2 = s->xs->sp; + StackPtr old_fp = s->xs->fp; ExecStack *old_xs = &(s->_executionStack.back()); if ((int)s->_executionStack.size() - 1 == s->execution_stack_base) { // Have we reached the base? @@ -1189,7 +1201,7 @@ void run_vm(EngineState *s, bool restoring) { s->_executionStack.pop_back(); s->_executionStackPosChanged = true; - s->restAdjust = scriptState.restAdjust; // Update &rest + s->restAdjust = s->restAdjustCur; // Update &rest return; // "Hard" return } @@ -1205,33 +1217,33 @@ void run_vm(EngineState *s, bool restoring) { // Not reached the base, so let's do a soft return s->_executionStack.pop_back(); s->_executionStackPosChanged = true; - scriptState.xs = &(s->_executionStack.back()); + s->xs = &(s->_executionStack.back()); - if (scriptState.xs->sp == CALL_SP_CARRY // Used in sends to 'carry' the stack pointer - || scriptState.xs->type != EXEC_STACK_TYPE_CALL) { - scriptState.xs->sp = old_sp2; - scriptState.xs->fp = old_fp; + if (s->xs->sp == CALL_SP_CARRY // Used in sends to 'carry' the stack pointer + || s->xs->type != EXEC_STACK_TYPE_CALL) { + s->xs->sp = old_sp2; + s->xs->fp = old_fp; } - } while (scriptState.xs->type == EXEC_STACK_TYPE_VARSELECTOR); + } while (s->xs->type == EXEC_STACK_TYPE_VARSELECTOR); // Iterate over all varselector accesses s->_executionStackPosChanged = true; - xs_new = scriptState.xs; + xs_new = s->xs; break; case op_send: // 0x25 (37) - s_temp = scriptState.xs->sp; - scriptState.xs->sp -= ((opparams[0] >> 1) + scriptState.restAdjust); // Adjust stack + s_temp = s->xs->sp; + s->xs->sp -= ((opparams[0] >> 1) + s->restAdjustCur); // Adjust stack - scriptState.xs->sp[1].offset += scriptState.restAdjust; + s->xs->sp[1].offset += s->restAdjustCur; xs_new = send_selector(s, s->r_acc, s->r_acc, s_temp, - (int)(opparams[0] >> 1) + (uint16)scriptState.restAdjust, scriptState.xs->sp); + (int)(opparams[0] >> 1) + (uint16)s->restAdjustCur, s->xs->sp); - if (xs_new && xs_new != scriptState.xs) + if (xs_new && xs_new != s->xs) s->_executionStackPosChanged = true; - scriptState.restAdjust = 0; + s->restAdjustCur = 0; break; @@ -1242,7 +1254,7 @@ void run_vm(EngineState *s, bool restoring) { case op_class: // 0x28 (40) s->r_acc = s->_segMan->getClassAddress((unsigned)opparams[0], SCRIPT_GET_LOCK, - scriptState.xs->addr.pc); + s->xs->addr.pc); break; case 0x29: // (41) @@ -1250,48 +1262,48 @@ void run_vm(EngineState *s, bool restoring) { break; case op_self: // 0x2a (42) - s_temp = scriptState.xs->sp; - scriptState.xs->sp -= ((opparams[0] >> 1) + scriptState.restAdjust); // Adjust stack + s_temp = s->xs->sp; + s->xs->sp -= ((opparams[0] >> 1) + s->restAdjustCur); // Adjust stack - scriptState.xs->sp[1].offset += scriptState.restAdjust; - xs_new = send_selector(s, scriptState.xs->objp, scriptState.xs->objp, - s_temp, (int)(opparams[0] >> 1) + (uint16)scriptState.restAdjust, - scriptState.xs->sp); + s->xs->sp[1].offset += s->restAdjustCur; + xs_new = send_selector(s, s->xs->objp, s->xs->objp, + s_temp, (int)(opparams[0] >> 1) + (uint16)s->restAdjustCur, + s->xs->sp); - if (xs_new && xs_new != scriptState.xs) + if (xs_new && xs_new != s->xs) s->_executionStackPosChanged = true; - scriptState.restAdjust = 0; + s->restAdjustCur = 0; break; case op_super: // 0x2b (43) - r_temp = s->_segMan->getClassAddress(opparams[0], SCRIPT_GET_LOAD, scriptState.xs->addr.pc); + r_temp = s->_segMan->getClassAddress(opparams[0], SCRIPT_GET_LOAD, s->xs->addr.pc); if (!r_temp.segment) error("[VM]: Invalid superclass in object"); else { - s_temp = scriptState.xs->sp; - scriptState.xs->sp -= ((opparams[1] >> 1) + scriptState.restAdjust); // Adjust stack + s_temp = s->xs->sp; + s->xs->sp -= ((opparams[1] >> 1) + s->restAdjustCur); // Adjust stack - scriptState.xs->sp[1].offset += scriptState.restAdjust; - xs_new = send_selector(s, r_temp, scriptState.xs->objp, s_temp, - (int)(opparams[1] >> 1) + (uint16)scriptState.restAdjust, - scriptState.xs->sp); + s->xs->sp[1].offset += s->restAdjustCur; + xs_new = send_selector(s, r_temp, s->xs->objp, s_temp, + (int)(opparams[1] >> 1) + (uint16)s->restAdjustCur, + s->xs->sp); - if (xs_new && xs_new != scriptState.xs) + if (xs_new && xs_new != s->xs) s->_executionStackPosChanged = true; - scriptState.restAdjust = 0; + s->restAdjustCur = 0; } break; case op_rest: // 0x2c (44) temp = (uint16) opparams[0]; // First argument - scriptState.restAdjust = MAX<int16>(scriptState.xs->argc - temp + 1, 0); // +1 because temp counts the paramcount while argc doesn't + s->restAdjustCur = MAX<int16>(s->xs->argc - temp + 1, 0); // +1 because temp counts the paramcount while argc doesn't - for (; temp <= scriptState.xs->argc; temp++) - PUSH32(scriptState.xs->variables_argp[temp]); + for (; temp <= s->xs->argc; temp++) + PUSH32(s->xs->variables_argp[temp]); break; @@ -1300,8 +1312,8 @@ void run_vm(EngineState *s, bool restoring) { var_number = temp & 0x03; // Get variable type // Get variable block offset - r_temp.segment = scriptState.variables_seg[var_number]; - r_temp.offset = scriptState.variables[var_number] - scriptState.variables_base[var_number]; + r_temp.segment = s->variables_seg[var_number]; + r_temp.offset = s->variables[var_number] - s->variables_base[var_number]; if (temp & 0x08) // Add accumulator offset if requested r_temp.offset += signed_validate_arithmetic(s->r_acc); @@ -1314,7 +1326,7 @@ void run_vm(EngineState *s, bool restoring) { case op_selfID: // 0x2e (46) - s->r_acc = scriptState.xs->objp; + s->r_acc = s->xs->objp; break; case 0x2f: // (47) @@ -1382,17 +1394,17 @@ void run_vm(EngineState *s, bool restoring) { break; case op_lofsa: // 0x39 (57) - s->r_acc.segment = scriptState.xs->addr.pc.segment; + s->r_acc.segment = s->xs->addr.pc.segment; switch (g_sci->_features->detectLofsType()) { case SCI_VERSION_1_1: - s->r_acc.offset = opparams[0] + local_script->_scriptSize; + s->r_acc.offset = opparams[0] + local_script->getScriptSize(); break; case SCI_VERSION_1_MIDDLE: s->r_acc.offset = opparams[0]; break; default: - s->r_acc.offset = scriptState.xs->addr.pc.offset + opparams[0]; + s->r_acc.offset = s->xs->addr.pc.offset + opparams[0]; } #ifndef DISABLE_VALIDATIONS @@ -1404,17 +1416,17 @@ void run_vm(EngineState *s, bool restoring) { break; case op_lofss: // 0x3a (58) - r_temp.segment = scriptState.xs->addr.pc.segment; + r_temp.segment = s->xs->addr.pc.segment; switch (g_sci->_features->detectLofsType()) { case SCI_VERSION_1_1: - r_temp.offset = opparams[0] + local_script->_scriptSize; + r_temp.offset = opparams[0] + local_script->getScriptSize(); break; case SCI_VERSION_1_MIDDLE: r_temp.offset = opparams[0]; break; default: - r_temp.offset = scriptState.xs->addr.pc.offset + opparams[0]; + r_temp.offset = s->xs->addr.pc.offset + opparams[0]; } #ifndef DISABLE_VALIDATIONS @@ -1440,10 +1452,10 @@ void run_vm(EngineState *s, bool restoring) { case op_pushSelf: // 0x3e (62) if (!(extOpcode & 1)) { - PUSH32(scriptState.xs->objp); + PUSH32(s->xs->objp); } else { // Debug opcode op_file, skip null-terminated string (file name) - while (code_buf[scriptState.xs->addr.pc.offset++]) ; + while (code_buf[s->xs->addr.pc.offset++]) ; } break; @@ -1657,16 +1669,16 @@ void run_vm(EngineState *s, bool restoring) { } // switch (opcode) if (s->_executionStackPosChanged) // Force initialization - scriptState.xs = xs_new; + s->xs = xs_new; //#ifndef DISABLE_VALIDATIONS - if (scriptState.xs != &(s->_executionStack.back())) { + if (s->xs != &(s->_executionStack.back())) { warning("xs is stale (%p vs %p); last command was %02x", - (void *)scriptState.xs, (void *)&(s->_executionStack.back()), + (void *)s->xs, (void *)&(s->_executionStack.back()), opcode); } //#endif - ++script_step_counter; + ++s->script_step_counter; } } @@ -1676,17 +1688,16 @@ static void _init_stack_base_with_selector(EngineState *s, Selector selector) { } static EngineState *_game_run(EngineState *&s) { - EngineState *successor = NULL; - int game_is_finished = 0; + bool restoring = false; if (DebugMan.isDebugChannelEnabled(kDebugLevelOnStartup)) g_sci->getSciDebugger()->attach(); do { s->_executionStackPosChanged = false; - run_vm(s, successor ? true : false); + run_vm(s, restoring); if (s->restarting_flags & SCI_GAME_IS_RESTARTING_NOW) { // Restart was requested? - successor = NULL; + restoring = false; s->_executionStack.clear(); s->_executionStackPosChanged = false; @@ -1700,17 +1711,15 @@ static EngineState *_game_run(EngineState *&s) { send_selector(s, s->_gameObj, s->_gameObj, s->stack_base, 2, s->stack_base); - script_abort_flag = 0; + s->script_abort_flag = 0; s->restarting_flags = SCI_GAME_WAS_RESTARTED; } else { - successor = s->successor; - if (successor) { + restoring = s->restoring; + if (restoring) { game_exit(s); - delete s; - s = successor; - - if (script_abort_flag == 2) { + s->restoring = false; + if (s->script_abort_flag == 2) { debugC(2, kDebugLevelVM, "Restarting with replay()"); s->_executionStack.clear(); // Restart with replay @@ -1719,12 +1728,12 @@ static EngineState *_game_run(EngineState *&s) { send_selector(s, s->_gameObj, s->_gameObj, s->stack_base, 2, s->stack_base); } - script_abort_flag = 0; + s->script_abort_flag = 0; } else - game_is_finished = 1; + break; // exit loop } - } while (!game_is_finished); + } while (true); return s; } @@ -1750,19 +1759,18 @@ int game_run(EngineState **_s) { return 0; } -void quit_vm() { - script_abort_flag = 1; // Terminate VM +void quit_vm(EngineState *s) { + s->script_abort_flag = 1; // Terminate VM g_debugState.seeking = kDebugSeekNothing; g_debugState.runningStep = 0; } -reg_t* ObjVarRef::getPointer(SegManager *segMan) const { +reg_t *ObjVarRef::getPointer(SegManager *segMan) const { Object *o = segMan->getObject(obj); - if (!o) return 0; - return &(o->_variables[varindex]); + return o ? &o->getVariableRef(varindex) : 0; } -reg_t* ExecStack::getVarPointer(SegManager *segMan) const { +reg_t *ExecStack::getVarPointer(SegManager *segMan) const { assert(type == EXEC_STACK_TYPE_VARSELECTOR); return addr.varp.getPointer(segMan); } diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h index f6151dfc9d..67a6bd0dc3 100644 --- a/engines/sci/engine/vm.h +++ b/engines/sci/engine/vm.h @@ -43,44 +43,9 @@ class ResourceManager; /** Number of bytes to be allocated for the stack */ #define VM_STACK_SIZE 0x1000 -/** Maximum number of calls residing on the stack */ -#define SCRIPT_MAX_EXEC_STACK 256 -/** Maximum number of entries in the class table */ -#define SCRIPT_MAX_CLASSTABLE_SIZE 256 -/** Maximum number of cloned objects on the heap */ -#define SCRIPT_MAX_CLONES 256 - - -/** Object-relative offset of the selector area inside a script */ -#define SCRIPT_SELECTOR_OFFSET 8 -8 - -/** Object-relative offset of the pointer to the underlying script's local variables */ -#define SCRIPT_LOCALVARPTR_OFFSET 2 -8 - -/** Object-relative offset of the selector counter */ -#define SCRIPT_SELECTORCTR_OFFSET 6 -8 - -/** Object-relative offset of the offset of the function area */ -#define SCRIPT_FUNCTAREAPTR_OFFSET 4 -8 - -/** Offset that has to be added to the function area pointer */ -#define SCRIPT_FUNCTAREAPTR_MAGIC 8 -8 - -/** Offset of the name pointer */ -#define SCRIPT_NAME_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? 14 -8 : 16) - -/** Object-relative offset of the -info- selector */ -#define SCRIPT_INFO_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? 12 -8 : 14) - -/** Flag fo the -info- selector */ -#define SCRIPT_INFO_CLONE 0x0001 - -/** Flag for the -info- selector */ -#define SCRIPT_INFO_CLASS 0x8000 - - /** Magical object identifier */ #define SCRIPT_OBJECT_MAGIC_NUMBER 0x1234 + /** Offset of this identifier */ #define SCRIPT_OBJECT_MAGIC_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? -8 : 0) @@ -89,13 +54,10 @@ class ResourceManager; #define SCRIPT_SUPERCLASS_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? 10 -8 : 12) -/** Magic adjustment value for lofsa and lofss */ -#define SCRIPT_LOFS_MAGIC 3 - /** Stack pointer value: Use predecessor's value */ #define CALL_SP_CARRY NULL -/** Types of selectors as returned by lookup_selector() below. */ +/** Types of selectors as returned by lookupSelector() below. */ enum SelectorType { kSelectorNone = 0, kSelectorVariable, @@ -195,7 +157,6 @@ struct SelectorCache { // Used for auto detection purposes Selector overlay; ///< Used to determine if a game is using old gfx functions or not - Selector setCursor; ///< For cursor semantics autodetection // SCI1.1 Mac icon bar selectors Selector iconIndex; ///< Used to index icon bar objects @@ -263,41 +224,11 @@ enum { VAR_PARAM = 3 }; -/** - * Structure for storing the current internal state of the VM. - */ -struct ScriptState { - ExecStack *xs; - int16 restAdjust; - reg_t *variables[4]; ///< global, local, temp, param, as immediate pointers - reg_t *variables_base[4]; ///< Used for referencing VM ops - SegmentId variables_seg[4]; ///< Same as above, contains segment IDs - int variables_max[4]; ///< Max. values for all variables -}; - -/** - * The current internal state of the VM. - */ -extern ScriptState scriptState; - -/** - * Set this to 1 to abort script execution immediately. Aborting will - * leave the debug exec stack intact. - * Set it to 2 to force a replay afterwards. - */ -extern int script_abort_flag; - /** Number of kernel calls in between gcs; should be < 50000 */ enum { GC_INTERVAL = 32768 }; -/** Initially GC_DELAY, can be set at runtime */ -extern int script_gc_interval; - -/** Number of steps executed */ -extern int script_step_counter; - /** * Executes function pubfunct of the specified script. @@ -379,7 +310,7 @@ int script_init_engine(EngineState *); * kSelectorMethod if the selector represents a * method */ -SelectorType lookup_selector(SegManager *segMan, reg_t obj, Selector selectorid, +SelectorType lookupSelector(SegManager *segMan, reg_t obj, Selector selectorid, ObjVarRef *varp, reg_t *fptr); /** @@ -469,7 +400,7 @@ int game_exit(EngineState *s); /** * Instructs the virtual machine to abort */ -void quit_vm(); +void quit_vm(EngineState *s); /** * Read a PMachine instruction from a memory buffer and return its length. |