//
//  PlugIn name: IDA to SoftIce converter (only for 32bit programs)
//
//  Version: 0.01 prerelease
//
//
//  This file contains all other nms file sections. They are all small so hence the name.
//
//
#include "i2s.h"


//--------------------------------------------------------------------------
//This one must be called right after initializing memory.
//
//"constructor" (not really, memory must be set to use this one), but as I need return from
//ini.memory and constructor can't return value I had to do it like this.
void SetSoftIce::Set_SoftIce(void)
{
	unsigned long* typeData;

	typeData=(unsigned long*)nmtp.typesData;
	*typeData=TYPE_LF_LABEL;					//LABEL_NEAR=0
	*(typeData+2)=TYPE_LF_LABEL;
	*(typeData+3)=LABEL_FAR;
	*(typeData+4)=TYPE_LF_PROCEDURE;	//arglist=0,params=0
	*(typeData+6)=TP_VOID;
	*(typeData+7)=CALL_NEAR_PASCAL;

	nmtp.typesDataCurrent=nmtp.typesData+9*4;

													//offset1=>0
	*(nmtp.typesOffsets+1)=2*4;		//offset2=>8
	*(nmtp.typesOffsets+2)=(2+2)*4;	//offset3=>10
	nmtp.typesOffsetsCurrent=nmtp.typesOffsets+3; //->offset[4]
}


//--------------------------------------------------------------------------
//This function sets String table. (STTB)
//
// stringT - Pointer to string we want to include.
//
// retutn - index name (iName in nms32.h) or FAILED_ . (Zero index is reserved for zero string ('\x0').)
//
unsigned long SetSoftIce::SetStringTable(char* stringNew)
{
	unsigned long   strLength;
	unsigned long	i;
	char			stringFixed[MAXSTR];
	char			currentByte;


//Change all spaces with'_'
	strcpy(stringFixed, stringNew);
	i=0;
	currentByte=stringFixed[0];
	while ( currentByte!='\x0' )
	{
		currentByte=stringFixed[i];
		if ( currentByte==' ' )
			stringFixed[i]='_';
		++i;
	}

//Test if we have same string already in our buffer.
	i=0;	//it can be a null string too
	while ( i<sttb.cStrings )	//if cStrings=1->only zero string, else cStrings=2...->check
	{
		if( 0==strcmp( (sttb.strings+ *(sttb.offsets+i)), stringFixed) ) return(i);
		++i;
	}

//Get string length.
	strLength=strlen( stringFixed )+1;		//+ '\x0'
//Check if we have enough memory.
	if ( FAILED_==CheckMemory(strLength, MEMORY_STTB) ) return(FAILED_);

//Copy string.
	sttb.stringsCurrent=stpcpy(sttb.stringsCurrent, stringFixed) + 1 ;	//returns pointer to end of string(+'\x0')
	sttb.freeStrings-=strLength;

	*(sttb.offsetsCurrent+1)=*(sttb.offsetsCurrent)+strLength;
	++sttb.offsetsCurrent;

	--sttb.freeOffsets;
	++sttb.cStrings;			//increase number of strings in buffer

//return
	return(sttb.cStrings-1);
}

//--------------------------------------------------------------------------
//This function creates hash number from name.
//
// name - pointer to string.
//
// return - hash number.
//
inline unsigned long SetSoftIce::GetHashNumber(char* Name)
{
	unsigned long	hashNumber=0;

	unsigned long*	nameD=(unsigned long*)Name;
	unsigned long	temp;

	unsigned long	stringLength;
	unsigned long	counter;
	unsigned long	i;

//get string length
	stringLength=strlen(Name);

//hash main (Loop1 in nm32.h)
	counter=stringLength/4;
	if ( 0!= counter )
	{
		i=0;
		while( i<counter )
		{
			hashNumber^=(nameD[i]&0xDFDFDFDF);
			hashNumber=_lrotl( hashNumber, 4);
			++i;
		}
	}

//hash rest (loop2 in nm32.h)
	counter=stringLength&3;
	if ( 0!=counter )
	{
		temp=0;
		i=0;
		while(i<counter)
		{
			temp|=Name[stringLength-1-i];
			temp<<=8;
			++i;
		}
		temp&=0xDFDFDFDF;
		hashNumber^=temp;
	}

//return
	return(hashNumber);
}


//--------------------------------------------------------------------------
//This function gets/sets hashOffsets.
//
// hashNumber - Hash number created from a type name.
//
// return - previous offset.
//
inline unsigned long SetSoftIce::SetHashOffset(unsigned long hashNumber)
{
	unsigned long	previousOffset;
	unsigned long	index;

	unsigned long	newHashOffset;

//get index
	index=(hashNumber%0x25);

//get offset->write
	if ( 0==(previousOffset=hsht.hashOffsets[index]) )
	{
		newHashOffset=1;
	}
	else
	{
		newHashOffset=0;
	}
	newHashOffset+=(unsigned long)((char)hsht.hashTablesCurrent-(char)hsht.hashTables);
	hsht.hashOffsets[index]=newHashOffset;			//offset to current hash table

//return
	return(previousOffset);
}

//--------------------------------------------------------------------------
//This function sets Type and Hash tables;
//
// type -  Type of typeDefs, structs and global procedures.
// name - Pointer to name.
// iName - Index name. (check nm32.h)
//
// return - error (FAILED_ / SUCCESS_)
//
//NOTICE!
//If you already got iName use the first function(why? just for fun).
//
int SetSoftIce::Set_TYTB_HSHT(unsigned long	type, char* name, unsigned long	iName)
{
	unsigned long	hashNumber;

//check if we have enough memory
	if ( FAILED_==CheckMemory( sizeof(TYPETABLE), MEMORY_TYTB) ) return(FAILED_);
	if ( FAILED_==CheckMemory( sizeof(HSHT_TYPETABLE), MEMORY_HSHT) ) return(FAILED_);

	//Set TYTB
	tytb.typeTablesCurrent->type=type;
	tytb.typeTablesCurrent->iNameType=iName;

		//Update
	++tytb.typeTablesCurrent;
	tytb.freeTypeTables-=sizeof(TYPETABLE);
	++tytb.cTypeTables;

	//Set HSHT
	hashNumber=GetHashNumber(name);

	hsht.hashTablesCurrent->offPreviousTable=SetHashOffset(hashNumber);
	hsht.hashTablesCurrent->hashNumber=hashNumber;
	hsht.hashTablesCurrent->iNameType=iName;
	hsht.hashTablesCurrent->type=type;

		//Update
	++hsht.hashTablesCurrent;
	hsht.freeHashTables-=sizeof(HSHT_TYPETABLE);
	++hsht.cHashTables;

	return(SUCCESS_);
}

int SetSoftIce::Set_TYTB_HSHT(unsigned long	type, char* name)
{
	unsigned long	hashNumber;
	unsigned long	iName;


//check if we have enough memory
	if ( FAILED_==CheckMemory( sizeof(TYPETABLE), MEMORY_TYTB) ) return(FAILED_);
	if ( FAILED_==CheckMemory( sizeof(HSHT_TYPETABLE), MEMORY_HSHT) ) return(FAILED_);

	//Get iName
	iName=SetStringTable(name);

	//Set TYTB
	auto TYPETABLE*	typeTables;
	typeTables=tytb.typeTablesCurrent;
	typeTables->type=type;
	typeTables->iNameType=iName;

		//Update
	++tytb.typeTablesCurrent;
	tytb.freeTypeTables-=sizeof(TYPETABLE);
	++tytb.cTypeTables;

	//Set HSHT
	auto HSHT_TYPETABLE* hashTables;

	hashNumber=GetHashNumber(name);

	hashTables=hsht.hashTablesCurrent;
	hashTables->offPreviousTable=SetHashOffset(hashNumber);
	hashTables->hashNumber=hashNumber;
	hashTables->iNameType=iName;
	hashTables->type=type;

		//Update
	++hsht.hashTablesCurrent;
	hsht.freeHashTables-=sizeof(HSHT_TYPETABLE);
	++hsht.cHashTables;

	return(SUCCESS_);
}


