diff options
Diffstat (limited to 'engines/sci')
-rw-r--r-- | engines/sci/engine/kscripts.cpp | 6 | ||||
-rw-r--r-- | engines/sci/engine/object.cpp | 6 | ||||
-rw-r--r-- | engines/sci/engine/object.h | 4 | ||||
-rw-r--r-- | engines/sci/engine/script.cpp | 144 | ||||
-rw-r--r-- | engines/sci/engine/script.h | 33 | ||||
-rw-r--r-- | engines/sci/engine/vm.cpp | 2 |
6 files changed, 137 insertions, 58 deletions
diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp index af4b8ff081..75f905e81a 100644 --- a/engines/sci/engine/kscripts.cpp +++ b/engines/sci/engine/kscripts.cpp @@ -246,11 +246,7 @@ reg_t kScriptID(EngineState *s, int argc, reg_t *argv) { return NULL_REG; } - uint32 address = scr->validateExportFunc(index, true); - - // Point to the heap for SCI1.1 - SCI2.1 games - if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) - address += scr->getScriptSize(); + const uint32 address = scr->validateExportFunc(index, true) + scr->getHeapOffset(); // Bugfix for the intro speed in PQ2 version 1.002.011. // This is taken from the patch by NewRisingSun(NRS) / Belzorash. Global 3 diff --git a/engines/sci/engine/object.cpp b/engines/sci/engine/object.cpp index 386c3e26bf..640feb1418 100644 --- a/engines/sci/engine/object.cpp +++ b/engines/sci/engine/object.cpp @@ -123,12 +123,12 @@ int Object::locateVarSelector(SegManager *segMan, Selector slc) const { return -1; // Failed } -bool Object::relocateSci0Sci21(SegmentId segment, int location, size_t scriptSize) { - return relocateBlock(_variables, getPos().getOffset(), segment, location, scriptSize); +bool Object::relocateSci0Sci21(SegmentId segment, int location, uint32 heapOffset) { + return relocateBlock(_variables, getPos().getOffset(), segment, location, heapOffset); } #ifdef ENABLE_SCI32 -bool Object::relocateSci3(SegmentId segment, uint32 location, int offset, size_t scriptSize) { +bool Object::relocateSci3(SegmentId segment, uint32 location, int offset, uint32 scriptSize) { assert(offset >= 0 && (uint)offset < scriptSize); for (uint i = 0; i < _variables.size(); ++i) { diff --git a/engines/sci/engine/object.h b/engines/sci/engine/object.h index 71b366dc79..be20e3bef4 100644 --- a/engines/sci/engine/object.h +++ b/engines/sci/engine/object.h @@ -299,9 +299,9 @@ public: #endif } - bool relocateSci0Sci21(SegmentId segment, int location, size_t scriptSize); + bool relocateSci0Sci21(SegmentId segment, int location, uint32 heapOffset); #ifdef ENABLE_SCI32 - bool relocateSci3(SegmentId segment, uint32 location, int offset, size_t scriptSize); + bool relocateSci3(SegmentId segment, uint32 location, int offset, uint32 scriptSize); #endif int propertyOffsetToId(SegManager *segMan, int propertyOffset) const; diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index 2f04534343..ea1b6f2f5f 100644 --- a/engines/sci/engine/script.cpp +++ b/engines/sci/engine/script.cpp @@ -183,7 +183,7 @@ void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptP _exports = _buf->subspan<const uint16>(kSci11ExportTableOffset, _numExports * sizeof(uint16)); } - _localsOffset = _script.size() + 4; + _localsOffset = getHeapOffset() + 4; _localsCount = _buf->getUint16SEAt(_localsOffset - 2); } else if (getSciVersion() == SCI_VERSION_3) { _localsCount = _buf->getUint16LEAt(12); @@ -676,10 +676,9 @@ bool relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId se error("Attempt to relocate odd variable #%d.5e (relative to %04x)\n", idx, block_location); return false; } - block[idx].setSegment(segment); // Perform relocation - if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) - block[idx].incOffset(scriptSize); + block[idx].setSegment(segment); + block[idx].incOffset(heapOffset); return true; } @@ -702,60 +701,123 @@ int Script::relocateOffsetSci3(uint32 offset) const { bool Script::relocateLocal(SegmentId segment, int location) { if (_localsBlock) - return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location, _script.size()); + return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location, getHeapOffset()); else return false; } -void Script::relocateSci0Sci21(reg_t block) { - SciSpan<const byte> heap = *_buf; - uint16 heapOffset = 0; +uint32 Script::getRelocationOffset(const uint32 offset) const { + if (getSciVersion() == SCI_VERSION_3) { + SciSpan<const byte> relocStart = _buf->subspan(_buf->getUint32SEAt(8)); + const uint relocCount = _buf->getUint16SEAt(18); - if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) { - heap = _heap; - heapOffset = _script.size(); + for (uint i = 0; i < relocCount; ++i) { + if (offset == relocStart.getUint32SEAt(0)) { + return relocStart.getUint32SEAt(4); + } + relocStart += 10; + } + } else { + const SciSpan<const uint16> relocTable = getRelocationTableSci0Sci21(); + for (uint i = 0; i < relocTable.size(); ++i) { + if (relocTable.getUint16SEAt(i) == offset) { + return getHeapOffset(); + } + } } - if (block.getOffset() >= (uint16)heap.size() || - heap.getUint16SEAt(block.getOffset()) * 2 + block.getOffset() >= (uint16)heap.size()) - error("Relocation block outside of script"); - - int count = heap.getUint16SEAt(block.getOffset()); - int exportIndex = 0; - int pos = 0; - - for (int i = 0; i < count; i++) { - pos = heap.getUint16SEAt(block.getOffset() + 2 + (exportIndex * 2)) + heapOffset; - // This occurs in SCI01/SCI1 games where 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 = heap.getUint16SEAt(block.getOffset() + 2 + (exportIndex * 2)) + heapOffset; - if (!pos) - error("Script::relocate(): Consecutive zero exports found"); + return kNoRelocation; +} + +const SciSpan<const uint16> Script::getRelocationTableSci0Sci21() const { + SciSpan<const byte> relocationBlock; + uint16 numEntries; + uint16 dataOffset; + + if (getSciVersion() < SCI_VERSION_1_1) { + relocationBlock = findBlockSCI0(SCI_OBJ_POINTERS); + + if (!relocationBlock) { + return SciSpan<const uint16>(); + } + + if (relocationBlock != findBlockSCI0(SCI_OBJ_POINTERS, true)) { + warning("script.%u has multiple relocation tables", _nr); } + numEntries = relocationBlock.getUint16SEAt(4); + + if (!numEntries) { + return SciSpan<const uint16>(); + } + + dataOffset = 6; + + // Starting somewhere around SQ1, and continuing through the rest of + // SCI1, the relocation table in scripts started including an extra + // null entry at the beginning of the table, without a corresponding + // increase in the entry count. While this change is consistent in + // most of the SCI1mid+ games (all scripts in LSL1, Jones CD, + // EQ floppy, SQ1, LSL5, and Ms Astro Chicken have the null entry), + // a few games include scripts without the null entry (Castle of Dr + // Brain 947 & 997, PQ3 997, KQ5 CD 975 & 997). Since 0 is never a + // valid relocation offset, we just skip it if we see it + const uint16 firstEntry = relocationBlock.getUint16SEAt(6); + if (firstEntry == 0) { + dataOffset += 2; + } + } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) { + relocationBlock = _heap.subspan(_heap.getUint16SEAt(0)); + + if (!relocationBlock) { + return SciSpan<const uint16>(); + } + + numEntries = relocationBlock.getUint16SEAt(0); + + if (!numEntries) { + return SciSpan<const uint16>(); + } + + dataOffset = 2; + } else { + error("Invalid engine version called Script::getRelocationTableSci0Sci21"); + } + + // This check should work correctly even with SCI1.1+ because the relocation + // table is always at the very end of the heap in these games + if (dataOffset + numEntries * sizeof(uint16) != relocationBlock.size()) { + warning("script.%u unexpected relocation table size %u", _nr, relocationBlock.size()); + } + + return relocationBlock.subspan<const uint16>(dataOffset, numEntries * sizeof(uint16)); +} + +void Script::relocateSci0Sci21(const SegmentId segmentId) { + const SciSpan<const uint16> relocEntries = getRelocationTableSci0Sci21(); + + const uint32 heapOffset = getHeapOffset(); + + for (uint i = 0; i < relocEntries.size(); ++i) { + const uint pos = relocEntries.getUint16SEAt(i) + heapOffset; + // In SCI0-SCI1, script local variables, objects and code are relocated. // We only relocate locals and objects here, and ignore relocation of // code blocks. In SCI1.1 and newer versions, only locals and objects // are relocated. - if (!relocateLocal(block.getSegment(), pos)) { + if (!relocateLocal(segmentId, pos)) { // Not a local? It's probably an object or code block. If it's an // object, relocate it. const ObjMap::iterator end = _objects.end(); for (ObjMap::iterator it = _objects.begin(); it != end; ++it) - if (it->_value.relocateSci0Sci21(block.getSegment(), pos, _script.size())) + if (it->_value.relocateSci0Sci21(segmentId, pos, getHeapOffset())) break; } - - exportIndex++; } } #ifdef ENABLE_SCI32 -void Script::relocateSci3(reg_t block) { +void Script::relocateSci3(const SegmentId segmentId) { SciSpan<const byte> relocStart = _buf->subspan(_buf->getUint32SEAt(8)); const uint relocCount = _buf->getUint16SEAt(18); @@ -763,7 +825,7 @@ void Script::relocateSci3(reg_t block) { for (it = _objects.begin(); it != _objects.end(); ++it) { SciSpan<const byte> seeker = relocStart; for (uint i = 0; i < relocCount; ++i) { - it->_value.relocateSci3(block.getSegment(), + it->_value.relocateSci3(segmentId, seeker.getUint32SEAt(0), seeker.getUint32SEAt(4), _script.size()); @@ -831,7 +893,7 @@ uint32 Script::validateExportFunc(int pubfunct, bool relocSci3) { return offset; } -SciSpan<const byte> Script::findBlockSCI0(ScriptObjectTypes type, bool findLastBlock) { +SciSpan<const byte> Script::findBlockSCI0(ScriptObjectTypes type, bool findLastBlock) const { SciSpan<const byte> foundBlock; bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); @@ -1054,9 +1116,7 @@ void Script::initializeObjectsSci0(SegManager *segMan, SegmentId segmentId) { } while ((uint32)(seeker - *_buf) < getScriptSize() - 2); } - const SciSpan<const byte> relocationBlock = findBlockSCI0(SCI_OBJ_POINTERS); - if (relocationBlock) - relocateSci0Sci21(make_reg(segmentId, relocationBlock - *_buf + 4)); + relocateSci0Sci21(segmentId); } void Script::initializeObjectsSci11(SegManager *segMan, SegmentId segmentId) { @@ -1108,7 +1168,7 @@ void Script::initializeObjectsSci11(SegManager *segMan, SegmentId segmentId) { seeker += seeker.getUint16SEAt(2) * 2; } - relocateSci0Sci21(make_reg(segmentId, _heap.getUint16SEAt(0))); + relocateSci0Sci21(segmentId); for (uint i = 0; i < mismatchedVarCountObjects.size(); ++i) { const reg_t pos = mismatchedVarCountObjects[i]; @@ -1143,7 +1203,7 @@ void Script::initializeObjectsSci3(SegManager *segMan, SegmentId segmentId) { seeker += seeker.getUint16SEAt(2); } - relocateSci3(make_reg(segmentId, 0)); + relocateSci3(segmentId); } #endif diff --git a/engines/sci/engine/script.h b/engines/sci/engine/script.h index 65f3ffb6ab..b59f87f13a 100644 --- a/engines/sci/engine/script.h +++ b/engines/sci/engine/script.h @@ -56,6 +56,10 @@ enum ScriptOffsetEntryTypes { SCI_SCR_OFFSET_TYPE_SAID }; +enum { + kNoRelocation = 0xFFFFFFFF +}; + struct offsetLookupArrayEntry { uint16 type; // type of entry uint16 id; // id of this type, first item inside script data is 1, second item is 2, etc. @@ -105,6 +109,13 @@ public: uint32 getScriptSize() const { return _script.size(); } uint32 getHeapSize() const { return _heap.size(); } uint32 getBufSize() const { return _buf->size(); } + inline uint32 getHeapOffset() const { + if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) { + return _script.size(); + } + + return 0; + } const byte *getBuf(uint offset = 0) const { return _buf->getUnsafeDataAt(offset); } SciSpan<const byte> getSpan(uint offset) const { return _buf->subspan(offset); } @@ -247,7 +258,7 @@ public: * Finds the pointer where a block of a specific type starts from, * in SCI0 - SCI1 games */ - SciSpan<const byte> findBlockSCI0(ScriptObjectTypes type, bool findLastBlock = false); + SciSpan<const byte> findBlockSCI0(ScriptObjectTypes type, bool findLastBlock = false) const; /** * Syncs the string heap of a script. Used when saving/loading. @@ -276,23 +287,35 @@ public: uint16 getOffsetStringCount() { return _offsetLookupStringCount; }; uint16 getOffsetSaidCount() { return _offsetLookupSaidCount; }; + /** + * @returns kNoRelocation if no relocation exists for the given offset, + * otherwise returns a delta for the offset to its relocated position. + */ + uint32 getRelocationOffset(const uint32 offset) const; + private: /** + * Returns a Span containing the relocation table for a SCI0-SCI2.1 script. + * (The SCI0-SCI2.1 relocation table is simply a list of all of the + * offsets in the script heap whose values should be treated as pointers to + * objects (vs just being numbers).) + */ + const SciSpan<const uint16> getRelocationTableSci0Sci21() const; + + /** * Processes a relocation block within a SCI0-SCI2.1 script * This function is idempotent, but it must only be called after all * objects have been instantiated, or a run-time error will occur. - * @param obj_pos Location (segment, offset) of the block */ - void relocateSci0Sci21(reg_t block); + void relocateSci0Sci21(const SegmentId segmentId); #ifdef ENABLE_SCI32 /** * Processes a relocation block within a SCI3 script * This function is idempotent, but it must only be called after all * objects have been instantiated, or a run-time error will occur. - * @param obj_pos Location (segment, offset) of the block */ - void relocateSci3(reg_t block); + void relocateSci3(const SegmentId segmentId); #endif bool relocateLocal(SegmentId segment, int location); diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index 1d86948fdd..f4071a4843 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -570,7 +570,7 @@ uint32 findOffset(const int16 relOffset, const Script *scr, const uint32 pcOffse offset = relOffset; break; case SCI_VERSION_1_1: - offset = relOffset + scr->getScriptSize(); + offset = relOffset + scr->getHeapOffset(); break; #ifdef ENABLE_SCI32 case SCI_VERSION_3: |