// Decompiler.cpp: implementation of the Decompiler class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "chitem.h"
#include "Decompiler.h"
#include "chitem.h"
#include "2da.h"
#include "options.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <assert.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif


//////////////////////////////////////////////////////////////////////
// Helper classes
//////////////////////////////////////////////////////////////////////
/*************** point ****************************/
Point::Point(void)
{
	x = y = 0;
	//memset(this, 0, sizeof(*this));
}

Point::~Point(void)
{
}

Point::Point(const Point& pnt)
{
	x=pnt.x;
	y=pnt.y;
	//memcpy(this, &pnt, sizeof(*this));
}

Point& Point::operator=(const Point& pnt)
{
	x = pnt.x;
	y = pnt.y;
	//memcpy(this, &pnt, sizeof(*this));
	return *this;
}

bool Point::operator==(const Point& pnt)
{
	if (( x == pnt.x ) && ( y == pnt.y ))
	{
		return true;
	}
	return false;
}

Point::Point(short x, short y)
{
	this->x = x;
	this->y = y;
}

bool Point::isnull() const
{
	if (x==0 && y==0)
	{
		return true;
	}
	return false;
}

bool Point::isempty() const
{
	if (x==-1 && y==-1)
	{
		return true;
	}
	return false;
} 

/** Self-destructing object if it is empty */
bool Object::ReadyToDie(int ObjectFieldsCount)
{
	if (objectName[0]!=0)
	{
		return false;
	}
	if (objectFilters[0])
	{
		return false;
	}
	for (int i=0;i<ObjectFieldsCount;i++)
	{
		if (objectFields[i])
		{
			return false;
		}
	}
	return true;
}
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

Decompiler::Decompiler(int arg_log)
{
	logging = arg_log;
	flog = 0;
	fhandle = 0;
	fout = 0;
	buffer = 0;
	bpoi = 0;
	valid = 0;
	ObjectFieldsCount = 7;
	MaxObjectNesting = 5;
	ExtraParametersCount = 0;
	HasAdditionalRect = false;
	HasTriggerPoint = false;
	if (pst_compatible_var())
	{
		ObjectFieldsCount += 2;
		HasTriggerPoint = true;
		HasAdditionalRect = true;
	}
	if (has_xpvar())
	{
		HasAdditionalRect = true;
	}
	if (iwd2_structures())
	{
		ObjectFieldsCount++;
		ExtraParametersCount += 2;
	}
}

Decompiler::~Decompiler()
{
	CloseAll();
	if (buffer) free(buffer);
}

void Decompiler::CloseAll()
{
	if (flog) close(flog);
	flog = 0;
	if (fhandle) close(fhandle);
	fhandle = 0;
	if (fout) close(fout);
	fout = 0;
	bpoi = 0;
	valid = 0;
}

void Decompiler::ReadLine(int maxlength)
{
	int i;

	if (!buffer)
	{
		buffer = (char *) malloc(READ_BUFFERLENGTH);
		valid = 0;
		bpoi = 0;
	}
	assert(valid>=bpoi);
	assert(maxlength<=READ_BUFFERLENGTH);
	line.Empty();
	if (!buffer)
	{
		return;
	}
retry:
	if (bpoi+maxlength>valid)
	{
		memmove(buffer,buffer+bpoi, valid-bpoi);
		valid-=bpoi;
		bpoi=0;
	}
	valid += read(fhandle, buffer+valid, READ_BUFFERLENGTH-valid);
	if (valid<1)
	{
		return;
	}
	if (valid<maxlength)
	{
		maxlength=valid;
	}
	for(i=bpoi;i<bpoi+maxlength;i++)
	{
		if (buffer[i]=='\r' || buffer[i]=='\n')
		{
			buffer[i]=0;
			break;
		}
	}
	line = CString(buffer+bpoi);
	bpoi=i+1;
	if (line.IsEmpty())
	{
		goto retry;
	}
}

