diff options
-rw-r--r-- | engines/sci/console.cpp | 67 | ||||
-rw-r--r-- | engines/sci/console.h | 1 | ||||
-rw-r--r-- | engines/sci/engine/script.cpp | 187 | ||||
-rw-r--r-- | engines/sci/engine/script.h | 19 |
4 files changed, 274 insertions, 0 deletions
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 3f5548aac7..bc9ad362ab 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -209,6 +209,8 @@ Console::Console(SciEngine *engine) : GUI::Debugger(), registerCmd("bpe", WRAP_METHOD(Console, cmdBreakpointFunction)); // alias // VM registerCmd("script_steps", WRAP_METHOD(Console, cmdScriptSteps)); + registerCmd("script_strings", WRAP_METHOD(Console, cmdScriptStrings)); + registerCmd("scrs", WRAP_METHOD(Console, cmdScriptStrings)); registerCmd("vm_varlist", WRAP_METHOD(Console, cmdVMVarlist)); registerCmd("vmvarlist", WRAP_METHOD(Console, cmdVMVarlist)); // alias registerCmd("vl", WRAP_METHOD(Console, cmdVMVarlist)); // alias @@ -2828,6 +2830,71 @@ bool Console::cmdScriptSteps(int argc, const char **argv) { return true; } +bool Console::cmdScriptStrings(int argc, const char **argv) { + SegManager *segMan = _engine->_gamestate->_segMan; + int curScriptNr = -1; + SegmentId curSegmentNr; + Common::List<SegmentId> segmentNrList; + + SegmentType curSegmentType = SEG_TYPE_INVALID; + SegmentObj *curSegmentObj = NULL; + Script *curScriptObj = NULL; + + if (argc < 2) { + debugPrintf("Shows the strings inside a specified script.\n"); + debugPrintf("Usage: %s <script number>\n", argv[0]); + debugPrintf("Example: %s 999\n", argv[0]); + debugPrintf("<script number> may be * to show strings inside all loaded scripts\n"); + return true; + } + + segmentNrList.clear(); + + if (strcmp(argv[1], "*") == 0) { + // get strings of all currently loaded scripts + for (curSegmentNr = 0; curSegmentNr < segMan->_heap.size(); curSegmentNr++) { + curSegmentObj = segMan->_heap[curSegmentNr]; + if (curSegmentObj && curSegmentObj->getType() == SEG_TYPE_SCRIPT) { + segmentNrList.push_back(curSegmentNr); + } + } + + } else { + curScriptNr = atoi(argv[1]); + curSegmentNr = segMan->getScriptSegment(curScriptNr); + if (!curSegmentNr) { + debugPrintf("Script %d is currently not loaded/available\n", curScriptNr); + return true; + } + segmentNrList.push_back(curSegmentNr); + } + + Common::List<SegmentId>::iterator it; + const Common::List<SegmentId>::iterator end = segmentNrList.end(); + + for (it = segmentNrList.begin(); it != end; it++) { + curSegmentNr = *it; + // get object of this segment + curSegmentObj = segMan->getSegmentObj(curSegmentNr); + if (!curSegmentObj) + continue; + + curSegmentType = curSegmentObj->getType(); + if (curSegmentType != SEG_TYPE_SCRIPT) // safety check + continue; + + curScriptObj = (Script *)curSegmentObj; + debugPrintf("=== SCRIPT %d from Segment %d ===\n", curScriptObj->getScriptNumber(), curSegmentNr); + debugN("=== SCRIPT %d from Segment %d ===\n", curScriptObj->getScriptNumber(), curSegmentNr); + + // now print the string list + curScriptObj->debugPrintStrings(this); + debugPrintf("\n"); + debugN("\n"); + } + return true; +} + bool Console::cmdBacktrace(int argc, const char **argv) { debugPrintf("Call stack (current base: 0x%x):\n", _engine->_gamestate->executionStackBase); Common::List<ExecStack>::const_iterator iter; diff --git a/engines/sci/console.h b/engines/sci/console.h index 6d024082b5..00d95b511f 100644 --- a/engines/sci/console.h +++ b/engines/sci/console.h @@ -147,6 +147,7 @@ private: bool cmdBreakpointFunction(int argc, const char **argv); // VM bool cmdScriptSteps(int argc, const char **argv); + bool cmdScriptStrings(int argc, const char **argv); bool cmdVMVarlist(int argc, const char **argv); bool cmdVMVars(int argc, const char **argv); bool cmdStack(int argc, const char **argv); diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index 88becc82cc..70e558854b 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,7 @@ void Script::freeScript() { _lockers = 1; _markedAsDeleted = false; _objects.clear(); + _stringLookupList.clear(); } void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptPatcher) { @@ -204,6 +206,191 @@ void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptP //_localsCount = (_bufSize - _localsOffset) >> 1; } } + + // find all strings of this script + identifyStrings(); +} + +void Script::identifyStrings() { + stringLookupListEntry listEntry; + byte *scriptDataPtr = NULL; + byte *stringStartPtr = NULL; + byte *stringDataPtr = NULL; + int scriptDataLeft = 0; + int stringDataLeft = 0; + byte stringDataByte = 0; + + _stringLookupList.clear(); + //stringLookupListType _stringLookupList; // Table of string data, that is inside the currently loaded script + + if (getSciVersion() < SCI_VERSION_1_1) { + scriptDataPtr = _buf; + scriptDataLeft = _bufSize; + + // Go through all SCI_OBJ_STRINGS blocks + if (getSciVersion() == SCI_VERSION_0_EARLY) { + if (scriptDataLeft < 2) + error("Script::identifyStrings(): unexpected end of script"); + scriptDataPtr += 2; + scriptDataLeft -= 2; + } + + uint16 blockType; + uint16 blockSize; + + do { + if (scriptDataLeft < 2) + error("Script::identifyStrings(): unexpected end of script"); + + blockType = READ_LE_UINT16(scriptDataPtr); + scriptDataPtr += 2; + scriptDataLeft -= 2; + if (blockType == 0) // end of blocks detected + break; + + if (scriptDataLeft < 2) + error("Script::identifyStrings(): unexpected end of script"); + + blockSize = READ_LE_UINT16(scriptDataPtr); + if (blockSize < 4) + error("Script::identifyStrings(): invalid block size"); + blockSize -= 4; // block size includes block-type UINT16 and block-size UINT16 + scriptDataPtr += 2; + scriptDataLeft -= 2; + + if (scriptDataLeft < blockSize) + error("Script::identifyStrings(): invalid block size"); + + if (blockType == SCI_OBJ_STRINGS) { + // string block detected, we now grab all NUL terminated strings out of this block + stringDataPtr = scriptDataPtr; + stringDataLeft = blockSize; + + do { + if (stringDataLeft < 1) // no more bytes left + break; + + stringStartPtr = stringDataPtr; + listEntry.ptrOffset = stringStartPtr - _buf; // Calculate offset inside script data + // now look for terminating [NUL] + do { + stringDataByte = READ_SCI11ENDIAN_UINT16(stringDataPtr); + stringDataPtr++; + stringDataLeft--; + if (!stringDataByte) // NUL found, exit this loop + break; + if (stringDataLeft < 1) // no more bytes left + error("Script::identifyStrings(): string without terminating NUL"); + } while (1); + + listEntry.stringSize = stringDataPtr - stringStartPtr; + _stringLookupList.push_back(listEntry); + } while (1); + } + + scriptDataPtr += blockSize; + scriptDataLeft -= blockSize; + } while (1); + + } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) { + // Strings in SCI1.1 come after the object instances + scriptDataPtr = _heapStart; + scriptDataLeft = _heapSize; + + if (scriptDataLeft < 4) + error("Script::identifyStrings(): unexpected end of script"); + + uint16 endOfStringOffset = READ_SCI11ENDIAN_UINT16(scriptDataPtr); + uint16 objectStartOffset = READ_SCI11ENDIAN_UINT16(scriptDataPtr + 2) * 2 + 4; + + if (scriptDataLeft < objectStartOffset) + error("Script::identifyStrings(): object start is beyond heap size"); + if (scriptDataLeft < endOfStringOffset) + error("Script::identifyStrings(): end of string is beyond heap size"); + + byte *endOfStringPtr = scriptDataPtr + endOfStringOffset; + + scriptDataPtr += objectStartOffset; + scriptDataLeft -= objectStartOffset; + + uint16 blockType; + uint16 blockSize; + + // skip all objects + do { + if (scriptDataLeft < 2) + error("Script::identifyStrings(): unexpected end of script"); + + blockType = READ_SCI11ENDIAN_UINT16(scriptDataPtr); + scriptDataPtr += 2; + scriptDataLeft -= 2; + if (blockType != SCRIPT_OBJECT_MAGIC_NUMBER) + break; + + if (scriptDataLeft < 2) + error("Script::identifyStrings(): unexpected end of script"); + + blockSize = READ_SCI11ENDIAN_UINT16(scriptDataPtr) * 2; + scriptDataPtr += 2; + scriptDataLeft -= 2; + blockSize -= 4; // blocksize contains UINT16 type and UINT16 size + if (scriptDataLeft < blockSize) + error("Script::identifyStrings(): invalid block size"); + + scriptDataPtr += blockSize; + scriptDataLeft -= blockSize; + } while (1); + + // now scriptDataPtr points to right at the start of the strings + if (scriptDataPtr > endOfStringPtr) + error("Script::identifyStrings(): string block / end-of-string block mismatch"); + + stringDataPtr = scriptDataPtr; + stringDataLeft = endOfStringPtr - scriptDataPtr; // Calculate byte count within string-block + + do { + if (stringDataLeft < 1) // no more bytes left + break; + + stringStartPtr = stringDataPtr; + listEntry.ptrOffset = stringStartPtr - _buf; // Calculate offset inside script data + // now look for terminating [NUL] + do { + stringDataByte = READ_SCI11ENDIAN_UINT16(stringDataPtr); + stringDataPtr++; + stringDataLeft--; + if (!stringDataByte) // NUL found, exit this loop + break; + if (stringDataLeft < 1) // no more bytes left + error("Script::identifyStrings(): string without terminating NUL"); + } while (1); + + listEntry.stringSize = stringDataPtr - stringStartPtr; + _stringLookupList.push_back(listEntry); + } while (1); + + } else if (getSciVersion() == SCI_VERSION_3) { + warning("TODO: identifyStrings(): Implement SCI3 variant"); + return; + } +} + +void Script::debugPrintStrings(Console *con) { + stringLookupListType::iterator it; + const stringLookupListType::iterator end = _stringLookupList.end(); + byte *stringPtr; + + if (!_stringLookupList.size()) { + con->debugPrintf(" no strings\n"); + debugN(" no strings\n"); + return; + } + + for (it = _stringLookupList.begin(); it != end; ++it) { + stringPtr = _buf + it->ptrOffset; + con->debugPrintf(" %04x: '%s' (size %d)\n", it->ptrOffset, stringPtr, it->stringSize); + debugN(" %04x: '%s' (size %d)\n", it->ptrOffset, stringPtr, it->stringSize); + } } const byte *Script::getSci3ObjectsPointer() { diff --git a/engines/sci/engine/script.h b/engines/sci/engine/script.h index 46d6ace917..fe774aeab5 100644 --- a/engines/sci/engine/script.h +++ b/engines/sci/engine/script.h @@ -49,6 +49,13 @@ enum ScriptObjectTypes { typedef Common::HashMap<uint16, Object> ObjMap; +struct stringLookupListEntry { + uint16 ptrOffset; // offset of the string + uint16 stringSize; // size of string, including terminating [NUL] +}; + +typedef Common::List<stringLookupListEntry> stringLookupListType; + class Script : public SegmentObj { private: int _nr; /**< Script number */ @@ -75,6 +82,8 @@ private: ObjMap _objects; /**< Table for objects, contains property variables */ + stringLookupListType _stringLookupList; // Table of string data, that is inside the currently loaded script + public: int getLocalsOffset() const { return _localsOffset; } uint16 getLocalsCount() const { return _localsCount; } @@ -248,6 +257,11 @@ public: */ int getCodeBlockOffsetSci3() { return READ_SCI11ENDIAN_UINT32(_buf); } + /** + * Print string lookup table (list) to debug console + */ + void debugPrintStrings(Console *con); + private: /** * Processes a relocation block within a SCI0-SCI2.1 script @@ -294,6 +308,11 @@ private: void initializeObjectsSci3(SegManager *segMan, SegmentId segmentId); LocalVariables *allocLocalsSegment(SegManager *segMan); + + /** + * Identifies strings within script data and set up lookup-table + */ + void identifyStrings(); }; } // End of namespace Sci |