diff options
Diffstat (limited to 'engines/sci/engine/script.cpp')
-rw-r--r-- | engines/sci/engine/script.cpp | 397 |
1 files changed, 397 insertions, 0 deletions
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index 88becc82cc..6034378ef6 100644 --- a/engines/sci/engine/script.cpp +++ b/engines/sci/engine/script.cpp @@ -20,6 +20,7 @@ * */ +#include "sci/console.h" #include "sci/sci.h" #include "sci/resource.h" #include "sci/util.h" @@ -64,6 +65,11 @@ void Script::freeScript() { _lockers = 1; _markedAsDeleted = false; _objects.clear(); + + _offsetLookupArray.clear(); + _offsetLookupObjectCount = 0; + _offsetLookupStringCount = 0; + _offsetLookupSaidCount = 0; } void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptPatcher) { @@ -204,6 +210,397 @@ void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptP //_localsCount = (_bufSize - _localsOffset) >> 1; } } + + // find all strings of this script + identifyOffsets(); +} + +void Script::identifyOffsets() { + offsetLookupArrayEntry arrayEntry; + const byte *scriptDataPtr = NULL; + const byte *stringStartPtr = NULL; + const byte *stringDataPtr = NULL; + uint32 scriptDataLeft = 0; + uint32 stringDataLeft = 0; + byte stringDataByte = 0; + uint16 typeObject_id = 0; + uint16 typeString_id = 0; + uint16 typeSaid_id = 0; + + uint16 blockType = 0; + uint16 blockSize = 0; + + _offsetLookupArray.clear(); + _offsetLookupObjectCount = 0; + _offsetLookupStringCount = 0; + _offsetLookupSaidCount = 0; + + if (getSciVersion() < SCI_VERSION_1_1) { + // SCI0 + SCI1 + scriptDataPtr = _buf; + scriptDataLeft = _bufSize; + + // Go through all blocks + if (getSciVersion() == SCI_VERSION_0_EARLY) { + if (scriptDataLeft < 2) + error("Script::identifyOffsets(): unexpected end of script %d", _nr); + scriptDataPtr += 2; + scriptDataLeft -= 2; + } + + do { + if (scriptDataLeft < 2) + error("Script::identifyOffsets(): unexpected end of script %d", _nr); + + blockType = READ_LE_UINT16(scriptDataPtr); + scriptDataPtr += 2; + scriptDataLeft -= 2; + if (blockType == 0) // end of blocks detected + break; + + if (scriptDataLeft < 2) + error("Script::identifyOffsets(): unexpected end of script %d", _nr); + + blockSize = READ_LE_UINT16(scriptDataPtr); + if (blockSize < 4) + error("Script::identifyOffsets(): invalid block size in script %d", _nr); + blockSize -= 4; // block size includes block-type UINT16 and block-size UINT16 + scriptDataPtr += 2; + scriptDataLeft -= 2; + + if (scriptDataLeft < blockSize) + error("Script::identifyOffsets(): invalid block size in script %d", _nr); + + switch (blockType) { + case SCI_OBJ_OBJECT: + case SCI_OBJ_CLASS: + typeObject_id++; + arrayEntry.type = SCI_SCR_OFFSET_TYPE_OBJECT; + arrayEntry.id = typeObject_id; + arrayEntry.offset = scriptDataPtr - _buf + 8; // Calculate offset inside script data (VM uses +8) + arrayEntry.stringSize = 0; + _offsetLookupArray.push_back(arrayEntry); + _offsetLookupObjectCount++; + break; + + case SCI_OBJ_STRINGS: + // string block detected, we now grab all NUL terminated strings out of this block + stringDataPtr = scriptDataPtr; + stringDataLeft = blockSize; + + arrayEntry.type = SCI_SCR_OFFSET_TYPE_STRING; + + do { + if (stringDataLeft < 1) // no more bytes left + break; + + stringStartPtr = stringDataPtr; + + if (stringDataLeft == 1) { + // only 1 byte left and that byte is a [00], in that case we also exit + stringDataByte = *stringStartPtr; + if (stringDataByte == 0x00) + break; + } + + // now look for terminating [NUL] + do { + stringDataByte = *stringDataPtr; + stringDataPtr++; + stringDataLeft--; + if (!stringDataByte) // NUL found, exit this loop + break; + if (stringDataLeft < 1) { + // no more bytes left + warning("Script::identifyOffsets(): string without terminating NUL in script %d", _nr); + break; + } + } while (1); + + if (stringDataByte) + break; + + typeString_id++; + arrayEntry.id = typeString_id; + arrayEntry.offset = stringStartPtr - _buf; // Calculate offset inside script data + arrayEntry.stringSize = stringDataPtr - stringStartPtr; + _offsetLookupArray.push_back(arrayEntry); + _offsetLookupStringCount++; + } while (1); + break; + + case SCI_OBJ_SAID: + // said block detected, we now try to find every single said "string" inside this block + // said strings are terminated with a 0xFF, the string itself may contain words (2 bytes), where + // the second byte of a word may also be a 0xFF. + stringDataPtr = scriptDataPtr; + stringDataLeft = blockSize; + + arrayEntry.type = SCI_SCR_OFFSET_TYPE_SAID; + + do { + if (stringDataLeft < 1) // no more bytes left + break; + + stringStartPtr = stringDataPtr; + if (stringDataLeft == 1) { + // only 1 byte left and that byte is a [00], in that case we also exit + // happens in some scripts, for example Conquests of Camelot, script 997 + // may have been a bug in the compiler or just an intentional filler byte + stringDataByte = *stringStartPtr; + if (stringDataByte == 0x00) + break; + } + + // now look for terminating 0xFF + do { + stringDataByte = *stringDataPtr; + stringDataPtr++; + stringDataLeft--; + if (stringDataByte == 0xFF) // Terminator found, exit this loop + break; + if (stringDataLeft < 1) // no more bytes left + error("Script::identifyOffsets(): said-string without terminator in script %d", _nr); + if (stringDataByte < 0xF0) { + // Part of a word, skip second byte + stringDataPtr++; + stringDataLeft--; + if (stringDataLeft < 1) // no more bytes left + error("Script::identifyOffsets(): said-string without terminator in script %d", _nr); + } + } while (1); + + typeSaid_id++; + arrayEntry.id = typeSaid_id; + arrayEntry.offset = stringStartPtr - _buf; // Calculate offset inside script data + arrayEntry.stringSize = 0; + _offsetLookupArray.push_back(arrayEntry); + _offsetLookupSaidCount++; + } while (1); + break; + + default: + break; + } + + scriptDataPtr += blockSize; + scriptDataLeft -= blockSize; + } while (1); + + } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) { + // Strings in SCI1.1 up to SCI2 come after the object instances + scriptDataPtr = _heapStart; + scriptDataLeft = _heapSize; + + if (scriptDataLeft < 4) + error("Script::identifyOffsets(): unexpected end of script in script %d", _nr); + + uint16 endOfStringOffset = READ_SCI11ENDIAN_UINT16(scriptDataPtr); + uint16 objectStartOffset = READ_SCI11ENDIAN_UINT16(scriptDataPtr + 2) * 2 + 4; + + if (scriptDataLeft < objectStartOffset) + error("Script::identifyOffsets(): object start is beyond heap size in script %d", _nr); + if (scriptDataLeft < endOfStringOffset) + error("Script::identifyOffsets(): end of string is beyond heap size in script %d", _nr); + + const byte *endOfStringPtr = scriptDataPtr + endOfStringOffset; + + scriptDataPtr += objectStartOffset; + scriptDataLeft -= objectStartOffset; + + // go through all objects + do { + if (scriptDataLeft < 2) + error("Script::identifyOffsets(): unexpected end of script %d", _nr); + + blockType = READ_SCI11ENDIAN_UINT16(scriptDataPtr); + scriptDataPtr += 2; + scriptDataLeft -= 2; + if (blockType != SCRIPT_OBJECT_MAGIC_NUMBER) + break; + + // Object found, add offset of object + typeObject_id++; + arrayEntry.type = SCI_SCR_OFFSET_TYPE_OBJECT; + arrayEntry.id = typeObject_id; + arrayEntry.offset = scriptDataPtr - _buf - 2; // the VM uses a pointer to the Magic-Number + arrayEntry.stringSize = 0; + _offsetLookupArray.push_back(arrayEntry); + _offsetLookupObjectCount++; + + if (scriptDataLeft < 2) + error("Script::identifyOffsets(): unexpected end of script in script %d", _nr); + + blockSize = READ_SCI11ENDIAN_UINT16(scriptDataPtr) * 2; + if (blockSize < 4) + error("Script::identifyOffsets(): invalid block size in script %d", _nr); + scriptDataPtr += 2; + scriptDataLeft -= 2; + blockSize -= 4; // blocksize contains UINT16 type and UINT16 size + if (scriptDataLeft < blockSize) + error("Script::identifyOffsets(): invalid block size in script %d", _nr); + + scriptDataPtr += blockSize; + scriptDataLeft -= blockSize; + } while (1); + + // now scriptDataPtr points to right at the start of the strings + if (scriptDataPtr > endOfStringPtr) + error("Script::identifyOffsets(): string block / end-of-string block mismatch in script %d", _nr); + + stringDataPtr = scriptDataPtr; + stringDataLeft = endOfStringPtr - scriptDataPtr; // Calculate byte count within string-block + + arrayEntry.type = SCI_SCR_OFFSET_TYPE_STRING; + do { + if (stringDataLeft < 1) // no more bytes left + break; + + stringStartPtr = stringDataPtr; + // now look for terminating [NUL] + do { + stringDataByte = *stringDataPtr; + stringDataPtr++; + stringDataLeft--; + if (!stringDataByte) // NUL found, exit this loop + break; + if (stringDataLeft < 1) { + // no more bytes left + warning("Script::identifyOffsets(): string without terminating NUL in script %d", _nr); + break; + } + } while (1); + + if (stringDataByte) + break; + + typeString_id++; + arrayEntry.id = typeString_id; + arrayEntry.offset = stringStartPtr - _buf; // Calculate offset inside script data + arrayEntry.stringSize = stringDataPtr - stringStartPtr; + _offsetLookupArray.push_back(arrayEntry); + _offsetLookupStringCount++; + } while (1); + + } else if (getSciVersion() == SCI_VERSION_3) { + // SCI3 + uint32 sci3StringOffset = 0; + uint32 sci3RelocationOffset = 0; + uint32 sci3BoundaryOffset = 0; + + if (_bufSize < 22) + error("Script::identifyOffsets(): script %d smaller than expected SCI3-header", _nr); + + sci3StringOffset = READ_LE_UINT32(_buf + 4); + sci3RelocationOffset = READ_LE_UINT32(_buf + 8); + + if (sci3RelocationOffset > _bufSize) + error("Script::identifyOffsets(): relocation offset is beyond end of script %d", _nr); + + // First we get all the objects + scriptDataPtr = getSci3ObjectsPointer(); + scriptDataLeft = _bufSize - (scriptDataPtr - _buf); + do { + if (scriptDataLeft < 2) + error("Script::identifyOffsets(): unexpected end of script %d", _nr); + + blockType = READ_SCI11ENDIAN_UINT16(scriptDataPtr); + scriptDataPtr += 2; + scriptDataLeft -= 2; + if (blockType != SCRIPT_OBJECT_MAGIC_NUMBER) + break; + + // Object found, add offset of object + typeObject_id++; + arrayEntry.type = SCI_SCR_OFFSET_TYPE_OBJECT; + arrayEntry.id = typeObject_id; + arrayEntry.offset = scriptDataPtr - _buf - 2; // the VM uses a pointer to the Magic-Number + arrayEntry.stringSize = 0; + _offsetLookupArray.push_back(arrayEntry); + _offsetLookupObjectCount++; + + if (scriptDataLeft < 2) + error("Script::identifyOffsets(): unexpected end of script in script %d", _nr); + + blockSize = READ_SCI11ENDIAN_UINT16(scriptDataPtr); + if (blockSize < 4) + error("Script::identifyOffsets(): invalid block size in script %d", _nr); + scriptDataPtr += 2; + scriptDataLeft -= 2; + blockSize -= 4; // blocksize contains UINT16 type and UINT16 size + if (scriptDataLeft < blockSize) + error("Script::identifyOffsets(): invalid block size in script %d", _nr); + + scriptDataPtr += blockSize; + scriptDataLeft -= blockSize; + } while (1); + + // And now we get all the strings + if (sci3StringOffset > 0) { + // string offset set, we expect strings + if (sci3StringOffset > _bufSize) + error("Script::identifyOffsets(): string offset is beyond end of script %d", _nr); + + if (sci3RelocationOffset < sci3StringOffset) + error("Script::identifyOffsets(): string offset points beyond relocation offset in script %d", _nr); + + stringDataPtr = _buf + sci3StringOffset; + stringDataLeft = sci3RelocationOffset - sci3StringOffset; + + arrayEntry.type = SCI_SCR_OFFSET_TYPE_STRING; + + do { + if (stringDataLeft < 1) // no more bytes left + break; + + stringStartPtr = stringDataPtr; + + if (stringDataLeft == 1) { + // only 1 byte left and that byte is a [00], in that case we also exit + stringDataByte = *stringStartPtr; + if (stringDataByte == 0x00) + break; + } + + // now look for terminating [NUL] + do { + stringDataByte = *stringDataPtr; + stringDataPtr++; + stringDataLeft--; + if (!stringDataByte) // NUL found, exit this loop + break; + if (stringDataLeft < 1) { + // no more bytes left + warning("Script::identifyOffsets(): string without terminating NUL in script %d", _nr); + break; + } + } while (1); + + if (stringDataByte) + break; + + typeString_id++; + arrayEntry.id = typeString_id; + arrayEntry.offset = stringStartPtr - _buf; // Calculate offset inside script data + arrayEntry.stringSize = stringDataPtr - stringStartPtr; + _offsetLookupArray.push_back(arrayEntry); + _offsetLookupStringCount++; + + // SCI3 seems to have aligned all string on DWORD boundaries + sci3BoundaryOffset = stringDataPtr - _buf; // Calculate current offset inside script data + sci3BoundaryOffset = sci3BoundaryOffset & 3; // Check boundary offset + if (sci3BoundaryOffset) { + // lower 2 bits are set? Then we have to adjust the offset + sci3BoundaryOffset = 4 - sci3BoundaryOffset; + if (stringDataLeft < sci3BoundaryOffset) + error("Script::identifyOffsets(): SCI3 string boundary adjustment goes beyond end of string block in script %d", _nr); + stringDataLeft -= sci3BoundaryOffset; + stringDataPtr += sci3BoundaryOffset; + } + } while (1); + } + return; + } } const byte *Script::getSci3ObjectsPointer() { |