/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program 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.

 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-12-0/engines/cruise/decompiler.cpp $
 * $Id: decompiler.cpp 30664 2008-01-27 19:47:41Z jvprat $
 *
 */

#include "cruise/cruise_main.h"

namespace Cruise {

#ifdef DUMP_SCRIPT

#define numMaxLignes 100000
#define lineMaxSize 10000

int currentLineType = 0;

struct decompileLineStruct {
	int lineOffset;
	char line[lineMaxSize];
	int indent;
	int type;
	int pendingElse;
};

struct decompileLineStruct decompileLineTable[numMaxLignes];

int positionInDecompileLineTable;

int failed;

char *currentDecompScript;
scriptInstanceStruct dummy;
scriptInstanceStruct *currentDecompScriptPtr = &dummy;

uint8 *getDataFromData3(ovlData3Struct * ptr, int param);

opcodeTypeFunction decompOpcodeTypeTable[64];

int currentLineIdx = 0;

unsigned long int currentOffset;

unsigned long int dumpIdx = 0;

FILE *fHandle = NULL;

#define DECOMPILER_STACK_DEPTH 100
#define DECOMPILER_STACK_ENTRY_SIZE 5000

char tempbuffer[5000];

char decompileStack[DECOMPILER_STACK_DEPTH][DECOMPILER_STACK_ENTRY_SIZE];

unsigned long int decompileStackPosition = 0;

uint8 stringName[256];

ovlData3Struct *currentScript;

ovlDataStruct *currentDecompOvl;
int currentDecompScriptIdx;

char decompSaveOpcodeVar[256];

uint8 *getStringNameFromIdx(uint16 stringTypeIdx, char *offset) {
	switch (stringTypeIdx & 7) {
	case 2:
		{
			sprintf(stringName, "\"%s\"",
			    currentScript->dataPtr +
			    currentScript->offsetToSubData3 + atoi(offset));
			break;
		}
	case 5:
		{
			sprintf(stringName, "vars[%s]", offset);
			break;
		}
	default:
		{
			sprintf(stringName, "string[%d][%s]",
			    stringTypeIdx & 7, offset);
			break;
		}
	}

	return stringName;
}

char *resolveMessage(char *messageIdxString) {
	char buffer[500];
	int variable;

	variable = atoi(messageIdxString);
	sprintf(buffer, "%d", variable);

	if (strcmp(buffer, messageIdxString)) {
		return messageIdxString;
	} else {
		return currentDecompOvl->stringTable[atoi(messageIdxString)].
		    string;
	}
}

void pushDecomp(char *string, ...) {
	va_list va;

	va_start(va, string);
	vsprintf(decompileStack[decompileStackPosition], string, va);
	va_end(va);

	// fprintf(fHandle, "----> %s\n",decompileStack[decompileStackPosition]);

	decompileStackPosition++;
}

void resolveDecompShort(char *buffer) {
	ovlData3Struct *data3Ptr = currentScript;
	int i;

	importScriptStruct *importEntry =
		(importScriptStruct *) (data3Ptr->dataPtr +
		data3Ptr->offsetToImportData);

	for (i = 0; i < data3Ptr->numRelocGlob; i++) {
		switch (importEntry->type) {
		case 20:	// script
		case 30:
		case 40:
		case 50:
			{
				if (importEntry->offset == currentDecompScriptPtr->var4 - 3) {	// param1
					sprintf(buffer,
						data3Ptr->dataPtr +
						data3Ptr->
						offsetToImportName +
						importEntry->offsetToName);
					return;
				}
				if (importEntry->offset == currentDecompScriptPtr->var4 - 6) {	// param2
					sprintf(buffer, "linkedIdx");
					return;
				}
				break;
			}
		default:
			{
				if (importEntry->offset ==
					currentDecompScriptPtr->var4 - 4) {
					sprintf(buffer,
						data3Ptr->dataPtr +
						data3Ptr->
						offsetToImportName +
						importEntry->offsetToName);
					return;
				}
			}
		}
		importEntry++;
	}

	buffer[0] = 0;

}

void resolveDecompChar(char *buffer) {
	ovlData3Struct *data3Ptr = currentScript;
	int i;

	importScriptStruct *importEntry =
		(importScriptStruct *) (data3Ptr->dataPtr +
		data3Ptr->offsetToImportData);

	for (i = 0; i < data3Ptr->numRelocGlob; i++) {
		switch (importEntry->type) {
		default:
			{
				if (importEntry->offset ==
					currentDecompScriptPtr->var4 - 2) {
					sprintf(buffer,
						data3Ptr->dataPtr +
						data3Ptr->
						offsetToImportName +
						importEntry->offsetToName);
					return;
				}
			}
		}
		importEntry++;
	}

	buffer[0] = 0;

}

char *popDecomp() {
	//  printf("<----\n");

	if (!decompileStackPosition) {
		return ("");
	}

	decompileStackPosition--;

	return decompileStack[decompileStackPosition];
}

void getByteFromDecompScript(char *buffer) {
	short int var = currentDecompScript[currentDecompScriptPtr->var4];

	currentDecompScriptPtr->var4 = currentDecompScriptPtr->var4 + 1;

	if (var == -1) {
		resolveDecompChar(buffer);

		if (buffer[0])
			return;
	}

	sprintf(buffer, "%d", var);
}

char getByteFromDecompScriptReal(void) {
	short int var = currentDecompScript[currentDecompScriptPtr->var4];

	currentDecompScriptPtr->var4 = currentDecompScriptPtr->var4 + 1;

	return var;
}

void getShortFromDecompScript(char *buffer) {
	short int var =
	    *(int16 *) (currentDecompScript + currentDecompScriptPtr->var4);

	currentDecompScriptPtr->var4 = currentDecompScriptPtr->var4 + 2;

	flipShort(&var);

	if (var == -1) {
		resolveDecompShort(buffer);

		if (buffer[0])
			return;
	}

	sprintf(buffer, "%d", var);
}

short int getShortFromDecompScriptReal(void) {
	short int var =
	    *(int16 *) (currentDecompScript + currentDecompScriptPtr->var4);

	currentDecompScriptPtr->var4 = currentDecompScriptPtr->var4 + 2;

	flipShort(&var);

	return var;
}

void addDecomp(char *string, ...) {
	va_list va;

	/* fprintf(fHandle,"%d: ",currentLineIdx);
	 *
	 * va_start(va,string);
	 * vfprintf(fHandle,string,va);
	 * va_end(va);
	 *
	 * fprintf(fHandle,"\n"); */

	struct decompileLineStruct *pLineStruct =
	    &decompileLineTable[positionInDecompileLineTable++];

	pLineStruct->lineOffset = currentLineIdx;
	pLineStruct->indent = 0;
	pLineStruct->type = currentLineType;
	pLineStruct->line[0] = 0;
	pLineStruct->pendingElse = 0;

	va_start(va, string);
	vsprintf(pLineStruct->line, string, va);
	va_end(va);

	currentLineIdx = currentDecompScriptPtr->var4;
	currentLineType = 0;

	/*printf("%d: ",currentOffset);
	 *
	 * va_start(va,string);
	 * vprintf(string,va);
	 * va_end(va);
	 *
	 * printf("\n"); */
}

void resolveVarName(char *ovlIdxString, int varType, char *varIdxString,
	    char *outputName) {
	int varIdx = atoi(varIdxString);

	strcpy(outputName, "");

	if (varType == 2) {
		strcpy(outputName, getStringNameFromIdx(varType,
			varIdxString));
		return;
	}
	if (varType == 1) {
		sprintf(outputName, "localVar_%s", varIdxString);
		return;
	}

	if (!strcmp(ovlIdxString, "0")) {
		int i;

		for (i = 0; i < currentDecompOvl->numSymbGlob; i++) {
			if (varIdx == currentDecompOvl->arraySymbGlob[i].idx) {
				if (((currentDecompOvl->arraySymbGlob[i].var4 & 0xF0) == 0) && varType != 0x20) {	// var
					strcpy(outputName,
					    currentDecompOvl->arrayNameSymbGlob +
					    currentDecompOvl->arraySymbGlob[i].
					    offsetToName);
					return;
				}
				if ((currentDecompOvl->arraySymbGlob[i].var4) == 20 && varType == 0x20) {	// script
					strcpy(outputName,
					    currentDecompOvl->arrayNameSymbGlob +
					    currentDecompOvl->arraySymbGlob[i].
					    offsetToName);
					return;
				}
			}
		}
		sprintf(outputName, "ovl(%s).[%d][%s]", ovlIdxString, varType,
		    varIdxString);
	} else {
		strcpy(outputName, ovlIdxString);
	}
}

int decompLoadVar(void) {
	switch (currentScriptOpcodeType) {
	case 0:
		{
			char buffer[256];

			getShortFromDecompScript(buffer);

			pushDecomp(buffer);

			return (0);
		}
		// string
	case 1:
		{
			char buffer1[256];
			char buffer2[256];
			char buffer3[256];
			char varName[256];

			getByteFromDecompScript(buffer1);
			getByteFromDecompScript(buffer2);

			getShortFromDecompScript(buffer3);

			resolveVarName(buffer2, atoi(buffer1) & 7, buffer3,
			    varName);

			pushDecomp("%s", varName);
			return (0);
		}
	case 2:
		{
			char buffer1[256];
			char buffer2[256];
			char buffer3[256];
			char varName[256];

			getByteFromDecompScript(buffer1);
			getByteFromDecompScript(buffer2);

			getShortFromDecompScript(buffer3);

			resolveVarName(buffer2, atoi(buffer1) & 7, buffer3,
			    varName);

			pushDecomp("%s", varName);
			return (0);
		}
	case 5:
		{
			char buffer1[256];
			char buffer2[256];
			char buffer3[256];
			char varName[256];

			getByteFromDecompScript(buffer1);
			getByteFromDecompScript(buffer2);

			getShortFromDecompScript(buffer3);

			resolveVarName(buffer2, atoi(buffer1) & 7, buffer3,
			    varName);

			pushDecomp("%s[%s]", varName, decompSaveOpcodeVar);
			return (0);
		}
	default:
		{
			printf("Unsupported type %d in opcodeType0\n",
			    currentScriptOpcodeType);
			failed = 1;
		}
	}
}

int decompSaveVar(void) {
//      int var = popVar();

	switch (currentScriptOpcodeType) {
	case 0:
		{
			addDecomp(popDecomp());
			return (0);
		}
		// modify string
	case 1:
		{
			char buffer1[256];
			char buffer2[256];
			char buffer3[256];
			char varName[256];
			uint8 type;

			getByteFromDecompScript(buffer1);
			getByteFromDecompScript(buffer2);

			getShortFromDecompScript(buffer3);

			type = atoi(buffer1) & 7;

			resolveVarName(buffer2, type, buffer3, varName);

			addDecomp("%s = %s", varName, popDecomp());
			break;
		}
	case 2:
		{
			char buffer1[256];
			char buffer2[256];
			char buffer3[256];

			getByteFromDecompScript(buffer1);
			getByteFromDecompScript(buffer2);

			getShortFromDecompScript(buffer3);

			addDecomp("ovl(%s).setVar(%s,%s) = %s", buffer2,
			    buffer1, buffer3, popDecomp());
			break;
		}
	case 4:
		{
			strcpy(decompSaveOpcodeVar, popDecomp());
			break;
		}
	case 5:
		{
			char buffer1[256];
			char buffer2[256];
			char buffer3[256];
			char varName[256];
			uint8 type;

			getByteFromDecompScript(buffer1);
			getByteFromDecompScript(buffer2);

			getShortFromDecompScript(buffer3);

			type = atoi(buffer1) & 7;

			resolveVarName(buffer2, type, buffer3, varName);

			addDecomp("%s[%s] = %s", varName, decompSaveOpcodeVar,
			    popDecomp());
			break;
		}
	default:
		{
			printf("Unsupported type %d in opcodeType1\n",
			    currentScriptOpcodeType);
			failed = 1;
		}
	}

	return (0);
}

int decompOpcodeType2(void) {
	switch (currentScriptOpcodeType) {
	case 1:
		{
			char buffer3[256];
			char varName[256];
			int byte1 = getByteFromDecompScriptReal();
			int byte2 = getByteFromDecompScriptReal();
			getShortFromDecompScript(buffer3);

			resolveVarName("0", byte1 & 7, buffer3, varName);

			pushDecomp(varName);

			break;
		}
	case 5:
		{
			int byte1 = getByteFromDecompScriptReal();
			int byte2 = getByteFromDecompScriptReal();
			short int short1 = getShortFromDecompScriptReal();

			int8 *ptr = scriptDataPtrTable[byte1 & 7] + short1;

			if ((byte1 & 7) == 2) {
				pushDecomp("\"%s\"[%s]", ptr,
				    decompSaveOpcodeVar);
			} else if ((byte1 & 7) == 5) {
				pushDecomp("freeString[%d][%s]", short1,
				    decompSaveOpcodeVar);
			} else {
				printf("Unsupported type %d in opcodeType2\n",
				    byte1 & 7);
				failed = 1;
			}
			break;
		}
	default:
		{
			printf("Unsupported type %d in opcodeType2\n",
			    currentScriptOpcodeType);
			failed = 1;
		}
	}
	return (0);
}

int decompMath(void) {
	char *param1 = popDecomp();
	char *param2 = popDecomp();

	switch (currentScriptOpcodeType) {
	case 0:
		{
			sprintf(tempbuffer, "%s+%s", param1, param2);
			pushDecomp(tempbuffer);
			break;
		}
	case 1:
		{
			sprintf(tempbuffer, "%s/%s", param1, param2);
			pushDecomp(tempbuffer);
			break;
		}
	case 2:
		{
			sprintf(tempbuffer, "%s-%s", param1, param2);
			pushDecomp(tempbuffer);
			break;
		}
	case 3:
		{
			sprintf(tempbuffer, "%s*%s", param1, param2);
			pushDecomp(tempbuffer);
			break;
		}
	case 4:
		{
			sprintf(tempbuffer, "%s\%%s", param1, param2);
			pushDecomp(tempbuffer);
			break;
		}
	case 5:
	case 7:
		{
			sprintf(tempbuffer, "%s|%s", param1, param2);
			pushDecomp(tempbuffer);
			break;
		}
	case 6:
		{
			sprintf(tempbuffer, "%s&%s", param1, param2);
			pushDecomp(tempbuffer);
			break;
		}

	default:
		{
			sprintf(tempbuffer, "decompMath(%d,%s,%s)",
			    currentScriptOpcodeType, param1, param2);
			pushDecomp(tempbuffer);
			break;
		}
	}
	return (0);
}

int decompBoolCompare(void) {
	char *param1;
	char *param2;

	param1 = popDecomp();
	param2 = popDecomp();

	sprintf(tempbuffer, "compare(%s,%s)", param1, param2);
	pushDecomp(tempbuffer);

	return 0;
}

int decompTest(void) {
	unsigned long int oldOffset = currentDecompScriptPtr->var4;
	short int offset = getShortFromDecompScriptReal();

	switch (currentScriptOpcodeType) {
	case 0:
		{
			currentLineType = 1;
			addDecomp("test '!(bitMask & 1)' and goto %d",
			    offset + oldOffset);
			break;
		}
	case 1:
		{
			currentLineType = 1;
			addDecomp("test '(bitMask & 1)' and goto %d",
			    offset + oldOffset);
			break;
		}
	case 2:
		{
			currentLineType = 1;
			addDecomp("test '(bitMask & 2)' and goto %d",
			    offset + oldOffset);
			break;
		}
	case 3:
		{
			currentLineType = 1;
			addDecomp("test '(bitMask & 3)' and goto %d",
			    offset + oldOffset);
			break;
		}
	case 4:
		{
			currentLineType = 1;
			addDecomp("test '(bitMask & 4)' and goto %d",
			    offset + oldOffset);
			break;
		}
	case 5:
		{
			currentLineType = 1;
			addDecomp("test '(bitMask & 5)' and goto %d",
			    offset + oldOffset);
			break;
		}
	case 6:
		{
			currentLineType = 2;
			addDecomp("test 'never' and goto %d",
			    offset + oldOffset);
			break;
		}
	case 7:
		{
			currentLineType = 3;
			addDecomp("goto %d", offset + oldOffset);
			break;
		}

	}

	return 0;
}

int decompCompare(void) {
	char *param;

	param = popDecomp();

	addDecomp("sign(%s)", param);

/*
  if (!pop)
    si = 1;

  if (pop<0) {
    si |= 4;
  }

  if (pop>0) {
    si |= 2;
  }

  currentScriptPtr->bitMask = si;
*/

	return 0;
}

int decompSwapStack(void) {
	char *stack1;
	char *stack2;
	char buffer1[4000];
	char buffer2[4000];

	stack1 = popDecomp();
	stack2 = popDecomp();

	strcpy(buffer1, stack1);
	strcpy(buffer2, stack2);

	pushDecomp(buffer1);
	pushDecomp(buffer2);

	return 0;
}

int decompFunction(void) {
	currentScriptOpcodeType = getByteFromDecompScriptReal();
//    addDecomp("OP_%X", currentScriptOpcodeType);
	switch (currentScriptOpcodeType) {
	case 0x1:
		{
			pushDecomp("_setdoFade()");
			break;
		}
	case 0x2:
		{
			pushDecomp("_prepareFade()");
			break;
		}
	case 0x3:
		{
			sprintf(tempbuffer, "_loadBackground(%s,%s)",
			    popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x4:
		{
			sprintf(tempbuffer, "_loadFullBundle(%s,%s)",
			    popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x5:
		{
			sprintf(tempbuffer, "_addCell(%s,%s,%s)", popDecomp(),
			    popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x6:
		{
			unsigned long int numArg = atoi(popDecomp());
			char *ovlStr;
			char *idxStr;
			int i;
			char functionName[100];

			idxStr = popDecomp();
			ovlStr = popDecomp();

			resolveVarName(ovlStr, 0x20, idxStr, functionName);

			sprintf(tempbuffer, "_startASync(%s", functionName);

			for (i = 0; i < numArg; i++) {
				strcatuint8(tempbuffer, ",");
				strcatuint8(tempbuffer, popDecomp());
			}

			strcatuint8(tempbuffer, ")");

			pushDecomp(tempbuffer);
			break;
		}
	case 0x7:
		{
			char *var1;
			char *objIdxStr;
			char *ovlStr;
			char varName[256];
			int i;

			var1 = popDecomp();
			objIdxStr = popDecomp();
			ovlStr = popDecomp();

			sprintf(tempbuffer,
			    "_createObjectFromOvlData(ovl:%s,dataIdx:%s,%s)",
			    ovlStr, objIdxStr, var1);
			pushDecomp(tempbuffer);
			break;
		}
	case 0x8:
		{
			sprintf(tempbuffer, "_removeCell(%s,%s,%s)",
			    popDecomp(), popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x9:
		{
			pushDecomp("_freeobjectList()");
			break;
		}
	case 0xA:
		{
			sprintf(tempbuffer, "_removeScript(ovl(%s),%s)",
			    popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0xB:
		{
			sprintf(tempbuffer, "_resetFilesEntries(%s,%s)",
			    popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0xC:
		{
			sprintf(tempbuffer, "_loadOverlay(%s)", popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0xD:
		{
			sprintf(tempbuffer, "_palManipulation(%s,%s,%s,%s,%s)",
			    popDecomp(), popDecomp(), popDecomp(), popDecomp(),
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0xE:
		{
			sprintf(tempbuffer, "_playSample(%s,%s,%s,%s)",
			    popDecomp(), popDecomp(), popDecomp(),
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x10:
		{
			sprintf(tempbuffer, "_releaseScript2(%s)",
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x11:
		{
			sprintf(tempbuffer, "_getOverlayIdx(%s)", popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x13:
		{
			sprintf(tempbuffer,
			    "_displayMessage(%s,\"%s\",%s,%s,%s,%s)",
			    popDecomp(), resolveMessage(popDecomp()),
			    popDecomp(), popDecomp(), popDecomp(),
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x14:
		{
			sprintf(tempbuffer, "_removeObject(ovl(%s),%s)",
			    popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x15:
		{
			pushDecomp("_pauseScript()");
			break;
		}
	case 0x16:
		{
			sprintf(tempbuffer,
			    "_Op_FreezeCell(%s,%s,%s,%s,%s,%s)", popDecomp(),
			    popDecomp(), popDecomp(), popDecomp(), popDecomp(),
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x17:
		{
			sprintf(tempbuffer, "_loadCtp(%s)", popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x18:
		{
			sprintf(tempbuffer,
			    "_Op_AddAnimation(%s,%s,%s,%s,%s,%s,%s)",
			    popDecomp(), popDecomp(), popDecomp(), popDecomp(),
			    popDecomp(), popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x19:
		{
			sprintf(tempbuffer, "_Op_RemoveAnimation(%s,%s,%s)",
			    popDecomp(), popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x1A:
		{
			sprintf(tempbuffer, "_setupScaleFormula(%s,%s,%s,%s)",
			    popDecomp(), popDecomp(), popDecomp(),
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x1E:
		{
			sprintf(tempbuffer, "_Op_TrackAnim(%s,%s,%s,%s,%s,%s)",
			    popDecomp(), popDecomp(), popDecomp(), popDecomp(),
			    popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x21:
		{
			sprintf(tempbuffer, "_isActorLoaded(%s,%s,%s)",
			    popDecomp(), popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x22:
		{
			sprintf(tempbuffer, "_computeScale(%s)", popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x23:
		{
			sprintf(tempbuffer, "_convertToScale(%s,%s)",
			    popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x24:
		{
			sprintf(tempbuffer, "_op_24(%s,%s,%s,%s)", popDecomp(),
			    popDecomp(), popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x27:
		{
			sprintf(tempbuffer, "_getWalkBoxCollision(%s,%s)",
			    popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x28:
		{
			sprintf(tempbuffer, "_changeSaveAllowedState(%s)",
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x29:
		{
			pushDecomp("_freeAllPerso()");
			break;
		}
	case 0x2B:
		{
			sprintf(tempbuffer, "_getProcIdx(%s,%s)", popDecomp(),
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x2C:
		{
			sprintf(tempbuffer, "_setObjectPosition(%s,%s,%s)",
			    popDecomp(), popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x2E:
		{
			sprintf(tempbuffer, "_releaseScript(%s)", popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x2F:
		{
			sprintf(tempbuffer, "_addBackgroundIncrust(%s,%s,%s)",
			    popDecomp(), popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x30:
		{
			sprintf(tempbuffer, "_removeBackgroundIncrust(%s,%s)",
			    popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x31:
		{
			sprintf(tempbuffer, "_op_31(%s,%s)", popDecomp(),
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x32:
		{
			pushDecomp("_freeBackgroundInscrustList()");
			break;
		}
	case 0x35:
		{
			sprintf(tempbuffer, "_op35(%s)", popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x37:
		{
			sprintf(tempbuffer, "_op37(%s,%s)", popDecomp(),
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x38:
		{
			sprintf(tempbuffer, "_removeBackground(%s)",
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x39:
		{
			sprintf(tempbuffer, "_SetActiveBackgroundPlane(%s)",
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x3A:
		{
			sprintf(tempbuffer, "_setVar49(%s)", popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x3B:
		{
			pushDecomp("_op3B()");
			break;
		}
	case 0x3C:
		{
			sprintf(tempbuffer, "_rand(%s)", popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x3D:
		{
			sprintf(tempbuffer, "_loadMusic(%s)", popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x3E:
		{
			pushDecomp("_op_3E()");
			break;
		}
	case 0x3F:
		{
			pushDecomp("_op_3F()");
			break;
		}
	case 0x40:
		{
			pushDecomp("_op_40()");
			break;
		}
	case 0x41:
		{
			sprintf(tempbuffer, "_isFileLoaded2(%s)", popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x45:
		{
			pushDecomp("_stopSound()");
			break;
		}
	case 0x49:
		{
			sprintf(tempbuffer, "_op49(%s)", popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x54:
		{
			sprintf(tempbuffer, "_setFontVar(%s)", popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x56:
		{
			sprintf(tempbuffer, "_changeCutSceneState(%s)",
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x57:
		{
			pushDecomp("_getMouseX()");
			break;
		}
	case 0x58:
		{
			pushDecomp("_getMouseY()");
			break;
		}
	case 0x59:
		{
			pushDecomp("_getMouse3()");
			break;
		}
	case 0x5A:
		{
			sprintf(tempbuffer, "_isFileLoaded(%s)", popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x5B:
		{
			pushDecomp("_regenerateBackgroundIncrust()");
			break;
		}
	case 0x5C:
		{
			sprintf(tempbuffer, "_Op_AddCellC(%s,%s)", popDecomp(),
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x5E:
		{
			sprintf(tempbuffer, "_Op_AddCellE(%s)", popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x60:
		{
			sprintf(tempbuffer, "_op_60(%s,%s,%s,%s,%s)",
			    popDecomp(), popDecomp(), popDecomp(), popDecomp(),
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x61:
		{
			sprintf(tempbuffer, "_op61(%s,%s)", popDecomp(),
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x62:
		{
			pushDecomp("_pauseCallingScript()");
			break;
		}
	case 0x63:
		{
			pushDecomp("_resumeScript()");
			break;
		}
	case 0x64:
		{
			unsigned long int numArg = atoi(popDecomp());
			char *ovlStr;
			char *idxStr;
			int i;
			char functionName[256];

			idxStr = popDecomp();
			ovlStr = popDecomp();

			resolveVarName(ovlStr, 0x20, idxStr, functionName);

			sprintf(tempbuffer, "%s(", functionName);

			for (i = 0; i < numArg; i++) {
				if (i)
					strcatuint8(tempbuffer, ",");
				strcatuint8(tempbuffer, popDecomp());
			}

			strcatuint8(tempbuffer, ")");

			pushDecomp(tempbuffer);
			break;
		}
	case 0x65:
		{
			sprintf(tempbuffer,
			    "_addWaitObject(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)",
			    popDecomp(), popDecomp(), popDecomp(), popDecomp(),
			    popDecomp(), popDecomp(), popDecomp(), popDecomp(),
			    popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x66:
		{
			sprintf(tempbuffer, "_op_66(%s,%s)", popDecomp(),
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x67:
		{
			sprintf(tempbuffer, "_loadAudioResource(%s,%s)",
			    popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x68:
		{
			pushDecomp("_freeMediumVar()");
			break;
		}
	case 0x6A:
		{
			sprintf(tempbuffer, "_op_6A(%s)", popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x6B:
		{
			sprintf(tempbuffer, "_loadData(%s,%s,%s,%s)",
			    popDecomp(), popDecomp(), popDecomp(),
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x6C:
		{
			sprintf(tempbuffer, "_op_6C(%s,%s)", popDecomp(),
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x6D:
		{
			sprintf(tempbuffer, "_strcpy(%s,%s)", popDecomp(),
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x6E:
		{
			sprintf(tempbuffer, "_op_6E(%s,%s)", popDecomp(),
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x6F:
		{
			unsigned long int numArg = atoi(popDecomp());
			char *ovlStr;
			char *idxStr;
			int i;

			idxStr = popDecomp();
			ovlStr = popDecomp();

			sprintf(tempbuffer, "_op_6F(%s,%s", idxStr, ovlStr);

			for (i = 0; i < numArg; i++) {
				strcatuint8(tempbuffer, ",");
				strcatuint8(tempbuffer, popDecomp());
			}

			strcatuint8(tempbuffer, ")");

			pushDecomp(tempbuffer);
			break;
		}
	case 0x70:
		{
			sprintf(tempbuffer, "_comment(%s)", popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x71:
		{
			sprintf(tempbuffer, "_op71(%s,%s,%s,%s,%s)",
			    popDecomp(), popDecomp(), popDecomp(), popDecomp(),
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x72:
		{
			sprintf(tempbuffer, "_op72(%s,%s)", popDecomp(),
			    popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x73:
		{
			sprintf(tempbuffer, "_op73(%s)", popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x74:
		{
			sprintf(tempbuffer, "_getlowMemory()");
			pushDecomp(tempbuffer);
			break;
		}
	case 0x76:
		{
			sprintf(tempbuffer, "_Op_InitializeState6(%s,%s)",
			    popDecomp(), popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x77:
		{
			sprintf(tempbuffer, "_Op_InitializeState7(%s)",
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x78:
		{
			sprintf(tempbuffer, "_Op_InitializeState8(%s)",
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x79:
		{
			sprintf(tempbuffer, "_EnterPlayerMenu(%s)",
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x7B:
		{
			sprintf(tempbuffer, "_Op_InitializeStateB(%s)",
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x7C:
		{
			sprintf(tempbuffer, "_Op_InitializeStateC(%s)",
			    popDecomp());
			pushDecomp(tempbuffer);
			break;
		}
	case 0x7D:
		{
			pushDecomp("_freeAllMenu()");
			break;
		}
	default:
		{
			addDecomp("OP_%X", currentScriptOpcodeType);
			printf("OPCODE: %X\n", currentScriptOpcodeType);
			failed = 1;
			break;
		}
	}

//    pushDecomp("functionDummyPush");

	return (0);
}

uint8 stop = 0;

int decompStop(void) {
	stop = 1;
	addDecomp("stop\n");
	return 0;
}

int decompBreak(void) {
	addDecomp("break");
	return 0;
}

void generateIndentation(void) {
	int i, j;

	for (i = 0; i < positionInDecompileLineTable; i++) {
		if (decompileLineTable[i].type != 0) {
			char *gotoStatement;
			int destLine;
			int destLineIdx;

			gotoStatement =
			    strstr(decompileLineTable[i].line, "goto");
			assert(gotoStatement);
			gotoStatement = strchr(gotoStatement, ' ') + 1;

			destLine = atoi(gotoStatement);
			destLineIdx = -1;

			for (j = 0; j < positionInDecompileLineTable; j++) {
				if (decompileLineTable[j].lineOffset == destLine) {
					destLineIdx = j;
					break;
				}
			}

			assert(destLineIdx != -1);

			if (destLineIdx > i) {
				for (j = i + 1; j < destLineIdx; j++) {
					decompileLineTable[j].indent++;
				}

				if (strstr(decompileLineTable[destLineIdx - 1].line, "goto") ==
					decompileLineTable[destLineIdx - 1].line) {
					//decompileLineTable[destLineIdx-1].pendingElse = 1;
				}
			}
		}
	}
}

void dumpScript(uint8 *ovlName, ovlDataStruct *ovlData, int idx) {
	uint8 opcodeType;
	char buffer[256];
	int i;

	char temp[256];
	char scriptName[256];

	sprintf(temp, "%d", idx);

	failed = 0;

	currentScript = &ovlData->arrayProc[idx];

	currentDecompScript = currentScript->dataPtr;
	currentDecompScriptPtr->var4 = 0;

	currentDecompOvl = ovlData;
	currentDecompScriptIdx = idx;

	currentLineIdx = 0;
	positionInDecompileLineTable = 0;
	currentLineType = 0;

	resolveVarName("0", 0x20, temp, scriptName);

	printf("decompiling script %d - %s\n", idx, scriptName);

	// return;

//      scriptDataPtrTable[1] = *(char**)(ptr+0x6);
	scriptDataPtrTable[2] = getDataFromData3(currentScript, 1);	// strings
	scriptDataPtrTable[5] = ovlData->data4Ptr;	// free strings
	scriptDataPtrTable[6] = ovlData->ptr8;

	stop = 0;

	sprintf(buffer, "%s-%02d-%s.txt", ovlName, idx, scriptName);
	fHandle = fopen(buffer, "w+");

	decompileStackPosition = 0;

	for (i = 0; i < 64; i++) {
		decompOpcodeTypeTable[i] = NULL;
	}

	decompOpcodeTypeTable[1] = decompLoadVar;
	decompOpcodeTypeTable[2] = decompSaveVar;
	decompOpcodeTypeTable[3] = decompOpcodeType2;
	decompOpcodeTypeTable[4] = decompMath;
	decompOpcodeTypeTable[5] = decompBoolCompare;
	decompOpcodeTypeTable[6] = decompTest;
	decompOpcodeTypeTable[7] = decompCompare;
	decompOpcodeTypeTable[8] = decompSwapStack;
	decompOpcodeTypeTable[9] = decompFunction;
	decompOpcodeTypeTable[10] = decompStop;
	decompOpcodeTypeTable[12] = decompBreak;

	do {
		currentOffset = currentDecompScriptPtr->var4;

		opcodeType = getByteFromDecompScriptReal();

		currentScriptOpcodeType = opcodeType & 7;

		if (!decompOpcodeTypeTable[(opcodeType & 0xFB) >> 3]) {
			printf("Unsupported opcode type %d in decomp\n",
			    (opcodeType & 0xFB) >> 3);
			return;
		}

		//printf("Optype: %d\n",(opcodeType&0xFB)>>3);

		decompOpcodeTypeTable[(opcodeType & 0xFB) >> 3] ();

		if (failed) {
			printf("Aborting decompilation..\n");
			fclose(fHandle);
			return;
		}

	} while (!stop);

	dumpIdx++;

	generateIndentation();

	for (i = 0; i < positionInDecompileLineTable; i++) {
		int j;

		if (decompileLineTable[i].pendingElse) {
			fprintf(fHandle, "%05d:\t",
			    decompileLineTable[i].lineOffset);
			fprintf(fHandle, "else", decompileLineTable[i].line);
			fprintf(fHandle, "\n");
		}

		fprintf(fHandle, "%05d:\t", decompileLineTable[i].lineOffset);
		for (j = 0; j < decompileLineTable[i].indent; j++) {
			fprintf(fHandle, "\t");
		}
		fprintf(fHandle, "%s", decompileLineTable[i].line);
		fprintf(fHandle, "\n");
	}

	fclose(fHandle);
}

#endif

} // End of namespace Cruise
