//
//  PlugIn name: IDA to SoftIce converter (only for 32bit programs)
//
//  Version: 0.01 prerelease
//
//
//  This file contains function that deal with types (set/get).
//
//
#include "i2s.h"


//--------------------------------------------------------------------------
//This function updates/sets all variables common to NM32_xxxx functions.
//
// size - size of nm32_type_xxxx struct.
//
// return - type index.
//
unsigned long SetSoftIce::NMTP_UpdateNMTPStruct( unsigned long size )
{
	unsigned long	index;

	*nmtp.typesOffsetsCurrent=nmtp.typesDataCurrent-nmtp.typesData;		//set offset in typesData
	++nmtp.typesOffsetsCurrent;											//update pointer
	--nmtp.freeTypesOffsets;											//decrease size

	nmtp.typesDataCurrent+=size;					//update current pointer
	nmtp.freeTypesData-=size;						//decrease free size

	//index
	index=currentIndex;				//get current index
	++currentIndex;					//prepare index for next type
	
	//increase number of types
	++nmtp.cTypesData;

	return(index);
}


//--------------------------------------------------------------------------
//The SeArray function currently sets one dimension nm32 array type.
//
//	elemType - Primitive type. Currently it must be T_CHAR.
//  lenght - Lenght of one dimension array in bytes.
//
//  return - type index.
//
unsigned long SetSoftIce::NMTP_SetArray(unsigned long elemType, unsigned long length)
{
	DATAS_LF_ARRAY*		typeData;
	unsigned long		index;


// Test if we have same array already in our buffer.
	for (unsigned long i=0; i<nmtp.cTypesData; i++)
	{
		typeData=(DATAS_LF_ARRAY*)(nmtp.typesData+nmtp.typesOffsets[i]);
		if ( TYPE_LF_ARRAY==(typeData->lf_index) )
		{
			if (typeData->length==length)
				if (typeData->elemType==elemType) return(0x1000+i);
		}
	}

// Check if we have enough memory.
	if ( FAILED_==CheckMemory(sizeof(DATAS_LF_ARRAY), MEMORY_NMTP) ) return(FAILED_);
	
// Set_Update
	typeData=(DATAS_LF_ARRAY*)nmtp.typesDataCurrent;
	index=NMTP_UpdateNMTPStruct( sizeof(DATAS_LF_ARRAY) );

// Set LF_ARRAY
	typeData->lf_index=TYPE_LF_ARRAY;
	typeData->elemType=elemType;
	typeData->length=length;
	typeData->length2=length;
	typeData->indexType=TP_ULONG;	//for 32bit programs
	return(index);
}

//--------------------------------------------------------------------------
//The SetArglist function sets types of arguments passed to the procedure.
//
// frame - Frame of the procedure. ( struc_t *get_frame(func_t *pfn) )
// argCount - Number of arguments passed to procedure. (look in NMTP_SetProcedure)
// argOffsetFirst - Offset in frame to first argument.
//                  You get this if you call GetProcedureArgCount.
//
// return - type index or FAILED_.
//
unsigned long SetSoftIce::NMTP_SetArglist(struc_t* frame, unsigned long argCount, unsigned long	argOffset)
{
	unsigned long*	typeData;		//pointer to derived "struct" in typesData
	unsigned long	index;

	unsigned long	arglistLength;
	member_t*		argument;


//Get length
	arglistLength=(argCount+3)*4;

//Check if we have enough memory.
	if ( FAILED_==CheckMemory(arglistLength, MEMORY_NMTP) ) return(FAILED_);

//Set/Update
	typeData=(unsigned long*)nmtp.typesDataCurrent;
	index=NMTP_UpdateNMTPStruct( arglistLength );

//set DATAS_LF_ARGLIST
	*typeData=TYPE_LF_ARGLIST;
	*(typeData+2)=argCount;
	typeData=typeData+3;		//to first "member" type

	//set types
	while( 0!=argCount )
	{
		argument=get_member(frame, argOffset);
		while ( 0==argument)	//this means that user undefined function argument
		{
			if ( BADADDR==(argOffset=get_struc_next_offset(frame,argOffset)) ) goto END;
			argument=get_member(frame, argOffset);
		}

		*typeData=GetType_flag( (flags_t)(argument->flag&DT_TYPE) );
			
		++typeData;
		--argCount;

		argOffset=get_struc_next_offset(frame,argOffset);
	}
END:
	return(index);
}