//--------------------------------------------------------------------------
//Get number of arguments in procedure.
//
// procedure - Procedure structure. It MUST have a frame! (check funcs.hpp)
// offsetArgFirst - Pointer to variable that will get the offset of 1st argument in frame,
//                  if you don't want it set it to ZERO.
//                  It can be an offset to nameLess byte, but after all nameLess bytes we will came to first argument.
//
// return - Number of arguments in procedure.
//
unsigned long SetSoftIce::GetProcedureArgCount(func_t* procedure, unsigned long* offsetArgFirst)
{
	struc_t*		frame;
	unsigned long	frameSize;
	
	unsigned long	argCount=0;
	char*			argName;
	unsigned long	argOffset;
	unsigned long	argSize;
	member_t*		argument;

//Error in frame.hpp and funcs.hpp!!!
//    +----------------------------------------+        <- SP
//    | local variables                        |
//    +----------------------------------------+        <- BP
//    | saved registers (SI,DI,etc)            |
//    +----------------------------------------+
//    | return address (isn't stored in func_t)|
//    +----------------------------------------+
//    | function arguments                     |
//    +----------------------------------------+

//	pArgSize=procedure->argsize;
//	if ( 0==pArgSize ) return(0); this only means that callee doesn't pop the arguments


	frame=get_frame(procedure);	//frame is a struct_t type, but the members are all pushed+local var + pushed regs
								//so we need only types of the first few (pushed var)
	
	//get first argumentOffset
	frameSize=get_struc_size(frame);
	argOffset=get_struc_first_offset(frame);
	argSize=0;
		//find " r"
	while ( argSize<frameSize )
	{
		argument=get_member(frame, argOffset);
		while ( 0==argument )	//because of the nameless bytes
		{
			argOffset=get_struc_next_offset(frame,argOffset);
			argument=get_member(frame, argOffset);
			++argSize;
		}
		argName=get_member_name(argument->id);
		if ( 0==strcmp(" r", argName) ) break;
		argSize+=get_member_size(argument);

		argOffset=get_struc_next_offset(frame,argOffset);
	}

	//now we have " r" frame member => get offset to 1st argument (it can be an offset to name less byte)
	argOffset=get_struc_next_offset(frame,argOffset);
	if ( BADADDR!=argOffset )
	{
		if ( 0!=offsetArgFirst )
			*offsetArgFirst=argOffset;

		//get number of arguments
		while( BADADDR!=argOffset )	
		{
			argument=get_member(frame, argOffset);
			while ( 0==argument )	//because of the nameless bytes
			{
				if ( BADADDR==(argOffset=get_struc_next_offset(frame,argOffset)) ) goto END;
				argument=get_member(frame, argOffset);
			}
			++argCount;
			argOffset=get_struc_next_offset(frame,argOffset);
		}
	}
END:
	return(argCount);
}

//--------------------------------------------------------------------------
//Get number of local variables in procedure and offset to first one in frame.
//
// procedure - Procedure structure. It must have frame! (check funcs.hpp)
//
// return - Number of local variables in procedure.
//
unsigned long SetSoftIce::GetProcedureLVarCount(func_t* procedure)
{
	struc_t*		frame;

	unsigned long	lVarCount=0;
	unsigned long	lVarOffset;
	unsigned long	lVarSize;
	member_t*		lVariable;

	unsigned long	frsize;

//Error in frame.hpp and funcs.hpp!!!
//    +----------------------------------------+        <- SP
//    | local variables                        |
//    +----------------------------------------+        <- BP
//    | saved registers (SI,DI,etc)            |
//    +----------------------------------------+
//    | return address (isn't stored in func_t)|
//    +----------------------------------------+
//    | function arguments                     |
//    +----------------------------------------+

	frsize=procedure->frsize;
	//Do we have any local variables
	if ( 0==frsize ) return(0);

	//We have local variables
	frame=get_frame(procedure);
	//get number of localVariables
	lVarOffset=get_struc_first_offset(frame);
	lVarSize=0;
	while( lVarSize<frsize )
	{
		lVariable=get_member(frame, lVarOffset);
		while ( 0==lVariable )	//because of the nameless bytes
		{
			lVarOffset=get_struc_next_offset(frame,lVarOffset);
			lVariable=get_member(frame, lVarOffset);
			++lVarSize;
			if ( lVarSize>=frsize ) goto END;	//this must be here if nameless byte is the last localByte
		}

		if ( FF_STRU==((lVariable->flag)&DT_TYPE) )
		{
			lVarCount+=GetStructureMemberCount(get_sptr(lVariable));	//member numbers
//			++lVarCount; //for structure local name (set in next line)
		}
		++lVarCount;
		
		lVarSize+=get_member_size(lVariable);
		lVarOffset=get_struc_next_offset(frame,lVarOffset);
	}
END:

//return
	return(lVarCount);
}



//--------------------------------------------------------------------------
//Get number of variables in structure. If structure has a structure member
//than it will add the count of that struct too.
//
// structure - Pointer to structure.
//
// return - Number of members in structure.
//
//ex.
//struct ONE{
//	DWORD onet;
//  DWORD twot;
//};
//
//struct TWO{
//	DWORD twot;
//	ONE   one;
//}
//return = 3
//
unsigned long SetSoftIce::GetStructureMemberCount(struc_t* structure)
{
	unsigned long	memberCount=0;
	unsigned long	memberOffset;
	member_t*		member;

//Test if we have any members.
	if ( 0==(structure->memqty) ) return(0);

//Test if the struct is union. Unions has only one 'real' member.
	if ( structure->is_union() ) return(1);


//Get the number of members
	memberOffset=get_struc_first_offset(structure);
	for (unsigned long i=0;i<(structure->memqty); i++)
	{
		member=get_member(structure, memberOffset);
		if ( 0==member )
		{
			warning("PlugIn doesn't support undefined(unNamed) bytes in structure (%s).", get_struc_name(structure->id));
			return(FAILED_);
		}

		if ( FF_STRU==((member->flag)&DT_TYPE) )
		{
			memberCount+=GetStructureMemberCount(get_sptr(member));
		}
		else
		{
			++memberCount;
		}

		memberOffset=get_struc_next_offset(structure,memberOffset);
	}
	return(memberCount);
}

//--------------------------------------------------------------------------
//Set local structure member names.
//
// structure - Pointer to structure.
// localName - Pointer to the end of structure's local name. (We'll write to this buffer so the name
//                                                                    must be in a 'scratch' buffer.)
// structOffset - EBP offset in procedure.
// segLocal - Pointer to 1st free VLPTABLE (localVariablesCurrent). 
//
// return - Pointer to 1st free VLPTABLE (localVariablesCurrent) or FAILED_.
//
SEGDATA_VLPTABLE* SetSoftIce::SetLocalStructMembers(struc_t* structure, char* localName, unsigned long structOffset, SEGDATA_VLPTABLE* segLocal)
{
	char*			afterDot;
	member_t*		member;
	unsigned long	memberOffset;
	
//Test if we have any members.
	if ( 0==(structure->memqty) )
	{
		warning("Structure(%s) doesn't have any members.", get_struc_name(structure->id));
		return(FAILED_);
	}

//Set
	afterDot=strcat(localName, ".");
	while ( afterDot[0]!='\x0' ) ++afterDot;
	memberOffset=get_struc_first_offset(structure);
	member=get_member(structure, memberOffset);

//Test if the struct is union. Well set only the first member name.
	if ( (structure->is_union())&&(0!=member) )
	{
		segLocal->ten=0x10;
		segLocal->tableType=TT_LOCALV;
		segLocal->type=GetType_member(member);

		strcpy(afterDot, get_member_name(member->id));
		segLocal->iNameVLP=SetStringTable(localName);

		segLocal->offVLP=structOffset;
		++segLocal;
		return(segLocal);
	}

//We have a normal structure
	for (unsigned long i=0;i<(structure->memqty); i++)
	{
		if ( 0!=member )
		{
			if ( FF_STRU==((member->flag)&DT_TYPE) )
			{
				strcpy(afterDot, get_member_name(member->id));
				segLocal=SetLocalStructMembers(get_sptr(member), localName, structOffset, segLocal);
			}
			else
			{
				segLocal->ten=0x10;
				segLocal->tableType=TT_LOCALV;
				segLocal->type=GetType_member(member);

				strcpy(afterDot, get_member_name(member->id));
				segLocal->iNameVLP=SetStringTable(localName);

				segLocal->offVLP=structOffset;
				++segLocal;
			}
		}

		structOffset+=get_member_size(member);
		if ( BADADDR==(memberOffset=get_struc_next_offset(structure, memberOffset)) ) break;
		member=get_member(structure, memberOffset);
	}

	return(segLocal);
}