/*
**	I386MIPS - change the machine ID on a COFF object from I386 to R3000
**	Steve Salisbury, 03/24/1992
*/

#include <fcntl.h>
#include <io.h>
#include <stdio.h>
#include <nt.h>

int main(int argc, char **argv);

IMAGE_FILE_HEADER ifh ;
IMAGE_SECTION_HEADER ish ;
IMAGE_RELOCATION reloc ;

const char * RelocName ( int reloctype ) ;

int main(int argc, char **argv)
{
	FILE *fileptr;
	char * name ;
	char * cp ;
	int errors = 0 ;
	int WriteFlag = 0 ;

	-- argc , ++ argv ;

	if ( setvbuf ( stderr , NULL , _IONBF , 0 ) )
	{
		printf ( "setvbuf error\n" ) ;
		exit ( 1 ) ;
	}

	if ( argc == 0 || ( cp = * argv ) && * cp == '-'
	&& ( * cp == 'h' || * cp == 'H' || * cp == '?' ) )
	{
		fprintf ( stderr ,
	"usage: i386mips [-w] <objectfile> ...\n"
	"each object file listed is converted from I386 to MIPS\n"
	"-w specifies that i386 objects should be changed to MIPS objects\n"
	"NOTE: This only works for files containing data but no code!\n"
		);
		exit ( 1 ) ;
	}

	if ( argc > 1 && ! strcmp ( "-w" , * argv ) )
	{
		-- argc , ++ argv ;
		WriteFlag = 1 ;
	}

	for ( ; * argv ; )
	{
		char * name ;
		int sect ;

		name = * argv ++ ;

		++ errors ; /* increment on principle */

		if ( WriteFlag )
		{
			if ( ( fileptr = fopen ( name , "r+b" ) ) == NULL )
			{
				fprintf ( stderr ,
		"i386mips: cannot open \"%s\" for modification\n" ,
					name ) ;
				continue ;
			}
		}
		else
		{
			if ( ( fileptr = fopen ( name , "rb" ) ) == NULL )
			{
				fprintf ( stderr ,
		"i386mips: cannot open \"%s\" for reading\n" ,
					name ) ;
				continue ;
			}
		}

		if ( 1 != fread ( & ifh , sizeof ( ifh ) , 1 , fileptr ) )
		{
			fprintf ( stderr ,
	"i386mips: cannot read first %d bytes from \"%s\"\n" ,
				sizeof ( ifh ) , name ) ;
			fclose ( fileptr ) ;
			continue ;
		}

		printf ( "Machine ID = 0x%X (%s)\n" ,
			ifh.Machine , 
			ifh.Machine == IMAGE_FILE_MACHINE_I386 ? "i386" :
			ifh.Machine == IMAGE_FILE_MACHINE_R3000 ? "MIPS" :
			"?Unknown?" ) ;

		printf ( "%d Sections, TimeDate=%08lX, SizeOptHdr=%u, Flags=0x%X" ,
			ifh.NumberOfSections, ifh.TimeDateStamp, 
			ifh.SizeOfOptionalHeader , ifh.Characteristics);

		if ( ifh.Characteristics & IMAGE_FILE_RELOCS_STRIPPED )
			printf ( " NoRelocs" ) ;
		if ( ifh.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE )
			printf ( " EXECUTABLE" ) ;
		if ( ifh.Characteristics & IMAGE_FILE_LINE_NUMS_STRIPPED )
			printf ( " NoLineNums" ) ;
		if ( ifh.Characteristics & IMAGE_FILE_LOCAL_SYMS_STRIPPED )
			printf ( " NoLocalSyms" ) ;
		printf ( "\n" ) ;

		printf ( "\n" ) ;

		if ( WriteFlag )
		{
			if ( ifh.Machine != IMAGE_FILE_MACHINE_I386 )
			{
				if ( ifh.Machine == IMAGE_FILE_MACHINE_R3000 )
					fprintf ( stderr ,
		"i386mips: \"%s\" is already a MIPS COFF file (machine type = 0x%X)\n" ,
						name , ifh.Machine ) ;
				else
					fprintf ( stderr ,
		"i386mips: \"%s\" is not an i386 COFF file (machine type = 0x%X)\n" ,
						name , ifh.Machine ) ;

				fclose ( fileptr ) ;
				continue ;
			}

			if ( fseek ( fileptr , 0L , SEEK_SET ) )
			{
				fprintf ( stderr ,
		"i386mips: fseek() error on \"%s\"\n" , name  ) ;
				fclose ( fileptr ) ;
				continue ;
			}

			ifh.Machine = IMAGE_FILE_MACHINE_R3000 ;
		
			if ( 1 != fwrite ( & ifh.Machine , sizeof ( ifh.Machine ) , 1 , fileptr ) )
			{
				fprintf ( stderr ,
		"i386mips: cannot re-write first %d bytes of \"%s\"\n" ,
					sizeof ( ifh.Machine ) , name ) ;
				fclose ( fileptr ) ;
				continue ;
			}

			printf ( "Machine ID on \"%s\" changed from 0x%X (i386) to 0x%X (MIPS)\n" ,
				name , IMAGE_FILE_MACHINE_I386 , IMAGE_FILE_MACHINE_R3000 ) ;

			if ( fseek ( fileptr , (long) sizeof ( ifh ) , SEEK_SET ) )
			{
				fprintf ( stderr ,
			"i386mips: fseek() error on \"%s\" (@%ld)\n" , name , sizeof ( ifh )  ) ;
				fclose ( fileptr ) ;
				continue ;
			}
		}

		for ( sect = 1 ; sect <= ifh.NumberOfSections ; ++ sect )
		{
			int s ;
			long savedpos ;
			
			if ( 1 != fread ( & ish , sizeof ( ish ) , 1 , fileptr ) )
			{
				fprintf ( stderr ,
		"i386mips: cannot read section header %d from \"%s\"\n" ,
					sect , name ) ;
				fclose ( fileptr ) ;
				continue ;
			}

			printf ( "Section %d: \"%s\": VirtSize=0x%lX, VirtAdddr=0x%lX\n" ,
				sect , ish.Name , ish.Misc.VirtualSize, ish.VirtualAddress);

			printf ( "PtrRawData=0x%lX, PtrRelocs=0x%lX, PtrLineNums=0x%lX\n" ,
				ish.PointerToRawData, ish.PointerToRelocations,
				ish.PointerToLinenumbers ) ;

			printf ( "#Relocs=%u, #LineNums=%u, Flags=0x%lX" ,
				ish.NumberOfRelocations, ish.NumberOfLinenumbers, ish.Characteristics);
			if ( ish.Characteristics & IMAGE_SCN_CNT_CODE )
				printf ( " Text" ) ;
			if ( ish.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA )
				printf ( " Data" ) ;
			if ( ish.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA )
				printf ( " BSS" ) ;
			printf ( "\n" ) ;

			printf ( "\n" ) ;

			savedpos = 0 ;

			if ( ish.PointerToRawData )
			{
				int b ;

				savedpos = ftell ( fileptr ) ;

				if ( fseek ( fileptr , ish.PointerToRawData , SEEK_SET ) )
				{
					fprintf ( stderr ,
			"i386mips: fseek() error on \"%s\" (Raw Data at 0x%lX)\n" , name ,
						ish.PointerToRawData ) ;
					fclose ( fileptr ) ;
					continue ;
				}

				printf ( "Raw Data for Section %d: " , sect ) ;

				for ( b = 0 ; b < ish.Misc.VirtualSize ; ++ b )
					printf ( " %02X" , getc ( fileptr ) ) ;

				printf ( "\n\n" ) ;
			}
				
			if ( ish.NumberOfRelocations )
			{
				int r ;

				if ( ! savedpos )
					savedpos = ftell ( fileptr ) ;

				if ( fseek ( fileptr , ish.PointerToRelocations , SEEK_SET ) )
				{
					fprintf ( stderr ,
			"i386mips: fseek() error on \"%s\" (Relocs at 0x%lX)\n" , name ,
						ish.PointerToRelocations ) ;
					fclose ( fileptr ) ;
					continue ;
				}

				for ( r = 1 ; r <= ish.NumberOfRelocations ; ++ r )
				{
					if ( 1 != fread ( & reloc , sizeof ( reloc ) , 1 , fileptr ) )
					{
						fprintf ( stderr ,
				"i386mips: cannot read relocation %d in section %d from \"%s\"\n" ,
							r , sect , name ) ;
						fclose ( fileptr ) ;
						continue ;
					}

					printf ( "Relocation %d: Addr=0x%lX, Symbol=%ld, Type=0x%X  %s\n" ,
						r , reloc.VirtualAddress , reloc.SymbolTableIndex ,
						reloc.Type , RelocName ( reloc.Type ) ) ;

					if ( WriteFlag && reloc.Type == IMAGE_REL_I386_DIR32 )
					{
						if ( fseek ( fileptr , ish.PointerToRelocations , SEEK_SET ) )
						{
							fprintf ( stderr ,
					"i386mips: fseek() error on \"%s\" (Reloc %d in Section %d at 0x%lX)\n" , name ,
								sect , r , ish.PointerToRelocations ) ;
							fclose ( fileptr ) ;
							continue ;
						}

						reloc.Type = IMAGE_REL_MIPS_REFWORD ;
						
						if ( 1 != fwrite ( & reloc , sizeof ( reloc ) , 1 , fileptr ) )
						{
							fprintf ( stderr ,
					"i386mips: cannot re-write relocation %d in section %d from \"%s\"\n" ,
								r , sect , name ) ;
							fclose ( fileptr ) ;
							continue ;
						}
						
						if ( fseek ( fileptr , 0L , SEEK_CUR ) )
						{
							fprintf ( stderr ,
					"i386mips: fseek() error on \"%s\" (after Reloc %d in Section %d at 0x%lX)\n" , name ,
								sect , r , ish.PointerToRelocations ) ;
							fclose ( fileptr ) ;
							continue ;
						}

						printf ( "===> relocation changed to type %d (%s)\n" ,
							reloc.Type , RelocName ( reloc.Type ) ) ;
					}

				}

				printf ( "\n" ) ;
			}
				
			printf ( "\n" ) ;

			if ( ! savedpos )
				savedpos = ftell ( fileptr ) ;


			if ( savedpos && fseek ( fileptr , savedpos , SEEK_SET ) )
			{
				fprintf ( stderr ,
			"i386mips: fseek() error on \"%s\" (return to 0x%lX)\n" , name ,
					savedpos ) ;
				fclose ( fileptr ) ;
				continue ;
			}
		}

		if ( fclose ( fileptr ) )
		{
			fprintf ( stderr ,
	"i386mips: fclose () error on \"%s\"\n" , name  ) ;
			continue ;
		}

		-- errors ; /* undo increment on principle */

	}

	return errors ;
}

const char * RelocName ( int reloctype )
{
	char * name ;

	switch ( reloctype )
	{
	case 0 :
		name = "Absolute" ;
		break ;
	case 1 :
		name = "I386_DIR16 / MIPS_REFHALF" ;
		break ;
	case 2 :
		name = "I386_REL16 / MIPS_REFWORD" ;
		break ;
	case 3 :
		name = "MIPS_JMPADDR" ;
		break ;
	case 4 :
		name = "MIPS_REFHI" ;
		break ;
	case 5 :
		name = "MIPS_REFLO" ;
		break ;
	case 6 :
		name = "I386_DIR32 / MIPS_GPREL" ;
		break ;
	case 7 :
		name = "I386_DIR32NB / MIPS_LITERAL" ;
		break ;
	case 011 :
		name = "I386_SEG12" ;
		break ;
	case 024 :
		name = "I386_REL32" ;
		break ;
	case 042 :
		name = "MIPS_REFWORDNB" ;
		break ;
	case 045 :
		name = "MIPS_PAIR" ;
		break ;
	default :
		name = "*** ??? ***" ;
	}

	return name ;
}
