SCI2/2.1: Improvements to the find_callk debug function, and some kernel function updates

- Improved the find_callk function to properly find the end of script objects, by
monitoring jump calls
- Added three extra special calls to find_callk: find kernel function calls to dummy,
unused and unmapped kernel functions
- Updated several kernel function calls because of the above functionality
- The above functionality has also uncovered a VM bug in some SCI2/2.1 opcode - added 
a FIXME for it

svn-id: r55151
This commit is contained in:
Filippos Karapetis 2011-01-07 18:25:38 +00:00
parent 2f7c04e54b
commit 6b250f8c9b
3 changed files with 101 additions and 23 deletions

View File

@ -2710,26 +2710,12 @@ bool Console::cmdDisassembleAddress(int argc, const char **argv) {
return true;
}
bool Console::cmdFindKernelFunctionCall(int argc, const char **argv) {
if (argc < 2) {
DebugPrintf("Finds the scripts and methods that call a specific kernel function.\n");
DebugPrintf("Usage: %s <kernel function>\n", argv[0]);
DebugPrintf("Example: %s Display\n", argv[0]);
return true;
}
// Find the number of the kernel function call
int kernelFuncNum = _engine->getKernel()->findKernelFuncPos(argv[1]);
if (kernelFuncNum < 0) {
DebugPrintf("Invalid kernel function requested\n");
return true;
}
void Console::printKernelCallsFound(int kernelFuncNum, bool showFoundScripts) {
Common::List<ResourceId> *resources = _engine->getResMan()->listResources(kResourceTypeScript);
Common::sort(resources->begin(), resources->end());
Common::List<ResourceId>::iterator itr = resources->begin();
if (showFoundScripts)
DebugPrintf("%d scripts found, dissassembling...\n", resources->size());
int scriptSegment;
@ -2762,6 +2748,7 @@ bool Console::cmdFindKernelFunctionCall(int argc, const char **argv) {
int16 opparams[4];
byte extOpcode;
byte opcode;
uint16 maxJmpOffset = 0;
while (true) {
offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams);
@ -2778,8 +2765,28 @@ bool Console::cmdFindKernelFunctionCall(int argc, const char **argv) {
}
}
// Monitor all jump opcodes (bt, bnt and jmp), so that if
// there is a jump after a ret, we don't stop processing
if (opcode == op_bt || opcode == op_bnt || opcode == op_jmp) {
uint16 curJmpOffset = offset + (uint16)opparams[0];
if (curJmpOffset > maxJmpOffset)
maxJmpOffset = curJmpOffset;
// FIXME: There seems to be a bug in the way we handle the SCI2 debug opcode
// (i.e. 0x7e/0x3f), which is probably why the bugs below occur
if (maxJmpOffset >= script->getBufSize()) {
warning("Called from script %d, object %s, method %s(%d) with %d parameters",
itr->getNumber(), objName,
_engine->getKernel()->getSelectorName(obj->getFuncSelector(i)).c_str(), i, 0);
warning("Script %d has a jump to an invalid offset (%d, script size is %d) - adjusting",
script->getScriptNumber(), maxJmpOffset, script->getBufSize());
maxJmpOffset = script->getBufSize() - 1;
}
}
// Check for end of function/script
if (opcode == op_ret || offset >= script->getBufSize())
if (offset >= script->getBufSize())
break;
if (opcode == op_ret)// && offset >= maxJmpOffset)
break;
} // while (true)
} // for (uint16 i = 0; i < obj->getMethodCount(); i++)
@ -2790,6 +2797,69 @@ bool Console::cmdFindKernelFunctionCall(int argc, const char **argv) {
}
delete resources;
}
bool Console::cmdFindKernelFunctionCall(int argc, const char **argv) {
if (argc < 2) {
DebugPrintf("Finds the scripts and methods that call a specific kernel function.\n");
DebugPrintf("Usage: %s <kernel function>\n", argv[0]);
DebugPrintf("Example: %s Display\n", argv[0]);
DebugPrintf("Special usage:\n");
DebugPrintf("%s Dummy - find all calls to actual dummy functions "
"(mapped to kDummy, and dummy in the kernel table). "
"There shouldn't be calls to these (apart from a known "
"one in Shivers)\n", argv[0]);
DebugPrintf("%s Unused - find all calls to unused functions (mapped to "
"kDummy - i.e. mapped in SSCI but dummy in ScummVM, thus "
"they'll error out when called). Only debug scripts should "
"be calling these\n", argv[0]);
DebugPrintf("%s Unmapped - find all calls to currently unmapped or "
"unimplemented functions (mapped to kStub/kStubNull)\n", argv[0]);
return true;
}
Kernel *kernel = _engine->getKernel();
Common::String funcName(argv[1]);
if (funcName != "Dummy" && funcName != "Unused" && funcName != "Unmapped") {
// Find the number of the kernel function call
int kernelFuncNum = kernel->findKernelFuncPos(argv[1]);
if (kernelFuncNum < 0) {
DebugPrintf("Invalid kernel function requested\n");
return true;
}
printKernelCallsFound(kernelFuncNum, true);
} else if (funcName == "Dummy") {
// Find all actual dummy kernel functions (mapped to kDummy, and dummy
// in the kernel table)
for (uint i = 0; i < kernel->_kernelFuncs.size(); i++) {
if (kernel->_kernelFuncs[i].function == &kDummy && kernel->getKernelName(i) == "Dummy") {
DebugPrintf("Searching for kernel function %d (%s)...\n", i, kernel->getKernelName(i).c_str());
printKernelCallsFound(i, false);
}
}
} else if (funcName == "Unused") {
// Find all actual dummy kernel functions (mapped to kDummy - i.e.
// mapped in SSCI but dummy in ScummVM, thus they'll error out when
// called)
for (uint i = 0; i < kernel->_kernelFuncs.size(); i++) {
if (kernel->_kernelFuncs[i].function == &kDummy && kernel->getKernelName(i) != "Dummy") {
DebugPrintf("Searching for kernel function %d (%s)...\n", i, kernel->getKernelName(i).c_str());
printKernelCallsFound(i, false);
}
}
} else if (funcName == "Unmapped") {
// Find all unmapped kernel functions (mapped to kStub/kStubNull)
for (uint i = 0; i < kernel->_kernelFuncs.size(); i++) {
if (kernel->_kernelFuncs[i].function == &kStub ||
kernel->_kernelFuncs[i].function == &kStubNull) {
DebugPrintf("Searching for kernel function %d (%s)...\n", i, kernel->getKernelName(i).c_str());
printKernelCallsFound(i, false);
}
}
}
return true;
}

