//
//  PlugIn name: IDA to SoftIce converter (only for 32bit programs)
//
//  Version: 0.01 prerelease
//
//
//  This file contains function to create SYMD.
//
//
#include "i2s.h"

#ifdef IDA55UP
 #define getSize get_item_size
#endif

//--------------------------------------------------------------------------
//NOTICE!
//Next two inline functions should be used together, because I created them just to have nicer source code.
//
//1st SYMD_SetProcedure
//2nd SYMD_SetVLP_Procedure
//
//This function sets/updates VLP structure for the procedure.
//
// procedure - Procedure structure. (check funcs.hpp)
//
inline void SetSoftIce::SYMD_SetVLP_Procedure(func_t* procedure)
{
	SEGDATA_VLPTABLE*	segVLP;
	segVLP=	symd.segmentCurrent->VLPCurrent;
	unsigned long*		VLPOffset;

#ifdef IDA55UP
	char	procedureName[MAXSTR];
#endif

	//Set
	segVLP->ten=0x10;
	segVLP->offVLP=(procedure->startEA)-minEA;
	if ( 0!=((procedure->flags)&FUNC_FAR) )
	{	//GLOBAL FUNCTION
		segVLP->tableType=TT_GLOBALP;
		segVLP->type=NMTP_SetProcedure(procedure);
	}
	else
	{	//LOCAL FUNCTION
		segVLP->tableType=TT_LOCALP;
		segVLP->type=0x1002;
	}
#ifdef IDA55UP
	segVLP->iNameVLP=SetStringTable( get_func_name(procedure->startEA, procedureName, MAXSTR) );
#else
	segVLP->iNameVLP=SetStringTable( get_func_name(procedure->startEA) );
#endif

	//Update
	++(symd.segmentCurrent->VLPCurrent);//to next struct
	VLPOffset=symd.segmentCurrent->VLPOffsetsCurrent;
	*(VLPOffset + 1)=*(VLPOffset)+sizeof(SEGDATA_VLPTABLE);
	++symd.segmentCurrent->VLPOffsetsCurrent;
	symd.segmentCurrent->freeVLP-=sizeof(SEGDATA_VLPTABLE);
	--symd.segmentCurrent->freeVLPOffsets;

	//Increase number of VLPs
	++symd.segmentCurrent->cVLP;
}

//This function sets/updates procedure definition structure.
//
// procedure - Procedure structure. (check funcs.hpp)
//
inline void SetSoftIce::SYMD_SetProcedureStruct(func_t* procedure, unsigned long lVarCount)
{
	SEGDATA_PROCEDURE*	segProcedure;
	segProcedure=symd.segmentCurrent->procDefinitionsCurrent;

	//Set
	segProcedure->offStartProc=(procedure->startEA)-minEA;
	segProcedure->offEndProc=(procedure->endEA)-minEA-1;
	segProcedure->offLocalVariables=symd.segmentCurrent->offsetLVarCurrent;
	segProcedure->cLocalVariables=lVarCount;  //both arguments and lVars
	segProcedure->M1=-1;

	//Update
		//ProcDef.
	++(symd.segmentCurrent->procDefinitionsCurrent);
	symd.segmentCurrent->freeProcDefinitions-=sizeof(SEGDATA_PROCEDURE);
	++(symd.segmentCurrent->cProcDefinitions);

}