void Decompiler::WriteString(CString tmp)
{
	write(fout,tmp, tmp.GetLength());
}

int Decompiler::ParseInt(int &pos)
{
	char number[33];

	char* tmp = number;
	while (isdigit(line[pos]) || line[pos]=='-')
	{
		*tmp = line[pos];
		tmp++;
		pos++;
	}
	*tmp = 0;
	pos++;
	return atoi( number );
}

void Decompiler::ParseString(int &pos, char* tmp)
{
	while (line[pos] != '"')
	{
		*tmp = line[pos];
		if (!*tmp) break;
		tmp++;
		pos++;
	}
	*tmp = 0;
} 

CString Decompiler::PrintObject(Object *oB)
{
	CString ret;
	CString tmpstr;
	int i,j;
	bool flg = false;

	if (oB->objectName[0])
	{
		ret.Format("\"%s\"",oB->objectName);
	}
	else
	{
		ret="[";
		j = ObjectFieldsCount;
		while(j--)
		{
			if(oB->objectFields[j]) break;
		}
		for (i = 0; i <= j; i++)
		{
			tmpstr=IDSToken(IDSName(i,false), oB->objectFields[i], false);
			if (tmpstr.IsEmpty()) tmpstr.Format("%d",oB->objectFields[i]);
			tmpstr.MakeUpper();
			if (flg) ret+=".";
			ret+=tmpstr;
			flg=true;
		}
		ret+="]";
	}
	//
	for (i = 0; i < MaxObjectNesting; i++)
	{
		if (oB->objectFilters[i])
		{
			tmpstr=IDSToken("OBJECT",oB->objectFilters[i], false);
			if (tmpstr.IsEmpty()) tmpstr.Format("unknown");
			tmpstr.SetAt(0,(char) toupper(tmpstr[0]) );
			if (!flg)
			{
				ret = tmpstr;
				flg=true;
			}
			else
			{
				ret = tmpstr+"("+ret+")";
			}
		}
	}

	if (HasAdditionalRect)
	{
		ret+=PrintRegion(oB->objectRect);
	}
	return ret;
}

Object* Decompiler::DecodeObject(int &pos)
{
	int i;
	pos = 0;

	Object* oB = new Object();
	for (i = 0; i < ObjectFieldsCount; i++)
	{
		oB->objectFields[i] = ParseInt( pos );
	}
	for (i = 0; i < MaxObjectNesting; i++)
	{
		oB->objectFilters[i] = ParseInt( pos );
	}
	//iwd tolerates the missing rectangle, so we do so too
	if (HasAdditionalRect && (line[pos]=='[') )
	{
		pos++; //Skip [
		for (i = 0; i < 4; i++)
		{
			oB->objectRect[i] = ParseInt( pos );
		}
		if(line[pos]!=' ') goto die;
		pos++; //Skip a ' ', the ] was skipped by the ParseInt function
  } else {
    for (i = 0; i < 4; i++)
    {
      oB->objectRect[i]=-1;
    }
  }
	if (line[pos]!='"') goto die;
	pos++; //Skip "
	ParseString( pos, oB->objectName );
	if (line[pos]!='"') goto die;
	pos++; //Skip " (the same as above)
	if (ExtraParametersCount)
	{
		if (line[pos]!=' ') goto die;
		pos++;
	}
	for (i = 0; i < ExtraParametersCount; i++)
	{
		oB->objectFields[i + ObjectFieldsCount] = ParseInt( pos );
	}
	if (line[pos]!='O' || line[pos+1]!='B' || line.GetLength()!=pos+2)
	{
		goto die;
	}
	//let the object realize it has no future (in case of null objects)
	if (oB->ReadyToDie(ObjectFieldsCount+ExtraParametersCount))
	{
die:
		delete oB;
		oB = NULL;
	}
	return oB;
}