//--------------------------------------------------------------------------
//The SetProcedure function sets global function type.
//
// procedure - Function struct. (check funcs.hpp)
//
// return - type index.
//
// Notice!
// Local procedures "don't" need this type set(well they have it 0x1002 (argCount=0)).
// Their arguments are taken in account in SYMD section as local variables (with EBP+offset).
//
unsigned long SetSoftIce::NMTP_SetProcedure(func_t* procedure) 
{
	DATAS_LF_PROCEDURE*	typeData;
	unsigned long		index;

	struc_t*		frame;

	unsigned long	argCount;
	unsigned long	argOffset;
	unsigned long	argOffsetFirst;
	member_t*		argument;

	unsigned long*	arglistType;	//pointer
	unsigned long	type;			//value

	char			functionName[MAXSTR];

	unsigned long	j;	//counter (number of types checked)


//Test if we have same procedure already in our buffer.
	//Get number of arguments in procedure
	argCount=GetProcedureArgCount(procedure, &argOffsetFirst);

//Test if procedure has any arguments at all
//	if ( 0==(procedure->argsize)) return(0x1002);	//this one doesn't work on STDCALLs
	if ( 0==argCount ) return(0x1002);		//this one works as GetProcedureArgCount can handle STDCALLs

	
	argOffset=argOffsetFirst;

	frame=get_frame(procedure);

	//check for same procedure
	for (unsigned long i=0; i<nmtp.cTypesData; i++)
	{
		typeData=(DATAS_LF_PROCEDURE*)(nmtp.typesData+nmtp.typesOffsets[i]);
		if ( TYPE_LF_PROCEDURE==(typeData->lf_index) )
		{
			if ( argCount==(typeData->argCount) )   //we have procedure with same number of arguments
			{										//now check if the argument types are the same
				arglistType=(unsigned long*)((unsigned char*)typeData+sizeof(DATAS_LF_PROCEDURE)+3*4); //to first type
				//DATAS_LF_ARGLIST is always right after DATAS_LF_PROCEDURE.

				j=0;	//reset counter (number of types checked)
				while (1)
				{
					argument=get_member(frame, argOffset);
					while ( 0==argument)	//this means that argument is undefined, this sometimes happen
					{					//at the end of the frame
						if ( BADADDR==(argOffset=get_struc_next_offset(frame,argOffset)) ) goto ENDTEST;
						argument=get_member(frame, argOffset);
					}

					type = (argument->flag) & DT_TYPE;
					if ( type == FF_BYTE )  //procedure type is and it must be the same in arglist
					{
						if ( *arglistType!=TP_UCHAR ) break;
					}
					else if ( type == FF_WORD )
					{
						if ( *arglistType!=TP_USHORT ) break;
					}
					else if ( type == FF_DWRD )
					{
						if ( *arglistType!=TP_ULONG ) break;
					}

					++arglistType;	//to next type
					++j;
					if ( j==argCount )	//this mean that every one was right
						return(0x1000+i);	//i = index of the procedure
					argOffset=get_struc_next_offset(frame,argOffset);
				}
			}
		}
	}
ENDTEST:

//IF WE CAME TO HERE THAN PROCEDURE ISN'T IN BUFFER YET

//Check if we have enough memory.
	if ( FAILED_==CheckMemory( sizeof(DATAS_LF_PROCEDURE), MEMORY_NMTP) ) return(FAILED_);

//Set/Update
	typeData=(DATAS_LF_PROCEDURE*)nmtp.typesDataCurrent;
	index=NMTP_UpdateNMTPStruct( sizeof(DATAS_LF_PROCEDURE) );

//set DATAS_LF_PROCEDURE
	typeData->lf_index=TYPE_LF_PROCEDURE;
	if ( 0==(typeData->argList=NMTP_SetArglist(frame, argCount, argOffsetFirst)) ) return(FAILED_);
	typeData->rType=TP_VOID;		//it's ok for all functions
	typeData->call=CALL_FAR_PASCAL;  //(arguments pushed left to right, callee pops arguments)
	typeData->argCount=argCount;

//TYTB_HSHT
#ifdef IDA55UP
	get_func_name(procedure->startEA, functionName, MAXSTR);
#else
	strcpy(functionName, get_func_name(procedure->startEA));
#endif
	if ( FAILED_ == Set_TYTB_HSHT(index, functionName) )	return(FAILED_);

	return(index);
}

//--------------------------------------------------------------------------
//The SetMemeber function sets the member index even if member is a struct type or union variable.
//
// member - Member structure. (check struct.hpp)
//
// return - type index.
//
unsigned long SetSoftIce::NMTP_SetMember(member_t* member)
{
	DATAS_LF_MEMBER*	typeData;
	unsigned long		index;


//Check if we have enough memory.
	if ( FAILED_==CheckMemory( sizeof(DATAS_LF_MEMBER), MEMORY_NMTP) ) return(FAILED_);

//Set/Update
	typeData=(DATAS_LF_MEMBER*)nmtp.typesDataCurrent;
	index=NMTP_UpdateNMTPStruct( sizeof(DATAS_LF_MEMBER) );

//set DATAS_LF_MEMBER
	typeData->lf_index=TYPE_LF_MEMBER;
	typeData->indexType=GetType_member(member);
	typeData->iName=SetStringTable(get_member_name(member->id));
	typeData->access=ACCESS_PUBLIC;
	if ( member->unimem() ) //is a member of a union?
	{
		typeData->offset=0;	//all variables in union have offset=0
	}
	else
	{
		typeData->offset=(unsigned short)member->soff; //if someone finds structure bigger than 64k let me know
	}

	return(index);
}


//--------------------------------------------------------------------------
// The SetDerived function defines all members of the structure.
//
// structure - The structure structure. ;) (check struct.hpp)
//
// return - type index.
//
unsigned long SetSoftIce::NMTP_SetDerived(struc_t* structure)
{
	unsigned long*	typeData;		//pointer to derived "struct" in typesData
	unsigned long	index;

	unsigned long	derivedLength;

	unsigned long	memberOffset;
	member_t*		member;


//Get length
	derivedLength=(structure->memqty)*4 + 3*4;

//Check if we have enough memory.
	if ( FAILED_==CheckMemory( derivedLength, MEMORY_NMTP) ) return(FAILED_);


//Set/Update
	typeData=(unsigned long*)nmtp.typesDataCurrent;					//get pointer to array struct
	index=NMTP_UpdateNMTPStruct( derivedLength );

//Set DATAS_LF_DERIVED
	*typeData=TYPE_LF_DERIVED;
	*(typeData+(unsigned long)2)=structure->memqty;
	typeData=typeData+(unsigned long)3;		//to first "member" type

	memberOffset=get_struc_first_offset(structure);
	for (unsigned long i=0;i<(structure->memqty); i++)
	{
		member=get_member(structure, memberOffset);

		*typeData=NMTP_SetMember(member);
		++typeData;

		memberOffset=get_struc_next_offset(structure,memberOffset);
	}
	return(index);
}
		
//--------------------------------------------------------------------------
//The SetStructure(Union) function sets all needed parts(DATAS_LF_STRUCT,..DERIVED,..MEMBERs)
//to define NM32 structure type.
//
// structure - The structure structure. ;) (check struct.hpp)
//
// return - type index.
//
unsigned long SetSoftIce::NMTP_SetStructure(struc_t* structure)
{
	DATAS_LF_STRUCT* typeData;		//procedure must keep pointer to it's info
	unsigned long	index;

	unsigned long	iName;
	char			structName[MAXSTR];


//Test if we have any members.
	if ( 0==(structure->memqty) )
	{
		warning("Structure(%s) doesn't have any members.", get_struc_name(structure->id));
		return(TP_NOTYPE);  //means that structure doesn't have any members
	}

//Test if we have same struct already in our buffer.
	strcpy( structName, get_struc_name(structure->id) );
	iName=SetStringTable(structName); //do we have this name already in our table
														//(Two structures can't have the same name.)
	for (unsigned long i=0; i<nmtp.cTypesData; i++)
	{
		typeData=(DATAS_LF_STRUCT*)(nmtp.typesData+nmtp.typesOffsets[i]);
		if ( TYPE_LF_STRUCT==(typeData->lf_index) )
		{
			if (typeData->iName == iName)
				return(0x1000+i);
		}
	}

//Check if we have enough memory.
	if ( FAILED_==CheckMemory( sizeof(DATAS_LF_STRUCT), MEMORY_NMTP) ) return(FAILED_);

//Set/Update
	typeData=(DATAS_LF_STRUCT*)nmtp.typesDataCurrent;
	index=NMTP_UpdateNMTPStruct( sizeof(DATAS_LF_STRUCT) );

//Set DATAS_LF_STRUCTURE
	typeData->lf_index=TYPE_LF_STRUCT;
	typeData->derivedType=NMTP_SetDerived(structure);
	typeData->iName=iName;
	typeData->property=PRO_NULL;
	typeData->cFields=structure->memqty;
	typeData->length=get_struc_size(structure);

	if ( FAILED_ == Set_TYTB_HSHT(index, structName, iName) )
		return(FAILED_);

	return(index);
}

