SCI: Scripts: identify strings + debug command

debug command is called "script_strings" / "scrs"
This commit is contained in:
Martin Kiewitz 2015-05-04 21:19:05 +02:00
parent 3d0c691694
commit ed7007162a
4 changed files with 274 additions and 0 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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() {

View File

@ -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