#include <OSIncludes.h>

#pragma header

#include <stdio.h>
#include <string.h>

#define ARG_TEMPLATE "SOURCEROM/A,DESTROM/A,RELOCFILE/K,NODRAGPATCH/S"

enum {ARG_SOURCE,ARG_DEST,ARG_RELOCFILE,ARG_NODRAGPATCH,NUM_ARGS};

APTR ROMStart,ROMPatchStart;

static struct RDArgs *MyArgs;
static LONG Args[NUM_ARGS];
static BPTR MyHandle;
static char s[256],relocfile[256];
static LONG FreeROMSpace;
static LONG l;

LONG ROMSize;

LONG relocarray[3*2];

UBYTE ROM[1048576];

/*****************************************************/

extern struct Resident *FindRes(register __a0 APTR startsearch,register __a1 APTR endsearch);
extern void CalcROMCheckSum(void);
extern void PatchROM(void);

extern LONG ROMPatchBytes;
extern APTR IntuitionStart;
extern APTR IntuitionEnd;

/*****************************************************/

static void Cleanup(char *mes)
{
	WORD rc;
	
	if (mes)
	{
		printf("PatchROMforPW: %s\n",mes);
		rc=RETURN_WARN;
	} else {
		rc=RETURN_OK;
	}
	
	if (MyArgs) FreeArgs(MyArgs);
	if (MyHandle) Close(MyHandle);
	
	exit(rc);
}

static void GetArguments(void)
{
	if (!(MyArgs=ReadArgs(ARG_TEMPLATE,Args,0)))
	{
		Fault(IoErr(),0,s,255);
		Cleanup(s);
	}
}

static void LoadROM(void)
{
	if (!(MyHandle=Open((char *)Args[ARG_SOURCE],MODE_OLDFILE)))
	{
		Fault(IoErr(),0,s,255);
		Cleanup(s);
	}
	
	Seek(MyHandle,0,OFFSET_END);
	ROMSize=Seek(MyHandle,0,OFFSET_BEGINNING);
	
	if (ROMSize < 262144 || ROMSize > 1048576)
	{
		l=IoErr();
		if (l)
		{
			Fault(IoErr(),0,s,255);
			Cleanup(s);
		} else {
			Cleanup("Bad ROM size!");
		}
	}
	
	if (Read(MyHandle,ROM,ROMSize) != ROMSize)
	{
		Fault(IoErr(),0,s,255);
		Cleanup(s);
	}
	
	Close(MyHandle);MyHandle=0;
}

static void SaveROM(void)
{
	if (!(MyHandle=Open((char *)Args[ARG_DEST],MODE_NEWFILE)))
	{
		Fault(IoErr(),0,s,255);
		Cleanup(s);
	}
	
	if (Write(MyHandle,ROM,ROMSize) != ROMSize)
	{
		Fault(IoErr(),0,s,255);
		Cleanup(s);
	}
	
	Close(MyHandle);MyHandle=0;
}

static void FindROMPlace(void)
{
	char *name;
	struct Resident *res,*lastres=0;

	if (ROMSize<524288)
	{
		ROMStart=(APTR)0xFC0000;
	} else if (ROMSize>524288) {
		ROMStart=(APTR)0xF00000;
	} else {
		ROMStart=(APTR)0xF80000;
	}
	
	res=(struct Resident *)ROM;
	
	while ((res=FindRes(res,&ROM[ROMSize-sizeof(struct Resident)])))
	{
		if (res->rt_Name)
		{
			name = res->rt_Name - (char *)ROMStart + ROM;

			if (!stricmp(name,"intuition.library"))
			{
				IntuitionStart=res;
				IntuitionEnd=(APTR)((ULONG)res->rt_EndSkip - (ULONG)ROMStart + (ULONG)ROM);
			}
		}
		lastres=res;
	}
	
	if (!lastres) Cleanup("Fatal error: No resident tag found!?");
	if (!IntuitionStart) Cleanup("Fatal error: Intuition resident not found!?");

	FreeROMSpace = (ROMSize - ((ULONG)lastres->rt_EndSkip - (ULONG)ROMStart) - 24);
	
	if (FreeROMSpace < ROMPatchBytes) Cleanup("There's not enough free space in the ROM!");

	ROMPatchStart = (APTR)((ULONG)lastres->rt_EndSkip - (ULONG)ROMStart + (ULONG)ROM);

	((UBYTE *)lastres->rt_EndSkip) += ROMPatchBytes;

}

static void SaveRelocFile(void)
{
	if (Args[ARG_RELOCFILE])
	{
		if (!(MyHandle=Open((char *)Args[ARG_RELOCFILE],MODE_NEWFILE)))
		{
			Fault(IoErr(),0,s,255);
			Cleanup(s);
		}
		if (Write(MyHandle,relocarray,3*2*4) != 3*2*4)
		{
			Fault(IoErr(),0,s,255);
			Cleanup(s);
		}
		Close(MyHandle);MyHandle=0;
	}
}

void main(void)
{
	GetArguments();
	LoadROM();
	FindROMPlace();
	PatchROM();
	CalcROMCheckSum();
	SaveROM();
	SaveRelocFile();
	Cleanup(0);
}
