From bca7c6eef385b3818e1443038f3326f8f02a895a Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 3 Feb 2010 01:34:15 +0000 Subject: SCI: Move more stuff around svn-id: r47836 --- engines/sci/engine/script.cpp | 155 +++++++++++++++ engines/sci/engine/seg_manager.cpp | 390 +------------------------------------ engines/sci/engine/seg_manager.h | 13 +- engines/sci/engine/segment.cpp | 205 ++++++++++++++++++- 4 files changed, 378 insertions(+), 385 deletions(-) diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index 604aff557c..32bc9182f3 100644 --- a/engines/sci/engine/script.cpp +++ b/engines/sci/engine/script.cpp @@ -116,6 +116,161 @@ void script_adjust_opcode_formats(EngineState *s) { #define INST_LOOKUP_CLASS(id) ((id == 0xffff)? NULL_REG : segMan->getClassAddress(id, SCRIPT_GET_LOCK, reg)) + +void SegManager::createClassTable() { + Resource *vocab996 = _resMan->findResource(ResourceId(kResourceTypeVocab, 996), 1); + + if (!vocab996) + error("SegManager: failed to open vocab 996"); + + int totalClasses = vocab996->size >> 2; + _classtable.resize(totalClasses); + + for (uint16 classNr = 0; classNr < totalClasses; classNr++) { + uint16 scriptNr = READ_LE_UINT16(vocab996->data + classNr * 4 + 2); + + _classtable[classNr].reg = NULL_REG; + _classtable[classNr].script = scriptNr; + } + + _resMan->unlockResource(vocab996); +} + +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()); + return NULL_REG; + } else { + Class *the_class = &_classtable[classnr]; + if (!the_class->reg.segment) { + getScriptSegment(the_class->script, lock); + + if (!the_class->reg.segment) { + error("[VM] Trying to instantiate class %x by instantiating script 0x%x (%03d) failed;" + " Entering debugger.", classnr, the_class->script, the_class->script); + return NULL_REG; + } + } else + if (caller.segment != the_class->reg.segment) + getScript(the_class->reg.segment)->incrementLockers(); + + return the_class->reg; + } +} + +void SegManager::scriptInitialiseLocalsZero(SegmentId seg, int count) { + Script *scr = getScript(seg); + + scr->_localsOffset = -count * 2; // Make sure it's invalid + + allocLocalsSegment(scr, count); +} + +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"); + + if (getSciVersion() >= SCI_VERSION_1_1) + count = READ_LE_UINT16(scr->_buf + location.offset - 2); + else + count = (READ_LE_UINT16(scr->_buf + location.offset - 2) - 4) >> 1; + // half block size + + 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; + } + + LocalVariables *locals = allocLocalsSegment(scr, count); + if (locals) { + uint i; + byte *base = (byte *)(scr->_buf + location.offset); + + for (i = 0; i < count; i++) + locals->_locals[i].offset = READ_LE_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_LE_UINT16((byte *)(scr->_exportTable + i)); + if ((location < scr->_heapSize - 1) && (READ_LE_UINT16(scr->_heapStart + location) == SCRIPT_OBJECT_MAGIC_NUMBER)) { + WRITE_LE_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_LE_UINT16(scr->_heapStart + 2) * 2; + + while (READ_LE_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) { + if (READ_LE_UINT16(seeker + 14) & SCRIPT_INFO_CLASS) { + int classpos = seeker - scr->_buf; + int species = READ_LE_UINT16(seeker + 10); + + 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); + return; + } + + _classtable[species].reg.segment = seg; + _classtable[species].reg.offset = classpos; + } + seeker += READ_LE_UINT16(seeker + 2) * 2; + } + + seeker = scr->_heapStart + 4 + READ_LE_UINT16(scr->_heapStart + 2) * 2; + while (READ_LE_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) { + 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)); + + // 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 + // uses this selector together with -propDict- to compare classes. + // For the purpose of Obj::isKindOf, using the script number appears + // to be sufficient. + obj->setClassScriptSelector(make_reg(0, scr->_nr)); + + seeker += READ_LE_UINT16(seeker + 2) * 2; + } +} + + + int script_instantiate_common(ResourceManager *resMan, SegManager *segMan, int script_nr, Resource **script, Resource **heap, int *was_new) { *was_new = 1; diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp index 430a58fb1f..55084b68ae 100644 --- a/engines/sci/engine/seg_manager.cpp +++ b/engines/sci/engine/seg_manager.cpp @@ -30,21 +30,11 @@ namespace Sci { -/** - * Verify the the given condition is true, output the message if condition is false, and exit. - * @param cond condition to be verified - * @param msg the message to be printed if condition fails - */ -#define VERIFY( cond, msg ) if (!(cond)) {\ - error("%s, line, %d, %s", __FILE__, __LINE__, msg); \ - } - - -#define DEFAULT_SCRIPTS 32 -#define DEFAULT_OBJECTS 8 // default # of objects per script -#define DEFAULT_OBJECTS_INCREMENT 4 // Number of additional objects to instantiate if we're running out of them - -#undef DEBUG_segMan // Define to turn on debugging +enum { + DEFAULT_SCRIPTS = 32, + DEFAULT_OBJECTS = 8, ///< default number of objects per script + DEFAULT_OBJECTS_INCREMENT = 4 ///< Number of additional objects to instantiate if we're running out of them +}; SegManager::SegManager(ResourceManager *resMan) { _heap.push_back(0); @@ -147,42 +137,6 @@ Script *SegManager::allocateScript(int script_nr, SegmentId *segid) { return (Script *)mem; } -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); - - _scriptSize = script->size; - _heapSize = 0; // Set later - - 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; - _heapSize = heap->size; - - // Ensure that the start of the heap resource can be word-aligned. - if (script->size & 2) { - _bufSize++; - _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; - } - } -} - int SegManager::deallocate(SegmentId seg, bool recursive) { SegmentObj *mobj; VERIFY(check(seg), "invalid seg id"); @@ -386,128 +340,6 @@ void SegManager::setExportAreWide(bool flag) { _exportsAreWide = flag; } -int Script::relocateBlock(Common::Array &block, int block_location, SegmentId segment, int location) { - int rel = location - block_location; - - if (rel < 0) - return 0; - - uint idx = rel >> 1; - - if (idx >= block.size()) - return 0; - - if (rel & 1) { - warning("Attempt to relocate odd variable #%d.5e (relative to %04x)\n", idx, block_location); - return 0; - } - block[idx].segment = segment; // Perform relocation - if (getSciVersion() >= SCI_VERSION_1_1) - block[idx].offset += _scriptSize; - - return 1; -} - -int Script::relocateLocal(SegmentId segment, int location) { - if (_localsBlock) - return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location); - 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); -} - -void Script::scriptAddCodeBlock(reg_t location) { - CodeBlock cb; - cb.pos = location; - cb.size = READ_LE_UINT16(_buf + location.offset - 2); - _codeBlocks.push_back(cb); -} - -void Script::scriptRelocate(reg_t block) { - VERIFY(block.offset < (uint16)_bufSize && READ_LE_UINT16(_buf + block.offset) * 2 + block.offset < (uint16)_bufSize, - "Relocation block outside of script\n"); - - int count = READ_LE_UINT16(_buf + block.offset); - - for (int i = 0; i <= count; i++) { - int pos = READ_LE_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; - } - - 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"); - } - } - } -} - -void Script::heapRelocate(reg_t block) { - VERIFY(block.offset < (uint16)_heapSize && READ_LE_UINT16(_heapStart + block.offset) * 2 + block.offset < (uint16)_bufSize, - "Relocation block outside of script\n"); - - if (_relocated) - return; - _relocated = true; - int count = READ_LE_UINT16(_heapStart + block.offset); - - for (int i = 0; i < count; i++) { - int pos = READ_LE_UINT16(_heapStart + block.offset + 2 + (i * 2)) + _scriptSize; - - 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; - } - - 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()); - error("Breakpoint in %s, line %d", __FILE__, __LINE__); - } - } - } -} - // return the seg if script_id is valid and in the map, else 0 SegmentId SegManager::getScriptSegment(int script_id) const { return _scriptSegMap.getVal(script_id, 0); @@ -528,55 +360,6 @@ SegmentId SegManager::getScriptSegment(int script_nr, ScriptLoadType load) { return segment; } -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()); - return NULL_REG; - } else { - Class *the_class = &_classtable[classnr]; - if (!the_class->reg.segment) { - getScriptSegment(the_class->script, lock); - - if (!the_class->reg.segment) { - error("[VM] Trying to instantiate class %x by instantiating script 0x%x (%03d) failed;" - " Entering debugger.", classnr, the_class->script, the_class->script); - return NULL_REG; - } - } else - if (caller.segment != the_class->reg.segment) - getScript(the_class->reg.segment)->incrementLockers(); - - return the_class->reg; - } -} - -Object *Script::scriptObjInit(reg_t obj_pos) { - Object *obj; - - if (getSciVersion() < SCI_VERSION_1_1) - 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"); - - obj->init(_buf, obj_pos); - - return obj; -} - -void Script::scriptObjRemove(reg_t obj_pos) { - if (getSciVersion() < SCI_VERSION_1_1) - obj_pos.offset += 8; - - _objects.erase(obj_pos.toUint16()); -} - LocalVariables *SegManager::allocLocalsSegment(Script *scr, int count) { if (!count) { // No locals scr->_localsSegment = 0; @@ -601,127 +384,6 @@ LocalVariables *SegManager::allocLocalsSegment(Script *scr, int count) { } } -void SegManager::scriptInitialiseLocalsZero(SegmentId seg, int count) { - Script *scr = getScript(seg); - - scr->_localsOffset = -count * 2; // Make sure it's invalid - - allocLocalsSegment(scr, count); -} - -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"); - - if (getSciVersion() >= SCI_VERSION_1_1) - count = READ_LE_UINT16(scr->_buf + location.offset - 2); - else - count = (READ_LE_UINT16(scr->_buf + location.offset - 2) - 4) >> 1; - // half block size - - 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; - } - - LocalVariables *locals = allocLocalsSegment(scr, count); - if (locals) { - uint i; - byte *base = (byte *)(scr->_buf + location.offset); - - for (i = 0; i < count; i++) - locals->_locals[i].offset = READ_LE_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_LE_UINT16((byte *)(scr->_exportTable + i)); - if ((location < scr->_heapSize - 1) && (READ_LE_UINT16(scr->_heapStart + location) == SCRIPT_OBJECT_MAGIC_NUMBER)) { - WRITE_LE_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_LE_UINT16(scr->_heapStart + 2) * 2; - - while (READ_LE_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) { - if (READ_LE_UINT16(seeker + 14) & SCRIPT_INFO_CLASS) { - int classpos = seeker - scr->_buf; - int species = READ_LE_UINT16(seeker + 10); - - 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); - return; - } - - _classtable[species].reg.segment = seg; - _classtable[species].reg.offset = classpos; - } - seeker += READ_LE_UINT16(seeker + 2) * 2; - } - - seeker = scr->_heapStart + 4 + READ_LE_UINT16(scr->_heapStart + 2) * 2; - while (READ_LE_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) { - 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)); - - // 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 - // uses this selector together with -propDict- to compare classes. - // For the purpose of Obj::isKindOf, using the script number appears - // to be sufficient. - obj->setClassScriptSelector(make_reg(0, scr->_nr)); - - seeker += READ_LE_UINT16(seeker + 2) * 2; - } -} - -/* -static char *SegManager::dynprintf(char *msg, ...) { - va_list argp; - char *buf = (char *)malloc(strlen(msg) + 100); - - va_start(argp, msg); - vsprintf(buf, msg, argp); - va_end(argp); - - return buf; -} -*/ - DataStack *SegManager::allocateStack(int size, SegmentId *segid) { SegmentObj *mobj = allocSegment(new DataStack(), segid); DataStack *retval = (DataStack *)mobj; @@ -736,21 +398,6 @@ SystemStrings *SegManager::allocateSysStrings(SegmentId *segid) { return (SystemStrings *)allocSegment(new SystemStrings(), segid); } -uint16 SegManager::validateExportFunc(int pubfunct, SegmentId seg) { - Script *scr = getScript(seg); - if (scr->_numExports <= pubfunct) { - warning("validateExportFunc(): pubfunct is invalid"); - return 0; - } - - if (_exportsAreWide) - pubfunct *= 2; - uint16 offset = READ_LE_UINT16((byte *)(scr->_exportTable + pubfunct)); - VERIFY(offset < scr->_bufSize, "invalid export function pointer"); - - return offset; -} - void SegManager::freeHunkEntry(reg_t addr) { HunkTable *ht = (HunkTable *)GET_SEGMENT(*this, addr.segment, SEG_TYPE_HUNK); @@ -912,7 +559,7 @@ SegmentRef SegManager::dereference(reg_t pointer) { return mobj->dereference(pointer); } -static void *_kernel_dereference_pointer(SegManager *segMan, reg_t pointer, int entries, bool wantRaw) { +static void *derefPtr(SegManager *segMan, reg_t pointer, int entries, bool wantRaw) { SegmentRef ret = segMan->dereference(pointer); if (!ret.isValid()) @@ -942,15 +589,15 @@ static void *_kernel_dereference_pointer(SegManager *segMan, reg_t pointer, int } byte *SegManager::derefBulkPtr(reg_t pointer, int entries) { - return (byte *)_kernel_dereference_pointer(this, pointer, entries, true); + return (byte *)derefPtr(this, pointer, entries, true); } reg_t *SegManager::derefRegPtr(reg_t pointer, int entries) { - return (reg_t *)_kernel_dereference_pointer(this, pointer, 2*entries, false); + return (reg_t *)derefPtr(this, pointer, 2*entries, false); } char *SegManager::derefString(reg_t pointer, int entries) { - return (char *)_kernel_dereference_pointer(this, pointer, entries, true); + return (char *)derefPtr(this, pointer, entries, true); } // Helper functions for getting/setting characters in string fragments @@ -1206,25 +853,6 @@ int SegManager::freeDynmem(reg_t addr) { return 0; // OK } -void SegManager::createClassTable() { - Resource *vocab996 = _resMan->findResource(ResourceId(kResourceTypeVocab, 996), 1); - - if (!vocab996) - error("SegManager: failed to open vocab 996"); - - int totalClasses = vocab996->size >> 2; - _classtable.resize(totalClasses); - - for (uint16 classNr = 0; classNr < totalClasses; classNr++) { - uint16 scriptNr = READ_LE_UINT16(vocab996->data + classNr * 4 + 2); - - _classtable[classNr].reg = NULL_REG; - _classtable[classNr].script = scriptNr; - } - - _resMan->unlockResource(vocab996); -} - #ifdef ENABLE_SCI32 SciArray *SegManager::allocateArray(reg_t *addr) { ArrayTable *table; diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h index 12fb323646..2421faa9a0 100644 --- a/engines/sci/engine/seg_manager.h +++ b/engines/sci/engine/seg_manager.h @@ -35,6 +35,17 @@ namespace Sci { #define GET_SEGMENT(mgr, index, rtype) (((mgr).getSegmentType(index) == (rtype))? (mgr)._heap[index] : NULL) +/** + * Verify the the given condition is true, output the message if condition is false, and exit. + * @param cond condition to be verified + * @param msg the message to be printed if condition fails + */ +#define VERIFY( cond, msg ) if (!(cond)) {\ + error("%s, line, %d, %s", __FILE__, __LINE__, msg); \ + } + + + /** * Parameters for getScriptSegment(). */ @@ -488,4 +499,4 @@ private: } // End of namespace Sci -#endif // SCI_ENGINE_SEGMAN +#endif // SCI_ENGINE_SEGMAN_H diff --git a/engines/sci/engine/segment.cpp b/engines/sci/engine/segment.cpp index 2fee80048b..5419de6f0b 100644 --- a/engines/sci/engine/segment.cpp +++ b/engines/sci/engine/segment.cpp @@ -121,9 +121,6 @@ bool Script::init(int script_nr, ResourceManager *resMan) { _buf = (byte *)malloc(_bufSize); -#ifdef DEBUG_segMan - printf("_buf = %p ", _buf); -#endif if (!_buf) { freeScript(); warning("Not enough memory space for script size"); @@ -149,6 +146,42 @@ bool Script::init(int script_nr, ResourceManager *resMan) { 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); + + _scriptSize = script->size; + _heapSize = 0; // Set later + + 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; + _heapSize = heap->size; + + // Ensure that the start of the heap resource can be word-aligned. + if (script->size & 2) { + _bufSize++; + _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; + } + } +} + Object *Script::allocateObject(uint16 offset) { return &_objects[offset]; } @@ -160,6 +193,152 @@ Object *Script::getObject(uint16 offset) { return 0; } +Object *Script::scriptObjInit(reg_t obj_pos) { + Object *obj; + + if (getSciVersion() < SCI_VERSION_1_1) + 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"); + + obj->init(_buf, obj_pos); + + return obj; +} + +void Script::scriptObjRemove(reg_t obj_pos) { + if (getSciVersion() < SCI_VERSION_1_1) + obj_pos.offset += 8; + + _objects.erase(obj_pos.toUint16()); +} + +int Script::relocateBlock(Common::Array &block, int block_location, SegmentId segment, int location) { + int rel = location - block_location; + + if (rel < 0) + return 0; + + uint idx = rel >> 1; + + if (idx >= block.size()) + return 0; + + if (rel & 1) { + warning("Attempt to relocate odd variable #%d.5e (relative to %04x)\n", idx, block_location); + return 0; + } + block[idx].segment = segment; // Perform relocation + if (getSciVersion() >= SCI_VERSION_1_1) + block[idx].offset += _scriptSize; + + return 1; +} + +int Script::relocateLocal(SegmentId segment, int location) { + if (_localsBlock) + return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location); + 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); +} + +void Script::scriptAddCodeBlock(reg_t location) { + CodeBlock cb; + cb.pos = location; + cb.size = READ_LE_UINT16(_buf + location.offset - 2); + _codeBlocks.push_back(cb); +} + +void Script::scriptRelocate(reg_t block) { + VERIFY(block.offset < (uint16)_bufSize && READ_LE_UINT16(_buf + block.offset) * 2 + block.offset < (uint16)_bufSize, + "Relocation block outside of script\n"); + + int count = READ_LE_UINT16(_buf + block.offset); + + for (int i = 0; i <= count; i++) { + int pos = READ_LE_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; + } + + 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"); + } + } + } +} + +void Script::heapRelocate(reg_t block) { + VERIFY(block.offset < (uint16)_heapSize && READ_LE_UINT16(_heapStart + block.offset) * 2 + block.offset < (uint16)_bufSize, + "Relocation block outside of script\n"); + + if (_relocated) + return; + _relocated = true; + int count = READ_LE_UINT16(_heapStart + block.offset); + + for (int i = 0; i < count; i++) { + int pos = READ_LE_UINT16(_heapStart + block.offset + 2 + (i * 2)) + _scriptSize; + + 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; + } + + 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()); + error("Breakpoint in %s, line %d", __FILE__, __LINE__); + } + } + } +} + void Script::incrementLockers() { _lockers++; } @@ -187,6 +366,26 @@ void Script::setExportTableOffset(int offset) { } } +// TODO: This method should be Script method. The only reason +// that it isn't is that it uses _exportsAreWide, which is true if +// detectLofsType() == SCI_VERSION_1_MIDDLE +// Maybe _exportsAreWide should become a Script member var, e.g. set +// by setExportTableOffset? +uint16 SegManager::validateExportFunc(int pubfunct, SegmentId seg) { + Script *scr = getScript(seg); + if (scr->_numExports <= pubfunct) { + warning("validateExportFunc(): pubfunct is invalid"); + return 0; + } + + if (_exportsAreWide) + pubfunct *= 2; + uint16 offset = READ_LE_UINT16((byte *)(scr->_exportTable + pubfunct)); + VERIFY(offset < scr->_bufSize, "invalid export function pointer"); + + return offset; +} + void Script::setSynonymsOffset(int offset) { _synonyms = _buf + offset; } -- cgit v1.2.3