CString Decompiler::PrintPoint(Point &pO)
{
	CString tmp;

	tmp.Format("[%d.%d]",pO.x,pO.y);
	return tmp;
}

CString Decompiler::PrintRegion(int *region)
{
	CString tmp;
	int i;
	bool flag = false;

	for (i=0;i<4;i++)
	{
		if (region[i]!=-1) flag=true;
	}
	if (flag)
	{
		tmp.Format("[%d,%d,%d,%d]",region[0],region[1],region[2],region[3]);
	}
	return tmp;
}

int Decompiler::FindColon(const char *variable, int add)
{
	for(int i=0;i<65;i++)
	{
		if (variable[i]=='\0') return i;
		if (variable[i]==':') return i+add;
	}
  if (add) {
    return 65;
  }
	return 0;
}

int Decompiler::IsAreaUsed(compiler_data &value, int nextpar)
{
	if (value.parnum<=nextpar) return 0;
	if (value.parameters[nextpar].type==SPT_AREA1) return 6;
	if (value.parameters[nextpar].type==SPT_AREA2) return 6;
	//
	return 0;
}

int Decompiler::guess_paramcount(Trigger *tR)
{
	int ret;

	if (tR->int2Parameter) ret = 3;
	else if(tR->int1Parameter) ret = 2;
	else if(tR->int0Parameter) ret = 1;
	else ret = 0;
	if (tR->string1Parameter[0]) ret+=0x20;
	else if(tR->string0Parameter[0]) ret+=0x10;
	if (!tR->pointParameter.isnull()) ret++;
	if (tR->objectParameter) ret++;
	return ret;
}

