aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Kiewitz2015-05-04 21:19:05 +0200
committerMartin Kiewitz2015-05-04 21:19:05 +0200
commited7007162a092e4b9e3d9306b078af5a8984cad0 (patch)
tree466c6b93adb5ee00550ee428e188896ae655b2a9
parent3d0c691694ddcf00a5bfc347626d38625f057dee (diff)
downloadscummvm-rg350-ed7007162a092e4b9e3d9306b078af5a8984cad0.tar.gz
scummvm-rg350-ed7007162a092e4b9e3d9306b078af5a8984cad0.tar.bz2
scummvm-rg350-ed7007162a092e4b9e3d9306b078af5a8984cad0.zip
SCI: Scripts: identify strings + debug command
debug command is called "script_strings" / "scrs"
-rw-r--r--engines/sci/console.cpp67
-rw-r--r--engines/sci/console.h1
-rw-r--r--engines/sci/engine/script.cpp187
-rw-r--r--engines/sci/engine/script.h19
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