//--------------------------------------------------------------------------
//SetProcedure fills all needed structures of SYMD section for code segment.
//It sets procedure definition, local variables(arguments too), local labels and
//source lines(this version doesn't support source codes so this lines are useless).
//
// procedure - Procedure structure. (check funcs.hpp)
//
// return: error (FAILED_ , SUCCESS_)
//
int SetSoftIce::SYMD_SetProcedure(func_t* procedure)
{
	struc_t*		frame;

	unsigned long	lVarCount;
	unsigned long	lVarSize;
	unsigned long	argCount;
	unsigned long	argOffsetFirst;

	unsigned long	localCount;
	unsigned long	localOffset;
	char			localName[MAXSTR];
	member_t*		localVariable;

	ea_t			currentAddress;
	unsigned long	cSourceLines=0;		//number of surce lines in procedure

	unsigned long	cLabels=0xffffffff;	//-1 -> we will always have one name in the function and
										//that's the procedure's name and we don't want that as a label
	SEGDATA_VLPTABLE*		segVLP;
	SEGDATA_SOURCELINES*	segSourceLines;
	unsigned long*			segVLPOffsets;

#ifdef IDA55UP
	char name[MAXSTR];
#endif


//Get number of local variables.
//currently only EBP based functions are supported. I tryed but ESP based funcitons have their own mind.
//Both Get..LVar or Arg count functions work with ESP too.
//	if ( -1==(procedure->frame) )
	if ( 0==((procedure->flags)&FUNC_FRAME) )
	{
		argCount=0;
		lVarCount=0;
		localCount=0;
	}
	else
	{
		//Get number of arguments+lVars => in SYMD they are both defined as procedure local variables
		argCount=GetProcedureArgCount(procedure, &argOffsetFirst);
		lVarCount=GetProcedureLVarCount(procedure);
		localCount=argCount+lVarCount;
	}

//Get number of lines in procedure
	currentAddress=(procedure->startEA);
	while ( currentAddress<(procedure->endEA) )
	{
		if ( has_any_name(getFlags(currentAddress)) ) ++cLabels;

		++cSourceLines;
		currentAddress+=getSize(currentAddress);
	}


//Check if we have enough memory.
	if ( FAILED_==CheckMemory( sizeof(SEGDATA_PROCEDURE), MEMORY_SYMD_PROCEDURE) ) return(FAILED_);
	if ( FAILED_==CheckMemory( localCount*sizeof(SEGDATA_VLPTABLE), MEMORY_SYMD_LOCAL) ) return(FAILED_);//local variables
	if ( FAILED_==CheckMemory( (cLabels + 1)*sizeof(SEGDATA_VLPTABLE), MEMORY_SYMD_VLP, (cLabels + 1) ) ) return(FAILED_);
	                            //number of labels + 1 (for procedure)
	if ( FAILED_==CheckMemory( cSourceLines*sizeof(SEGDATA_SOURCELINES), MEMORY_SYMD_SOURCELINES) ) return(FAILED_);


//Set procedure => procDef and VLP def
	SYMD_SetProcedureStruct(procedure, localCount);
	SYMD_SetVLP_Procedure(procedure);

//Set procedure local Variable definition
	if ( 0!=localCount )
	{
		auto SEGDATA_VLPTABLE*	segLocal;
		segLocal=symd.segmentCurrent->localVariablesCurrent;

		//set local variables (both arguments and 'real' local variables)
			//update
		symd.segmentCurrent->offsetLVarCurrent+=localCount*sizeof(SEGDATA_VLPTABLE);
		symd.segmentCurrent->freeLocalVariables-=localCount*sizeof(SEGDATA_VLPTABLE);
		symd.segmentCurrent->cLocalVariables+=localCount;

		frame=get_frame(procedure);
		localOffset=get_struc_first_offset(frame);

		if ( 0!=((procedure->flags)&FUNC_FRAME) )
		{
			lVarSize=(procedure->frsize);	//EBP
		}
		else
		{
			lVarSize=0;		//ESP
		}

			//set 'real' local variables
		while (0!=lVarCount)
		{
			localVariable=get_member(frame, localOffset);
			while ( 0==localVariable )	//because of the nameless bytes
			{
				localOffset=get_struc_next_offset(frame,localOffset);	//in here BADADDR can't happen
				localVariable=get_member(frame, localOffset);
			}

			strcpy( localName, get_member_name(localVariable->id) );

			segLocal->ten=0x10;
			segLocal->tableType=TT_LOCALV;
			segLocal->type=GetType_member(localVariable);
			segLocal->iNameVLP=SetStringTable(localName);
			segLocal->offVLP=(localVariable->get_soff())-lVarSize;

			++segLocal;
			//set struct members as separate localVariables
			if ( FF_STRU==((localVariable->flag)&DT_TYPE) )
			{
				segLocal=SetLocalStructMembers(get_sptr(localVariable), localName, ((localVariable->get_soff())-lVarSize), segLocal);
				if ( segLocal==FAILED_ ) return(FAILED_);
				lVarCount-=GetStructureMemberCount(get_sptr(localVariable));
			}
			--lVarCount;
			localOffset=get_struc_next_offset(frame,localOffset);
		}
			//now we have offset to pushed regs, return address and arguments

			//set arguments
		localOffset=argOffsetFirst;
		while (0!=argCount)
		{
			localVariable=get_member(frame, localOffset);
			while ( 0==localVariable )	//because of the nameless bytes
			{
				localOffset=get_struc_next_offset(frame,localOffset);	//in here BADADDR can't happen
				localVariable=get_member(frame, localOffset);
			}
			strcpy( localName, get_member_name(localVariable->id) );

			segLocal->ten=0x10;
			segLocal->tableType=TT_LOCALV;
			segLocal->type=GetType_member(localVariable);
			segLocal->iNameVLP=SetStringTable(localName);
			segLocal->offVLP=(localVariable->get_soff())-lVarSize;

			++segLocal;
			--argCount;

			localOffset=get_struc_next_offset(frame,localOffset);
		}
		symd.segmentCurrent->localVariablesCurrent=segLocal;
	}

//Set local lables
	segVLP=symd.segmentCurrent->VLPCurrent;
	segSourceLines=symd.segmentCurrent->sourceLinesCurrent;
	segVLPOffsets=symd.segmentCurrent->VLPOffsetsCurrent;

	currentAddress=procedure->startEA;

	//This first source line is here because it has a name (function name) and we don't that as a Label.
	segSourceLines->offInCode=currentAddress-minEA;
	segSourceLines->lineNumber=lineNumberCurrent;

	++segSourceLines;
	++lineNumberCurrent;
	currentAddress+=getSize(currentAddress);

	while ( currentAddress<(procedure->endEA) )
	{
		//labels
		if ( has_any_name(getFlags(currentAddress)) )
		{
			segVLP->ten=0x10;
			segVLP->tableType=TT_LABEL;
			segVLP->type=0x1000;
	#ifdef IDA55UP
			segVLP->iNameVLP=SetStringTable(get_name(0, currentAddress, name, MAXSTR));
	#else
			segVLP->iNameVLP=SetStringTable(get_name(0, currentAddress));
	#endif
			segVLP->offVLP=currentAddress-minEA;

			++segVLP;
			*(segVLPOffsets+1)=*(segVLPOffsets)+sizeof(SEGDATA_VLPTABLE);
			++segVLPOffsets;
		}

		//source
		segSourceLines->offInCode=currentAddress-minEA;
		segSourceLines->lineNumber=lineNumberCurrent;

		++segSourceLines;
		++lineNumberCurrent;
		currentAddress+=getSize(currentAddress);
	}
	//Update
		//source
	symd.segmentCurrent->sourceLinesCurrent=segSourceLines;
	symd.segmentCurrent->freeSourceLines-=cSourceLines*sizeof(SEGDATA_SOURCELINES);
	symd.segmentCurrent->cSourceLines+=cSourceLines;
		//labels
	symd.segmentCurrent->VLPCurrent=segVLP;
	symd.segmentCurrent->cVLP+=cLabels;
	symd.segmentCurrent->freeVLP-=cLabels*sizeof(SEGDATA_VLPTABLE);
	symd.segmentCurrent->freeVLPOffsets-=cLabels;
	symd.segmentCurrent->VLPOffsetsCurrent+=cLabels;

//Return
	return(SUCCESS_);
}