CString Decompiler::PrintTrigger(Trigger *tR)
{
	CString tmp, key, ob, params, comment;
	compiler_data value;
	int i;
	int areaused;

	i = guess_paramcount(tR);
  if (!trigger_data.ReverseLookup(tR->triggerID, i, key, value))
  {
    if (!trigger_data.ReverseLookup(tR->triggerID, key, value))
    {
      value.keyword.Format("unknown_%d",tR->triggerID);
    }
  }
	for (i=0;i<value.parnum;i++)
	{
		if (i) params = params+",";
		switch(value.parameters[i].type)
		{
		case SPT_ACTION:   //actionoverride in trigger, no way!
		case SPT_OVERRIDE: //actionoverrided object
		case SPT_SECONDOB: //second object in trigger
		case SPT_STRREF:   //no strref in triggers
		case SPT_STRREF2:
		case SPT_STRREF3:
			ob ="invalid";
			break;
		case '0':  //null argument, simply skip it (not in the original compiler)
			break;
		default:
			ob = "internal_error";
			break;
		case SPT_POINT:
			ob = PrintPoint(tR->pointParameter);
			break;
		case SPT_INTEGER:
			//lookup IDS, or simple number
			ob=IDSToken(value.parameters[i].idsfile, tR->int0Parameter, false);
			if (ob.IsEmpty()) ob.Format("%d",tR->int0Parameter);
			ob.MakeUpper();
			break;
		case SPT_INTEGER2:
			ob=IDSToken(value.parameters[i].idsfile, tR->int1Parameter, false);
			if (ob.IsEmpty()) ob.Format("%d",tR->int1Parameter);
			ob.MakeUpper();
			break;
		case SPT_INTEGER3:
			ob=IDSToken(value.parameters[i].idsfile, tR->int2Parameter, false);
			if (ob.IsEmpty()) ob.Format("%d",tR->int2Parameter);
			ob.MakeUpper();
			break;
		case SPT_DEAD1:
		case SPT_VAR1:
			areaused = IsAreaUsed(value,i+1);
			ob.Format("\"%s\"",CString(tR->string0Parameter+areaused) );
			break;
		case SPT_COLUMN1:
			ob.Format("\"%s\"",CString(tR->string0Parameter) );
			break;
		case SPT_TOKEN1:
			ob.Format("\"%s\"",CString(tR->string0Parameter) );
			break;
		case SPT_RESREF1: //resource
			ob.Format("\"%s\"",CString(tR->string0Parameter) );
			ob.MakeUpper();
			break;
    case SPT_1AREA:
		case SPT_AREA1: //area (only for var1)
			ob.Format("\"%s\"",CString(tR->string0Parameter,6) );
			ob.MakeUpper();
			break;
		case SPT_VAR4:
			areaused = FindColon(tR->string1Parameter, 1);
			ob.Format("\"%s\"",CString(tR->string1Parameter+areaused) );
			break;
		case SPT_VAR3:
			areaused = FindColon(tR->string0Parameter, 1);
			ob.Format("\"%s\"",CString(tR->string0Parameter+areaused) );
			break;
		case SPT_AREA4:
			areaused = FindColon(tR->string1Parameter, 0);
			ob.Format("\"%s\"",CString(tR->string1Parameter,areaused) );
			break;
		case SPT_AREA3:
			areaused = FindColon(tR->string0Parameter, 0);
			ob.Format("\"%s\"",CString(tR->string0Parameter,areaused) );
			break;
		case SPT_VAR2:
			areaused = IsAreaUsed(value,i+1);
			ob.Format("\"%s\"",CString(tR->string1Parameter+areaused) );
			break;
		case SPT_COLUMN2:
			ob.Format("\"%s\"",CString(tR->string1Parameter) );
			break;
		case SPT_TOKEN2:
			ob.Format("\"%s\"",CString(tR->string1Parameter) );
			break;
		case SPT_RESREF2: //resource
			ob.Format("\"%s\"",CString(tR->string1Parameter) );
			ob.MakeUpper();
			break;
    case SPT_2AREA:
		case SPT_AREA2: //area (only for var2)
			ob.Format("\"%s\"",CString(tR->string1Parameter,6) );
			ob.MakeUpper();
			break;
		case SPT_OBJECT: //only O exists in triggers
			if (tR->objectParameter)
			{
				ob = PrintObject(tR->objectParameter);
			}
			else
			{
				tmp = IDSToken("ea", 0, false);
				if (tmp.IsEmpty()) tmp="0";
				ob.Format("[%s]", tmp);
			}
			break;
		}
		params=params+ob;
	}
	//value.keyword.SetAt(0,(char) toupper(key[0]) );
	if (tR->flags&NEGATE_TRIGGER)
	{
    tmp.Format("  !%s(%s)",value.keyword, params);
	}
  else
  {
    tmp.Format("  %s(%s)",value.keyword, params);
  }
	
	if (num_or)
	{
		tmp=CString("  ")+tmp;
		num_or--;
	}
	if((tR->triggerID&0x3fff)==TR_OR)
	{
		num_or=tR->int0Parameter;
	}

	if (comment.GetLength())
	{
		tmp=tmp+"   // "+comment;
	}
	return tmp+"\r\n";
}

int Decompiler::ReadTrigger()
{
	int pos;
	CString tmp;

	if (strncmp( line, "TR", 2 ) != 0)
	{
		return 1;
	}
	ReadLine( 1024 );
	Trigger* tR = new Trigger();
	//this exists only in PST, but not always?
	if (HasTriggerPoint)
	{
		pos=sscanf( line, "%hu %d %d %d %d [%hd,%hd] \"%[^\"]\" \"%[^\"]\" OB",
			&tR->triggerID, &tR->int0Parameter, &tR->flags,
			&tR->int1Parameter, &tR->int2Parameter, &tR->pointParameter.x,
			&tR->pointParameter.y, tR->string0Parameter, tR->string1Parameter );
	}
	else
	{
		pos=0;
	}
	if (pos<7)
	{
		sscanf( line, "%hu %d %d %d %d \"%[^\"]\" \"%[^\"]\" OB",
			&tR->triggerID, &tR->int0Parameter, &tR->flags,
			&tR->int1Parameter, &tR->int2Parameter, tR->string0Parameter,
			tR->string1Parameter );
	}
	ReadLine( 1024 );
	tR->objectParameter = DecodeObject(pos);
	ReadLine( 10 );
	if (strncmp( line, "TR", 2 ) != 0)
	{
		delete tR;
		return 1;
	}

	//
	//write trigger
	//
	tmp = PrintTrigger(tR);
	WriteString(tmp);
	delete tR;
	return 0;
}

