aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/engine
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/engine')
-rw-r--r--engines/sci/engine/kscripts.cpp6
-rw-r--r--engines/sci/engine/object.cpp6
-rw-r--r--engines/sci/engine/object.h4
-rw-r--r--engines/sci/engine/script.cpp144
-rw-r--r--engines/sci/engine/script.h33
-rw-r--r--engines/sci/engine/vm.cpp2
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: