/* This file is part of Warzone 2100. Copyright (C) 1999-2004 Eidos Interactive Copyright (C) 2005-2010 Warzone 2100 Project Warzone 2100 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Warzone 2100 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Warzone 2100; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * CodePrint.c * * Routines for displaying compiled scripts */ #include "lib/framework/frame.h" #include "interpreter.h" #include "parse.h" #include "codeprint.h" #include "script.h" /* Display a value */ void cpPrintVal(INTERP_VAL value) { if (value.type & VAL_REF) { value.type &= ~VAL_REF; debug( LOG_NEVER, "type: %5s, value: %i (ref)", scriptTypeToString(value.type), value.v.ival ); return; } switch(value.type) { case VAL_BOOL: debug( LOG_NEVER, "type: %5s, value: %s", scriptTypeToString(value.type), bool2string(value.v.bval) ); break; case VAL_INT: debug( LOG_NEVER, "type: %5s, value: %d", scriptTypeToString(value.type), value.v.ival ); break; case VAL_FLOAT: debug( LOG_NEVER, "type: %5s, value: %f", scriptTypeToString(value.type), value.v.fval ); break; case VAL_STRING: debug( LOG_NEVER, "type: %5s, value: %s", scriptTypeToString(value.type), value.v.sval ); break; case VAL_TRIGGER: case VAL_EVENT: default: debug( LOG_NEVER, "type: %5s, value: %d", scriptTypeToString(value.type), value.v.ival ); break; } } /* Display a value from a program that has been packed with an opcode */ void cpPrintPackedVal(INTERP_VAL *ip) { INTERP_VAL data = { (ip->v.ival & OPCODE_DATAMASK), {0} }; data.v = (ip + 1)->v; cpPrintVal(data); } /* Print a variable access function name */ void cpPrintVarFunc(SCRIPT_VARFUNC pFunc, UDWORD index) { // Search the external variable functions if (asScrExternalTab) { unsigned int i; for(i = 0; asScrExternalTab[i].pIdent != NULL; i++) { if (asScrExternalTab[i].index == index) { if (asScrExternalTab[i].set == pFunc) { debug( LOG_NEVER, "%s (set)", asScrExternalTab[i].pIdent ); return; } else if (asScrExternalTab[i].get == pFunc) { debug( LOG_NEVER, "%s (get)", asScrExternalTab[i].pIdent ); return; } } } } // Search the object functions if (asScrObjectVarTab) { unsigned int i; for(i = 0; asScrObjectVarTab[i].pIdent != NULL; i++) { if (asScrObjectVarTab[i].index == index) { if (asScrObjectVarTab[i].set == pFunc) { debug( LOG_NEVER, "%s (set)", asScrObjectVarTab[i].pIdent ); return; } else if (asScrObjectVarTab[i].get == pFunc) { debug( LOG_NEVER, "%s (get)", asScrObjectVarTab[i].pIdent ); return; } } } } } /* Print the array information */ static void cpPrintArrayInfo(INTERP_VAL *ip, SCRIPT_CODE *psProg) { // get the base index of the array unsigned int base = (ip->v.ival & ARRAY_BASE_MASK); unsigned int i; debug( LOG_NEVER, "-> %d", base ); for (i = 0; i < psProg->psArrayInfo[base].dimensions; i++) { debug( LOG_NEVER, " [%d]", psProg->psArrayInfo[base].elements[i] ); } } /* Display the contents of a program in readable form */ void cpPrintProgram(SCRIPT_CODE *psProg) { INTERP_VAL *ip, *end; OPCODE opcode; UDWORD data, i, dim; SCRIPT_DEBUG *psCurrDebug=NULL; BOOL debugInfo, triggerCode; UDWORD jumpOffset; VAR_DEBUG *psCurrVar; ARRAY_DATA *psCurrArray; ARRAY_DEBUG *psCurrArrayDebug; ASSERT( psProg != NULL, "cpPrintProgram: Invalid program pointer" ); debugInfo = (psProg->psDebug != NULL); if (debugInfo) { // Print out the global variables if (psProg->numGlobals > 0) { debug( LOG_NEVER, "index storage type variable name\n" ); psCurrVar = psProg->psVarDebug; for(i = 0; i < psProg->numGlobals; i++) { debug( LOG_NEVER, "%-6d %s %-4d %s\n", (int)(psCurrVar - psProg->psVarDebug), psCurrVar->storage == ST_PUBLIC ? "Public " : "Private", psProg->pGlobals[i], psCurrVar->pIdent ); psCurrVar++; } } if (psProg->numArrays > 0) { debug( LOG_NEVER, "\narrays\n" ); psCurrArray = psProg->psArrayInfo; psCurrArrayDebug = psProg->psArrayDebug; for(i = 0; i < psProg->numArrays; i++) { debug( LOG_NEVER, "%-6d %s %-4d %s", i, psCurrArrayDebug->storage == ST_PUBLIC ? "Public " : "Private", psCurrArray->type, psCurrArrayDebug->pIdent ); for(dim=0; dim < psCurrArray->dimensions; dim += 1) { debug( LOG_NEVER, "[%d]", psCurrArray->elements[dim] ); } debug( LOG_NEVER, "\n" ); psCurrArray++; psCurrArrayDebug++; } } debug( LOG_NEVER, "\nindex line offset\n" ); psCurrDebug = psProg->psDebug; } // Find the first trigger with code for(jumpOffset = 0; jumpOffset < psProg->numTriggers; jumpOffset++) { if (psProg->psTriggerData[jumpOffset].type == TR_CODE) { break; } } ip = psProg->pCode; end = (INTERP_VAL*)((UBYTE*)ip + psProg->size); triggerCode = (psProg->numTriggers > 0); opcode = (ip->v.ival >> OPCODE_SHIFT); data = (ip->v.ival & OPCODE_DATAMASK); while (ip < end) { if (debugInfo) { // display a label if there is one if ((UDWORD)(ip - psProg->pCode) == psCurrDebug->offset && psCurrDebug->pLabel != NULL) { debug( LOG_NEVER, "label: %s\n", psCurrDebug->pLabel ); } // Display the line number if ((UDWORD)(ip - psProg->pCode) == psCurrDebug->offset) { debug( LOG_NEVER, "line: %-6d", psCurrDebug->line ); psCurrDebug++; } } // Display the trigger/event index if (triggerCode) { if (ip - psProg->pCode == psProg->pTriggerTab[jumpOffset]) { debug( LOG_NEVER, "jumpoffset: %-6d", jumpOffset ); jumpOffset++; // Find the next trigger with code while(jumpOffset < psProg->numTriggers) { if (psProg->psTriggerData[jumpOffset].type == TR_CODE) { break; } jumpOffset++; } if (jumpOffset >= psProg->numTriggers) { // Got to the end of the triggers triggerCode = false; jumpOffset = 0; } } } else { if (ip - psProg->pCode == psProg->pEventTab[jumpOffset]) { debug( LOG_NEVER, "jumpoffset: %-6d", jumpOffset ); jumpOffset++; } } // Display the code offset debug( LOG_NEVER, "offset: %-6d", (int)(ip - psProg->pCode) ); debug( LOG_NEVER, "opcode: %s", scriptOpcodeToString(opcode) ); switch (opcode) { case OP_POP: // cpPrintVal(*stackPeek(0)); break; case OP_PUSH: case OP_PUSHREF: cpPrintPackedVal(ip); break; case OP_POPGLOBAL: case OP_PUSHGLOBAL: case OP_POPLOCAL: case OP_PUSHLOCAL: case OP_PUSHLOCALREF: debug( LOG_NEVER, "-> %d", data ); break; case OP_PUSHARRAYGLOBAL: case OP_POPARRAYGLOBAL: cpPrintArrayInfo(ip, psProg); break; case OP_FUNC: debug( LOG_NEVER, "-> %d", (ip+1)->v.ival ); break; case OP_CALL: debug( LOG_NEVER, "-> %s", scriptFunctionToString((ip+1)->v.pFuncExtern) ); break; case OP_VARCALL: cpPrintVarFunc( ((INTERP_VAL *)(ip+1))->v.pObjGetSet, data); debug( LOG_NEVER, "-> (%d)", data ); break; case OP_JUMP: case OP_JUMPTRUE: case OP_JUMPFALSE: debug( LOG_NEVER, "-> %d (%d)", (SWORD)data, (int)(ip - psProg->pCode + (SWORD)data) ); break; case OP_BINARYOP: case OP_UNARYOP: debug( LOG_NEVER, "-> %s", scriptOpcodeToString(data) ); break; case OP_EXIT: break; case OP_PAUSE: debug( LOG_NEVER, "-> %d", data ); break; case OP_TO_FLOAT: case OP_TO_INT: break; default: ASSERT( false,"cpPrintProgram: Unknown opcode: %x", ip->type ); break; } debug( LOG_NEVER, "\n" ); ip += aOpSize[opcode]; ASSERT( (ip <= end) || ip != NULL, "cpPrintProgram: instruction pointer no longer valid" ); opcode = (OPCODE)(ip->v.ival >> OPCODE_SHIFT); data = ip->v.ival & OPCODE_DATAMASK; } }