//--------------------------------------------------------------------------
//Sets all variables(with names) in DATA segment.
//
// data - Data segment area structure.
//
// return - error (FAILED_ / SUCCESS_)
//
//NOTICE!
//minEA must be set to starting address of the segment (minEA=area->startEA;)
//
int SetSoftIce::SYMD_SetDataVariables(segment_t* data)
{
	ea_t			currentAddress;
	char			name[MAXSTR];	//we have to copy it because of IDA internal procesing
#ifndef IDA55UP
	char*			namePointer;
#endif
	unsigned long	type;

	SEGDATA_VLPTABLE*	segVLP;
	segVLP=symd.segmentCurrent->VLPCurrent;

	unsigned long*		segVLPOffsets;
	segVLPOffsets=symd.segmentCurrent->VLPOffsetsCurrent;


	currentAddress=data->startEA;
	while( currentAddress<(data->endEA) )
	{
		if ( 0!=wasBreak() )
		{
			plugInFlags|=USERCANCELLED;
			return(FAILED_);
		}

#ifdef IDA55UP
		if ( 0!=get_name(BADADDR, currentAddress, name, MAXSTR) )
		{
#else
		if ( 0!=(namePointer=get_name(BADADDR, currentAddress)) )
		{
			strcpy(name, namePointer);
#endif
			//Set
			segVLP->ten=0x10;
			segVLP->offVLP=currentAddress-minEA;
			segVLP->tableType=TT_VARIABLE;
			if ( FAILED_==(type=GetType_ea(currentAddress)) )
			{
				jumpto(currentAddress,-1);
				if ( isAlign(getFlags(currentAddress)) )
				{
					warning("Align directive (ea:%x (segment:%s)) isn't alowed to have a name.", currentAddress, get_segm_name(data) );
				}
				else
				{
					warning("Failed to get type (ea:%x (segment:%s)).", currentAddress, get_segm_name(data));
				}
				return(FAILED_);
			}
			segVLP->type=type;
			segVLP->iNameVLP=SetStringTable(name);

			//Update
				//VLP
			++segVLP;
			symd.segmentCurrent->freeVLP-=sizeof(SEGDATA_VLPTABLE);
				//VLPOffsets
			*(segVLPOffsets + 1)=*(segVLPOffsets)+sizeof(SEGDATA_VLPTABLE);
			++segVLPOffsets;
			--symd.segmentCurrent->freeVLPOffsets;

			//Increase number of VLPs
			++symd.segmentCurrent->cVLP;

			//Test if we have enough memory
			if ( (symd.segmentCurrent->freeVLP) <= (sizeof(SEGDATA_VLPTABLE)+1) )
			{
				symd.segmentCurrent->VLPCurrent=segVLP;
				if ( FAILED_== InitializeMemory(ALLOCATEBIG, symd.segmentCurrent->hVLP, (void**)&(symd.segmentCurrent->VLP), (void**)&(symd.segmentCurrent->VLPCurrent), &(symd.segmentCurrent->freeVLP), &(symd.segmentCurrent->sizeVLP)) ) return(FAILED_);
				segVLP=symd.segmentCurrent->VLPCurrent;
			}
			if ( (symd.segmentCurrent->freeVLPOffsets) == 0 )
			{
				symd.segmentCurrent->VLPOffsetsCurrent=segVLPOffsets;
				if ( FAILED_== InitializeMemory(ALLOCATESMALL, symd.segmentCurrent->hVLPOffsets, (void**)&(symd.segmentCurrent->VLPOffsets), (void**)&(symd.segmentCurrent->VLPOffsetsCurrent), &(symd.segmentCurrent->freeVLPOffsets), &(symd.segmentCurrent->sizeVLPOffsets)) ) return(FAILED_);
				segVLPOffsets=symd.segmentCurrent->VLPOffsetsCurrent;
			}
		}
		currentAddress+=getSize(currentAddress);   //get size of the operand or whatever is on that address	
	}
	symd.segmentCurrent->VLPCurrent=segVLP;

	return(SUCCESS_);
}