//--------------------------------------------------------------------------
//Get type.
//
// flag - Isn't it obvious.
//
// return - type.
//

//Doesn't support all types.
//(only byte, word, dword, qword, float, double, packreal) (intended for arguments)
unsigned long SetSoftIce::GetType_flag(flags_t flag)
{
    flag=flag&DT_TYPE;
	     if ( flag == FF_BYTE     ) return(TP_UCHAR);
	else if ( flag == FF_WORD     ) return(TP_USHORT);
	else if ( flag == FF_DWRD     ) return(TP_ULONG);
	else if ( flag == FF_QWRD     ) return(TP_UQUAD);
	else if ( flag == FF_FLOAT    ) return(TP_REAL32);
	else if ( flag == FF_DOUBLE   ) return(TP_REAL64);
	else if ( flag == FF_PACKREAL ) return(TP_REAL80);		//????
	return(TP_NOTYPE);
}

//Supports all types (intended for DATA segment).
unsigned long SetSoftIce::GetType_ea(ea_t currentAddress)
{
	flags_t			flag;	//just the type flag
	flags_t			flags;		//all flags
	typeinfo_t		typeInfo;

	flags=getFlags(currentAddress);
    flag=flags&DT_TYPE;
		 if ( flag == FF_BYTE     ) return(TP_UCHAR);
	else if ( flag == FF_WORD     ) return(TP_USHORT);
	else if ( flag == FF_DWRD     ) return(TP_ULONG);
	else if ( flag == FF_QWRD     ) return(TP_UQUAD);
//	else if ( flag == FF_TBYT     ) return(  ????  );
//	else if ( flag == FF_OWRD     ) return(  ????  );
	else if ( flag == FF_ASCI     )
	{
									auto unsigned long strLenght;
									get_typeinfo(currentAddress, 0, flags, &typeInfo);
									strLenght=get_max_ascii_length(currentAddress, typeInfo.strtype, false);
									return( NMTP_SetArray(TP_UCHAR, strLenght) );
	}
	else if ( flag == FF_STRU     )
	{
									get_typeinfo(currentAddress, 0, flags, &typeInfo);
									return ( NMTP_SetStructure(get_struc(typeInfo.tid)) );
	}
	else if ( flag == FF_FLOAT    ) return(TP_REAL32);
	else if ( flag == FF_DOUBLE   ) return(TP_REAL64);
	else if ( flag == FF_PACKREAL ) return(TP_REAL80);		//????
//	else if ( flag == FF_ALIGN    )	return(  ????   );
	return(TP_NOTYPE);  
}

//supports all types (intended for argument/lVariables (procedure frame)).
unsigned long SetSoftIce::GetType_member(member_t* member)
{
	unsigned long	type;

    type=(member->flag)&DT_TYPE;
	     if ( type == FF_BYTE     ) return(TP_UCHAR);
	else if ( type == FF_WORD     ) return(TP_USHORT);
	else if ( type == FF_DWRD     ) return(TP_ULONG);
	else if ( type == FF_QWRD     ) return(TP_UQUAD);
//	else if ( type == FF_TBYT     ) return(TP_REAL80);		//wrong type, but don't know what to put here
	else if ( type == FF_ASCI     ) return( NMTP_SetArray(TP_UCHAR, (member->eoff - member->soff)) );
	else if ( type == FF_STRU     ) return( NMTP_SetStructure(get_sptr(member)) );
//	else if ( type == FF_FF_OWRD  ) return(TP_NOTYPE);		// should not happen
	else if ( type == FF_FLOAT    ) return(TP_REAL32);
	else if ( type == FF_DOUBLE   ) return(TP_REAL64);
	else if ( type == FF_PACKREAL ) return(TP_REAL80);		//???
	return(TP_NOTYPE);
}

