aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/engine/script.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/engine/script.cpp')
-rw-r--r--engines/sci/engine/script.cpp298
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);