diff options
Diffstat (limited to 'engines/sci/engine/script.cpp')
-rw-r--r-- | engines/sci/engine/script.cpp | 298 |
1 files changed, 217 insertions, 81 deletions
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index 1f32e50b67..85a07f0efc 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->getBufSize(), "Locals beyond end of script\n"); + VERIFY(location.offset + 1 < (uint16)scr->_bufSize, "Locals beyond end of script\n"); if (getSciVersion() >= SCI_VERSION_1_1) count = READ_SCI11ENDIAN_UINT16(scr->_buf + location.offset - 2); @@ -185,61 +185,81 @@ void SegManager::scriptInitialiseLocals(reg_t location) { scr->_localsOffset = location.offset; - 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; + 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; } LocalVariables *locals = allocLocalsSegment(scr, count); if (locals) { uint i; - const byte *base = (const byte *)(scr->_buf + location.offset); + byte *base = (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); - const byte *seeker = scr->_heapStart + 4 + READ_SCI11ENDIAN_UINT16(scr->_heapStart + 2) * 2; + 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) & kInfoFlagClass) { // -info- selector + if (READ_SCI11ENDIAN_UINT16(seeker + 14) & SCRIPT_INFO_CLASS) { 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; } seeker = scr->_heapStart + 4 + READ_SCI11ENDIAN_UINT16(scr->_heapStart + 2) * 2; while (READ_SCI11ENDIAN_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) { - reg_t reg = make_reg(seg, seeker - scr->_buf); - Object *obj = scr->scriptObjInit(reg); + reg_t reg; + Object *obj; + + reg.segment = seg; + reg.offset = seeker - scr->_buf; + 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 @@ -252,24 +272,90 @@ void SegManager::scriptInitialiseObjectsSci11(SegmentId seg) { } } -void script_instantiate_sci0(Script *scr, int segmentId, SegManager *segMan) { + + +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); + + reg_t reg; + reg.segment = seg_id; + reg.offset = 0; + + // 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) { 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 localsCount = READ_LE_UINT16(scr->_buf); - if (localsCount) - segMan->scriptInitialiseLocalsZero(segmentId, localsCount); + int locals_nr = READ_LE_UINT16(script->data); + if (locals_nr) + segMan->scriptInitialiseLocalsZero(seg_id, locals_nr); } // Now do a first pass through the script objects to find the - // local variable blocks + // export table and local variable block do { objType = scr->getHeap(curOffset); @@ -277,30 +363,48 @@ void script_instantiate_sci0(Script *scr, int segmentId, SegManager *segMan) { 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(segmentId, curOffset)); + segMan->scriptInitialiseLocals(make_reg(seg_id, 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->classTableSize()) { - if (species == (int)segMan->classTableSize()) { + if (species < 0 || species >= (int)segMan->_classtable.size()) { + if (species == (int)segMan->_classtable.size()) { // Happens in the LSL2 demo warning("Applying workaround for an off-by-one invalid species access"); - segMan->resizeClassTable(segMan->classTableSize() + 1); + segMan->_classtable.resize(segMan->_classtable.size() + 1); } else { - error("Invalid species %d(0x%x) not in interval " - "[0,%d) while instantiating script at segment %d\n", - species, species, segMan->classTableSize(), - segmentId); - return; + 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; } } - segMan->setClassOffset(species, make_reg(segmentId, classpos)); + segMan->_classtable[species].reg.segment = seg_id; + segMan->_classtable[species].reg.offset = classpos; // Set technical class position-- into the block allocated for it } break; @@ -310,7 +414,7 @@ void script_instantiate_sci0(Script *scr, int segmentId, SegManager *segMan) { } curOffset += objLength - 4; - } while (objType != 0 && curOffset < scr->getScriptSize() - 2); + } while (objType != 0 && curOffset < script->size - 2); // And now a second pass to adjust objects and class pointers, and the general pointers objLength = 0; @@ -324,7 +428,7 @@ void script_instantiate_sci0(Script *scr, int segmentId, SegManager *segMan) { objLength = scr->getHeap(curOffset + 2); curOffset += 4; // skip header - reg_t addr = make_reg(segmentId, curOffset); + reg_t addr = make_reg(seg_id, curOffset); switch (objType) { case SCI_OBJ_CODE: @@ -333,61 +437,93 @@ void script_instantiate_sci0(Script *scr, int segmentId, SegManager *segMan) { case SCI_OBJ_OBJECT: case SCI_OBJ_CLASS: { // object or class? Object *obj = scr->scriptObjInit(addr); - obj->initSpecies(segMan, addr); - if (!obj->initBaseObject(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 { 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 < scr->getScriptSize() - 2); + } while (objType != 0 && curOffset < script->size - 2); + + if (relocation >= 0) + scr->scriptRelocate(make_reg(seg_id, relocation)); + + return seg_id; // instantiation successful } -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); - } +int script_instantiate_sci11(ResourceManager *resMan, SegManager *segMan, int script_nr) { + Resource *script, *heap; + int _heapStart; + reg_t reg; + int was_new; - scr->init(scriptNum, resMan); - scr->load(resMan); + const int seg_id = script_instantiate_common(resMan, segMan, script_nr, &script, &heap, &was_new); - 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)); - } + if (was_new) + return seg_id; + + Script *scr = segMan->getScript(seg_id); + + _heapStart = script->size; + if (script->size & 2) + _heapStart++; + + scr->mcpyInOut(0, script->data, script->size); + scr->mcpyInOut(_heapStart, heap->data, heap->size); - return segmentId; + if (READ_SCI11ENDIAN_UINT16(script->data + 6) > 0) + scr->setExportTableOffset(6); + + reg.segment = seg_id; + reg.offset = _heapStart + 4; + segMan->scriptInitialiseLocals(reg); + + segMan->scriptRelocateExportsSci11(seg_id); + segMan->scriptInitialiseObjectsSci11(seg_id); + + reg.offset = READ_SCI11ENDIAN_UINT16(heap->data); + scr->heapRelocate(reg); + + return seg_id; +} + +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); } void script_uninstantiate_sci0(SegManager *segMan, int script_nr, SegmentId seg) { bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); reg_t reg = make_reg(seg, oldScriptHeader ? 2 : 0); - int objType, objLength = 0; + int objType, objLength; Script *scr = segMan->getScript(seg); // Make a pass over the object in order uninstantiate all superclasses + objLength = 0; do { reg.offset += objLength; // Step over the last checked object @@ -407,7 +543,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->getClass(superclass).script; + int superclass_script = segMan->_classtable[superclass].script; if (superclass_script == script_nr) { if (scr->getLockers()) @@ -441,9 +577,9 @@ void script_uninstantiate(SegManager *segMan, int script_nr) { return; // Free all classtable references to this script - for (uint i = 0; i < segMan->classTableSize(); i++) - if (segMan->getClass(i).reg.segment == segment) - segMan->setClassOffset(i, NULL_REG); + for (uint i = 0; i < segMan->_classtable.size(); i++) + if (segMan->_classtable[i].reg.segment == segment) + segMan->_classtable[i].reg = NULL_REG; if (getSciVersion() < SCI_VERSION_1_1) script_uninstantiate_sci0(segMan, script_nr, segment); |