View File

@ -165,6 +165,13 @@ private:
void hexDumpReg(const reg_t *data, int len, int regsPerLine = 4, int startOffset = 0, bool isArray = false);
private:
/**
* Prints all the scripts calling the specified kernel function.
* NOTE: The results produced by this aren't 100% correct, as it
* does not dissect script exports
*/
void printKernelCallsFound(int kernelFuncNum, bool showFoundScripts);
SciEngine *_engine;
DebugState &_debugState;
bool _mouseVisible;

View File

@ -462,6 +462,7 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_DUMMY(Profiler), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(ShiftScreen), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(ListOps), SIG_EVERYWHERE, "(.*)", NULL, NULL },
// Used by the sysLogger class (e.g. script 952 in GK1CD), a class used to report bugs by Sierra's testers
{ MAP_DUMMY(ATan), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(Record), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(PlayBack), SIG_EVERYWHERE, "(.*)", NULL, NULL },
@ -503,6 +504,8 @@ static SciKernelMapEntry s_kernelMap[] = {
// VibrateMouse - used in QFG4 floppy
// PalCycle
// ObjectIntersect - used in QFG4 floppy
// MakeSaveCatName - used in the Save/Load dialog of GK1CD (SRDialog, script 64990)
// MakeSaveFileName - used in the Save/Load dialog of GK1CD (SRDialog, script 64990)
// SCI2 empty functions
@ -524,8 +527,6 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_DUMMY(ShowStylePercent), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(InvertRect), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(InputText), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(MakeSaveCatName), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(MakeSaveFileName), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(TextWidth), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(PointSize), SIG_EVERYWHERE, "(.*)", NULL, NULL },
@ -564,7 +565,6 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_DUMMY(CelRect), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(BaseLineSpan), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(CelLink), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(UpdateLine), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(AddPolygon), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(DeletePolygon), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(UpdatePolygon), SIG_EVERYWHERE, "(.*)", NULL, NULL },
@ -573,7 +573,6 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_DUMMY(Priority), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(WinDLL), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(DeletePic), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(GetSierraProfileInt), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(GetSierraProfileString), SIG_EVERYWHERE, "(.*)", NULL, NULL },
// SCI2.1 unmapped functions - TODO!
@ -585,11 +584,13 @@ static SciKernelMapEntry s_kernelMap[] = {
// ScrollWindow - used by Phantasmagoria 1 and SQ6
// AddLine - used by Torin's Passage to highlight the chapter buttons
// DeleteLine - used by Torin's Passage to delete the highlight from the chapter buttons
// UpdateLine = used by LSL6
// GetConfig - used by Phantasmagoria 1
// SetPalStyleRange
// NewRoom
// MorphOn - used by SQ6
// SetHotRectangles - used by Phantasmagoria 1
// GetSierraProfileInt - used by Phantasmagoria 1
#endif
{ NULL, NULL, SIG_EVERYWHERE, NULL, NULL, NULL }
@ -1040,7 +1041,7 @@ static const char *sci21_default_knames[] = {
/*0x7e*/ "Table",
/*0x7f*/ "WinHelp", // Windows only
/*0x80*/ "Dummy",
/*0x81*/ "Dummy",
/*0x81*/ "Dummy", // called when changing rooms in most SCI2.1 games (e.g. KQ7, GK2, MUMG deluxe, Phant1)
/*0x82*/ "Dummy",
/*0x83*/ "PrintDebug", // debug function, used by Shivers 2 (demo and full)
/*0x84*/ "Dummy",