int Decompiler::ReadCondition()
{
	ReadLine( 10 );
	if (strncmp( line, "CO", 2 ) != 0)
	{
		return 1;
	}
	WriteString("IF\r\n");
	num_or = 0;
	while (true)
	{
		ReadLine( 10 );
		if (strncmp( line, "CO", 2 ) == 0)
		{
			break;
		}
		if(ReadTrigger( )) {
			return 1;
		}
	}
	return 0;
}

int Decompiler::guess_paramcount(Action *aC)
{
	int ret;

	if (aC->int2Parameter) ret = 3;
	else if(aC->int1Parameter) ret = 2;
	else if(aC->int0Parameter) ret = 1;
	else ret = 0;
	if (aC->string1Parameter[0]) ret+=0x20;
	else if(aC->string0Parameter[0]) ret+=0x10;
	if (!aC->pointParameter.isnull()) ret++;
	if (aC->objects[2]) ret+=2;
	else if(aC->objects[1]) ret++;
	return ret;
}

CString Decompiler::GetComment(int strref)
{
	CString tmp, comment;

	if (!(weiduflg&WEI_TEXT)) goto end_of_comment;
	if (weiduflg&WEI_NOCOM) goto end_of_comment;
	comment.Format("~%s~", resolve_tlk_text(strref, 0) );
	comment.Replace("\r\n","");
	if (comment.GetLength()>50) comment=comment.Left(50)+"...";
	tmp = resolve_tlk_soundref(strref, 0);
	if (tmp.GetLength()) comment+=CString("  [")+tmp+"]";

end_of_comment:
	return comment;
}

CString Decompiler::PrintAction(Action *aC)
{
	CString tmp, key, ob, params, comment;
	compiler_data value;
	int i;
	int areaused;

  //strings mean +0x10
	i = guess_paramcount(aC);
	if (!action_data.ReverseLookup(aC->actionID, i, key, value))
	{
		if (!action_data.ReverseLookup(aC->actionID, key, value))
		{
			value.keyword.Format("unknown_%d",aC->actionID);
		}
	}
	for (i=0;i<value.parnum;i++)
	{
		if (i) params = params+",";
		switch(value.parameters[i].type)
		{
		case 0:  //null argument
			break;
		default:
			ob = "invalid";
			break;
		case SPT_ACTION:
			ob = "invalid";
			break;
		case SPT_INTEGER:
			ob=IDSToken(value.parameters[i].idsfile, aC->int0Parameter, false);
			if (ob.IsEmpty()) ob.Format("%d",aC->int0Parameter);
			ob.MakeUpper();
			break;
		case SPT_STRREF:
			ob.Format("%d",aC->int0Parameter);
			comment = GetComment(aC->int0Parameter);
			break;
		case SPT_INTEGER2:
			ob=IDSToken(value.parameters[i].idsfile, aC->int1Parameter, false);
			if (ob.IsEmpty()) ob.Format("%d",aC->int1Parameter);
			ob.MakeUpper();
			break;
		case SPT_STRREF2:
			ob.Format("%d",aC->int1Parameter);
			comment = GetComment(aC->int1Parameter);
			break;
		case SPT_INTEGER3:
			ob=IDSToken(value.parameters[i].idsfile, aC->int2Parameter, false);
			if (ob.IsEmpty()) ob.Format("%d",aC->int2Parameter);
			ob.MakeUpper();
			break;
		case SPT_STRREF3:
			ob.Format("%d",aC->int2Parameter);
			comment = GetComment(aC->int2Parameter);
			break;
		case SPT_VAR1:
			areaused = IsAreaUsed(value,i+1);
			ob.Format("\"%s\"",CString(aC->string0Parameter+areaused) );
			break;
		case SPT_COLUMN1:
			ob.Format("\"%s\"",CString(aC->string0Parameter) );
			break;
		case SPT_NUMLIST1:
			ob.Format("\"%s\"",CString(aC->string0Parameter) );
			break;
		case SPT_TOKEN1:
			ob.Format("\"%s\"",CString(aC->string0Parameter) );
			break;
		case SPT_RESREF1: //resource
			ob.Format("\"%s\"",CString(aC->string0Parameter) );
			ob.MakeUpper();
			break;
    case SPT_1AREA:
		case SPT_AREA1: //area (only for var1)
			ob.Format("\"%s\"",CString(aC->string0Parameter,6) );
			ob.MakeUpper();
			break;
		case SPT_VAR2:
			areaused = IsAreaUsed(value,i+1);
			ob.Format("\"%s\"",CString(aC->string1Parameter+areaused) );
			break;
		case SPT_VAR4:
    case SPT_RES4:
			areaused = FindColon(aC->string1Parameter, 1);
			ob.Format("\"%s\"",CString(aC->string1Parameter+areaused) );
			break;
		case SPT_VAR3:
    case SPT_RES2:
			areaused = FindColon(aC->string0Parameter, 1);
			ob.Format("\"%s\"",CString(aC->string0Parameter+areaused) );
			break;
		case SPT_AREA4:
    case SPT_RES3:
			areaused = FindColon(aC->string1Parameter, 0);
			ob.Format("\"%s\"",CString(aC->string1Parameter,areaused) );
			break;
		case SPT_AREA3:
    case SPT_RES1:
			areaused = FindColon(aC->string0Parameter, 0);
			ob.Format("\"%s\"",CString(aC->string0Parameter,areaused) );
			break;
		case SPT_COLUMN2:
			ob.Format("\"%s\"",CString(aC->string1Parameter) );
			break;
		case SPT_NUMLIST2:
			ob.Format("\"%s\"",CString(aC->string1Parameter) );
			break;
		case SPT_TOKEN2:
			ob.Format("\"%s\"",CString(aC->string1Parameter) );
			break;
		case SPT_RESREF2: //resource
			ob.Format("\"%s\"",CString(aC->string1Parameter) );
			ob.MakeUpper();
			break;
    case SPT_2AREA:
		case SPT_AREA2:
			ob.Format("\"%s\"",CString(aC->string1Parameter,6) );
			ob.MakeUpper();
			break;
		case SPT_POINT: //only one point allowed
			ob = PrintPoint(aC->pointParameter);
			break;
		case SPT_SECONDOB: //param2 object (3. place)
			if (aC->objects[2])
			{
				ob = PrintObject(aC->objects[2]);
			}
			else
			{
				ob = "anything";
			}
			break;
		case SPT_OBJECT: //regular param1 object (2. place)
			if (aC->objects[1])
			{
				ob = PrintObject(aC->objects[1]);
			}
			else
			{
				ob = "anything";
			}
			break;
		case SPT_OVERRIDE: //actionoverride object (1. place)
			ob = "invalid";
			break;
		}
		params=params+ob;
	}
	//value.keyword.SetAt(0,(char) toupper(key[0]) );
	tmp.Format("%s(%s)",value.keyword, params);
	if (aC->objects[0])
	{
			ob.Format("ActionOverride(%s,%s)",PrintObject(aC->objects[0]), tmp );
			tmp=ob;
	}
	if (comment.GetLength())
	{
		tmp=tmp+"   // "+comment;
	}
	return CString("    ")+tmp+"\r\n";
}

int Decompiler::ReadResponse()
{
	CString tmp;
	int pos;
	char *poi;

	if (strncmp( line, "RE", 2 ) != 0)
	{
		return 1;
	}
	ReadLine( 10 );
	int weight = (int) strtoul(line,&poi,10);

  //empty response block
  if (strncmp(poi,"RE",2)==0)
  {
  	tmp.Format("  RESPONSE #%d         //empty response block!!!\r\n",weight);
	  WriteString(tmp);
    return 0;
  }

	if (strncmp(poi,"AC",2)!=0)
	{
		return 1;
	}

	tmp.Format("  RESPONSE #%d\r\n",weight);
	WriteString(tmp);

	while (true)
	{
		ReadLine( 10 );
		int actionid = strtoul(line, &poi,10);
		Action* aC = new Action;

		for (int i = 0; i < 3; i++)
		{
			if (strncmp(poi,"OB",2)!=0)
			{
				delete aC;
				return 1;
			}
			ReadLine( 1024 );
			aC->objects[i] = DecodeObject(pos);
			ReadLine( 1024 );
			poi=(char *) (const char*) line;
		}
		aC->actionID = (unsigned short) actionid;

		sscanf( line, "%d %hd %hd %d %d\"%[^\"]\" \"%[^\"]\" AC",
			&aC->int0Parameter, &aC->pointParameter.x, &aC->pointParameter.y,
			&aC->int1Parameter, &aC->int2Parameter, aC->string0Parameter,
			aC->string1Parameter );

		//
		//print action
		//
		tmp = PrintAction(aC);
		WriteString(tmp);

		delete aC;

		ReadLine( 10 );
		if (strncmp( line, "RE", 2 ) == 0)
		{
			break;
		}
		if (strncmp( line, "AC", 2 ) != 0)
		{
			return 1;
		}
	}
	return 0;
}

int Decompiler::ReadResponseSet()
{
	ReadLine( 10 );
	if (strncmp( line, "RS", 2 ) != 0)
	{
		return 1;
	}
	while (true)
	{
		ReadLine (10 );
		if (strncmp (line, "RS",2) == 0)
		{
			break;
		}
		if (ReadResponse())
		{
			return 1;
		}
	}
	return 0;
}

int Decompiler::ReadResponseBlock()
{
	ReadLine( 10 );
	if (strncmp( line, "CR", 2 ) != 0)
	{
		return 1;
	}
	if (ReadCondition( ))
	{
		return 1;
	}
	WriteString("THEN\r\n");
	if (ReadResponseSet())
	{
		return 1;
	}
	WriteString("END\r\n\r\n");
	ReadLine( 10 );
	if (strncmp( line, "CR", 2 ) != 0)
	{
		return 1;
	}
	return 0;
}

bool Decompiler::Decompile(CString filepath, CString output)
{
	bpoi = 0;
	valid = 0;
	if (logging && !flog)
	{
		unlink(WEIDU_LOG); //removing the logfile
		flog = open(WEIDU_LOG, O_RDWR|O_CREAT|O_TRUNC, S_IWRITE|S_IREAD);
		if (flog<0) flog = 0;
	}

	if (fhandle) abort();

	fhandle=get_script_handle(filepath);
	if (fhandle<=0)
	{
		return true;
	}

	if (fout) abort();

	fout = open(output, O_RDWR|O_CREAT|O_TRUNC, S_IWRITE|S_IREAD);
	if (fout<=0)
	{
		return true;
	}
	//ok we have all the files now
	
	ReadLine( 10 );
	if (strncmp( line, "SC", 2 ) != 0)
	{
		return true;
	}

	while (true)
	{
		if (ReadResponseBlock( ))
			break;
	}
	if (strncmp( line, "SC", 2 ) != 0)
	{
		return true;
	}
	//ok
	return false;
}
