/* Create a raw file executable at a given address */

#include <stdio.h>
#include <stdlib.h>
#include <libraries/dos.h>

/* Some bits from dos/doshunks.h */
#define HUNK_UNIT       999
#define HUNK_NAME       1000
#define HUNK_CODE       1001
#define HUNK_DATA       1002
#define HUNK_BSS        1003
#define HUNK_RELOC32    1004
#define HUNK_ABSRELOC32 HUNK_RELOC32
#define HUNK_RELOC16    1005
#define HUNK_RELRELOC16 HUNK_RELOC16
#define HUNK_RELOC8     1006
#define HUNK_RELRELOC8  HUNK_RELOC8
#define HUNK_EXT        1007
#define HUNK_SYMBOL     1008
#define HUNK_DEBUG      1009
#define HUNK_END        1010
#define HUNK_HEADER     1011

#define HUNK_OVERLAY    1013
#define HUNK_BREAK      1014

#define HUNK_DREL32     1015
#define HUNK_DREL16     1016
#define HUNK_DREL8      1017

#define HUNK_LIB        1018
#define HUNK_INDEX      1019
/* End of extract from doshunks */


main( argc, argv )
int	argc;
char	**argv;
{
	int	word, len, ignore, base, currhunk, t, totlen, a, b, pos, ch, hunknum, flag, lasthunk;
	char	*outname;
	BPTR	infile, outfile;
	int	*hunk[64];		/* Maximum number of hunks */
	int	*thunk, *block;
	int	htype[64];		/* Maximum again */
	int	hlen[64];		/* ...and again */
	int	haddr[64];		/* ...and again */
	int	hids[64];

	for(t=0;t<32;t++)
	{
		hunk[t]=0;
		hids[t]=-1;
	}

	if( argc != 3 )
	{
		printf("Usage: rel addr filename\n");
		exit(0);
	}

	if( (base=atoi(argv[1])) <= 0 )
	{
		printf("Error: illegal base address\n");
		exit(0);
	}

	if( (infile=Open( argv[2], MODE_OLDFILE )) == 0 )
	{
		printf("Error: cannot open input file\n");
		exit(0);
	}

	outname=(char*)malloc( strlen(argv[2])+5 );
	strcpy( outname, argv[2] );
	strcat( outname, ".rex" );
	
	if( (outfile=Open( outname, MODE_NEWFILE )) == 0 )
	{
		free( outname );
		Close( infile );
		printf("Error: cannot open output file (%s)\n", outname );
		exit(0);
	}

	free( outname );

	hunknum=0;
	currhunk=0;
	ignore=0;
	lasthunk=-1;

	printf("Starting file read\n");
	while( myread( infile, &word, 4 ) == 4 )
	{
		_CheckIntr();
		if( ignore-- == 0 )
		{
			ignore=0;
			if( word != HUNK_END )
				myread( infile, &len, 4 );
			else
				len=0;
			printf( "Hunk:%i, length:%i\n", word, len );
			switch( word )
			{
				case HUNK_CODE:
				case HUNK_DATA:
				{
					hids[currhunk]=hunknum++;
					htype[currhunk]=word;
					hlen[currhunk]=len;
					hunk[currhunk]=(int*)malloc( len*4 );
					myread( infile, hunk[currhunk], len*4 );
					lasthunk=currhunk++;
					break;
				}
				case HUNK_BSS:
				{
					hids[currhunk]=hunknum++;
					htype[currhunk]=word;
					hlen[currhunk]=len;
					hunk[currhunk]=(void*)1; /* This is not a terminator */
					currhunk++;
					break;
				}
				case HUNK_HEADER:
				{
					htype[currhunk]=word;
					myread( infile, &t, 4 );
					hunk[currhunk]=(int*)malloc( 16 + (4*t) );
					hlen[currhunk]=4+t;
					*hunk[currhunk]=len;
					*(hunk[currhunk]+1)=t;
					myread( infile, hunk[currhunk++]+2, 8+(4*t) );
					break;
				}
				case HUNK_RELOC32:
				{
					htype[currhunk]=word;
					hlen[currhunk]=0;
					hunk[currhunk]=0;
					while( len != 0 )
					{
						thunk=(int*)malloc((hlen[currhunk]+len+3)*4);
						*(thunk+hlen[currhunk]+len+2)=0;
						if( hunk[currhunk]!=0 )
						{
							for( t=0; t<hlen[currhunk]; t++ )
								*(thunk+t)=*(hunk[currhunk]+t);
							free( hunk[currhunk] );
						}
						myread( infile, thunk+hlen[currhunk]+1, (len+1)*4 );
						*(thunk+hlen[currhunk])=len;
						hlen[currhunk]+=len+2;
						hunk[currhunk]=thunk;
						myread( infile, &len, 4 );
					}
					hlen[currhunk]=lasthunk;
					currhunk++;
					break;
				}
				case HUNK_END:
				{
					break;
				}
				default:
				{
					fprintf(stderr,"Unknown hunk %i, length %i\n",word,len);
					ignore=len;
				}
			}
		}
	}

	Close( infile );

	/* Total up the length of the file and get some memory for it */
	totlen=0;
	t=-1;
	while( hunk[++t] )
		if( (htype[t]==HUNK_CODE) || (htype[t]==HUNK_DATA) || (htype[t]==HUNK_BSS) )
			totlen+=hlen[t];

	printf("Total length:%i\n",totlen);

	block=(int*)malloc(totlen*4);

	/* Put all the text segments in */
	pos=0;
	t=-1;
	while( hunk[++t] )
		if( htype[t]==HUNK_CODE )
		{
			printf("Adding code hunk length %i\n",hlen[t]);
			for( a=0; a<hlen[t]; a++ )
				*(block+pos+a)=*(hunk[t]+a);
			haddr[t]=pos;
			pos+=hlen[t];
		}

	/* Put all the data segments in */
	t=-1;
	while( hunk[++t] )
		if( htype[t]==HUNK_DATA )
		{
			printf("Adding data hunk length %i\n",hlen[t]);
			for( a=0; a<hlen[t]; a++ )
				*(block+pos+a)=*(hunk[t]+a);
			haddr[t]=pos;
			pos+=hlen[t];
		}

	/* Put all the bss segments in */
	t=-1;
	while( hunk[++t] )
		if( htype[t]==HUNK_BSS )
		{
			printf("Adding bss hunk length %i\n",hlen[t]);
			for( a=0; a<hlen[t]; a++ )
				*(block+pos+a)=0;
			haddr[t]=pos;
			pos+=hlen[t];
		}

	printf("Final pos %i\n",pos);

	/* Relocate all the addresses */

	t=-1;
	while( hunk[++t] )
	{
		if( htype[t]==HUNK_RELOC32 )
		{
			printf("Sorting reloc32 hunk\n");
			printf("Doctoring hunk %i, type %i\n",hlen[t],htype[hlen[t]]);
			len=*hunk[t];
			a=0;
			while( len )
			{
				printf("\tSection length %i\n",len);
				ch=*(hunk[t]+a+1);
				hunknum=-1;
				flag=0;
				while( hunk[++hunknum] && !flag )
					if( hids[hunknum]==ch )
						flag=1;
				hunknum--;
				if( flag )
				{
					for(b=0;b<len;b++)
					{
						printf("\tadd to %i with %i - ", *(int*)((char*)(block+haddr[hlen[t]])+*(hunk[t]+2+b+a)), 
													(haddr[hunknum]*4)+base);
						printf("offset %i\n", haddr[hlen[t]]+*(hunk[t]+2+b+a));
						*((int*)((char*)(block+haddr[hlen[t]])+*(hunk[t]+2+b+a))) += (haddr[hunknum]*4)+base;
					}
				}
				else
					printf("Odd thing with hunk numbers...\nWanted %i\n", ch);
				a+=2+len;
				len=*(hunk[t]+a);
			}
		}
	}

	
	mywrite( outfile, block, totlen*4 );

	Close( outfile );

	free( block );
	t=-1;
	while( hunk[++t] )
		free( hunk[t] );

	exit(0);
}


int myread( infile, buf, len )
BPTR	*infile;
char	*buf;
int	len;
{
	int	sofar = len;
	int	one;

	printf("{%i}(",len);
	while( sofar && (one=Read(infile,buf,sofar)) )
	{
		printf("%i,",one);
		buf   += one;
		sofar -= one;
	}
	printf(")\n");

	if( sofar )
		printf("----Read failed (Oops)\n");

	return( len-sofar );
}


int mywrite( outfile, buf, len )
BPTR	*outfile;
char	*buf;
int	len;
{
	int	sofar = len;
	int	one;

	while( sofar && (one=Write(outfile,buf,sofar)) )
	{
		buf   += one;
		sofar -= one;
	}

	return( len-sofar );
}

