/*	KickLoad.c - Manipulate the Exec Kickstart delta list	*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <exec/resident.h>
#include <proto/exec.h>

static const char _version[] = "$VER: KickLoad 1.0 (19.10.93)";

struct ExecBase **ExecBase = (struct ExecBase **) 0x000004;

FILE	*fp;
char	*code;
unsigned long mem_size;

struct MemList	*KickMem, *myMemList;
long	*KickTag, *myTagPtr;

void clean (char *, char *);
void display_kick (void);
void clear_kick (void);
unsigned long getlong (void);
int	load_kick (char *);
int	load_image (char *);

main (argc, argv)
int	argc;
char	*argv[];
{
	int	i;

	if (argc < 2) {
		display_kick ();
	}
	else if (strcmp (argv[1], "-clear") == 0) {
		clear_kick ();
	}
	else for (i = 1; i < argc; ++i)
		load_kick (argv[i]);
}

/*
 *	load_kick (fname) - load an executable image and insert it into the
 *	Exec KickStart delta list
 *
 */

load_kick (fname)
char *fname;
{
	fp = fopen (fname, "r");
	if (fp == NULL) {
		clean ("unable to open %s", fname);
		return (-1);
	}
	if (load_image (fname))
		return (-1);
	fclose (fp);
	fp = NULL;
	/* link into ExecBase data */
	KickMem = (struct MemList *) (*ExecBase)->KickMemPtr;
	KickTag = (long *) (*ExecBase)->KickTagPtr;
#if 0
	FreeMem (code, mem_size);	/* temp */
	printf ("KickLoad:  %s test load sucessful\n", fname);
#else
	Forbid ();
	while (KickMem && KickMem->ml_Node.ln_Succ)
		KickMem = (struct MemList *) KickMem->ml_Node.ln_Succ;
	myMemList->ml_Node.ln_Pred = (struct Node *) KickMem;
	if (KickMem)
		KickMem->ml_Node.ln_Succ = (struct Node *) myMemList;
	else
		(*ExecBase)->KickMemPtr = (APTR) myMemList;
	/* Todo - add TagPtr to tail of TagPtr list */
	if (KickTag)
		myTagPtr[1] = ((long) KickTag) | 0x80000000;
	(*ExecBase)->KickTagPtr = (APTR) myTagPtr;
	(*ExecBase)->KickCheckSum = (APTR) SumKickData ();
	Permit ();
	printf ("KickLoad:  %s sucessfully installed\n", fname);
#endif
	code = NULL;
	return (0);
}

/*
 *  load_image (fname) - load an executable image into memory
 *	fname = path to image file
 *
 *	outputs:
 *		code - address of executable memory + TagPtr + MemList
 *		myTagPtr - address of TagPtr table
 *		myMemList - address of MemList structure
 *
 *	Executable image begins at code;  TagPtr is two longwords following
 *	the executable image;  MemList follows the TagPtr.  TagPtr points to
 *	the first RomTag found in the image.  MemList contains one entry and
 *	is the image/TagPtr/MemList data.
 *	*** only one RomTag is recognized
 *	*** memory is allocated from Chip memory
 *	*** [could request DMAable memory for V36 (2.0)
 */

load_image (fname)
char	*fname;
{
	unsigned long u, getlong ();
	int	i, j, k;
	unsigned long *hunk_table;
	unsigned long hunk;
	int hunk_type;
	char *hunk_ptr;
	unsigned long *loc;
	unsigned short *res_ptr;
	unsigned long code_size;

	if (getlong () != 1011) {		/* Must be HUNK_HEADER */
		clean ("%s is not an image file", fname);
		return (-1);
	}
	if (getlong ()) {			/* don't allow resident lib */
		clean ("%s Resident library not allowed", fname);
		return (-1);
	}
	u = getlong ();				/* Hunk table size */
	hunk = getlong ();			/* first hunk */
	u = getlong ();				/* last hunk */
	hunk_table = (long *) calloc (u + 1, sizeof (long));
	if (hunk_table == NULL) {
		clean ("unable to allocate memory for hunk table", NULL);
		return (-1);
	}
	code_size = 0;
	for (i = hunk; i <= u; ++i)
		code_size += hunk_table[i] = getlong () * 4;
	mem_size = code_size + sizeof (struct MemList) + 8 + 8;
#ifdef MEMF_24BITDMA
	code = (char *) AllocMem (mem_size, MEMF_CLEAR | MEMF_24BITDMA);
#else
	code = (char *) AllocMem (mem_size, MEMF_CLEAR | MEMF_CHIP);
#endif
	if (code == NULL) {
		clean ("unable to allocate memory for image", NULL);
		free (hunk_table);
		return (-1);
	}
	/* Leave 8 byte buffer from beginning of allocated memory
	 * Memory List
	 * RomTag pointer array
	 * resident module data
	 */
	myMemList = (struct MemList *) (code + 8);
	myTagPtr = (unsigned long *) (myMemList + 1);
	code = (char *) (myTagPtr + 2);
	for (i = hunk, j = 0; i <= u; ++i) {
		k = hunk_table[i];
		hunk_table[i] = (long) (code + j);
		j += k;
	}
	do {
		switch (hunk_type = getlong ()) {
		case 1001:			/* HUNK_CODE */
		case 1002:			/* HUNK_DATA */
		case 1003:			/* HUNK_BSS */
			hunk_ptr = (char *) hunk_table[hunk++];
			u = getlong ();		/* code length */
			if (hunk_type != 1003 && u)
				fread (hunk_ptr, u, 4, fp);
			break;
		case 1004:			/* HUNK_RELOC32 */
			while (i = getlong ()) {	/* get # refs */
				j = getlong ();		/* hunk # */
				/* tpdo - validate hunk # */
				j = hunk_table[j];
				while (i--) {
					u = getlong ();	/* offset */
					loc = (long *) (hunk_ptr + u);
					*loc += (unsigned long) j;
				}
			}
			break;
		case 1010:
			break;
		case 0xffffffff:
			break;
		default:
			clean ("Can't handle hunk type %d", (char *) hunk_type);
			free (hunk_table);
			return (-1);
			break;
		}
	} while (hunk_type != 0xffffffff);
	free (hunk_table);
	/* make MemList structure and TagPtr table */
	res_ptr = (unsigned short *) code;	/* search for ROMTAG */
	while ((char *) res_ptr < code + code_size) {
		if (*res_ptr == RTC_MATCHWORD)
			break;
		++res_ptr;
	}
	if (*res_ptr != RTC_MATCHWORD)
		res_ptr = 0;			/* didn't find ROMTAG */
	/* should abort if no ROMTAG found */
	/* also should verify ROMTAG:  rt_MatchTag == res_ptr,
	   rt_EndSkip <= code + code_size */
	myTagPtr[0] = (unsigned long) res_ptr;
	myTagPtr[1] = 0;
	myMemList->ml_Node.ln_Succ = NULL;
	myMemList->ml_Node.ln_Pred = NULL;
	myMemList->ml_Node.ln_Type = NT_MEMORY;
	myMemList->ml_Node.ln_Pri = 0;
	/* use ROMTAG name as MemList node name */
	/* todo - use fname as the MemList node name? */
	if (res_ptr)
		myMemList->ml_Node.ln_Name = ((struct Resident *) res_ptr)->rt_Name;
	myMemList->ml_NumEntries = 1;
	myMemList->ml_ME[0].me_Un.meu_Addr = (APTR) ((char *) myMemList - 8);
	myMemList->ml_ME[0].me_Length = mem_size;
	return (0);
}

unsigned long getlong ()
{
	unsigned long u;

	if (fread ((char *) &u, 4, 1, fp) != 1)
		return (0xffffffff);
	return (u);
}

void clean (text, arg)
char	*text, *arg;
{
	printf ("KickLoad: ");
	printf (text, arg);
	printf ("\n");
	if (fp) {
		fclose (fp);
		fp = NULL;
	}
	if (code) {
		FreeMem (code, mem_size);
		code = NULL;
	}
}

/*
 *  display_kick - display Exec KickStart delta list
 *
 */

void display_kick ()
{
	int	i, l;
	struct Resident *res_ptr;
	char *start_quote, *end_quote;

	KickMem = (struct MemList *) (*ExecBase)->KickMemPtr;
	KickTag = (long *) (*ExecBase)->KickTagPtr;
	printf ("KickMemPtr = $%08x\n", KickMem);
	while (KickMem) {
		printf ("  MemList = $%08x \"%s\"\n", KickMem,
			KickMem->ml_Node.ln_Name ? KickMem->ml_Node.ln_Name :
			"*unnamed*");
		for (i = 0; i < KickMem->ml_NumEntries; ++i) {
			printf ("    [%d] Addr = $%08x, Length = $%x\n",
				i, KickMem->ml_ME[i].me_Un.meu_Addr,
				KickMem->ml_ME[i].me_Length);
		}
		KickMem = (struct MemList *) KickMem->ml_Node.ln_Succ;
	}
	printf ("KickTagPtr = $%08x\n", KickTag);
	while (KickTag) {
		printf ("  RomTags at $%08x:\n", KickTag);
		i = 0;
		while (KickTag[i] > 0) {
			res_ptr = (struct Resident *) KickTag[i];
			printf ("  [%d] -> $%08x", i++, res_ptr);
			if (res_ptr == NULL) {
				printf (" <No ROMTAG>\n");
				continue;
			}
			start_quote = "\"";
			end_quote = "\"\n";
			if (res_ptr->rt_IdString && (l = strlen (res_ptr->rt_IdString))) {
				if (res_ptr->rt_IdString[l-1] == '\n')
					start_quote = end_quote = "";
			}
			printf ("  \"%s\" Type=%d Pri=%d Id=%s%s%s",
			    res_ptr->rt_Name,
			    res_ptr->rt_Type, res_ptr->rt_Pri,
			    start_quote, res_ptr->rt_IdString, end_quote);
			if (res_ptr->rt_MatchWord != RTC_MATCHWORD)
				printf ("\t**** Invalid MatchWord: $%04x\n",
					res_ptr->rt_MatchWord);
		}
		KickTag = (long *) (KickTag[i] & 0x7fffffff);
	}
}

/*
 *
 *  clear_kick - remove the Exec Kickstart delta list
 *
 *  Should add optional arguement to specify which entry to remove.
 *  currently the entire list is removed.
 *
 */

void clear_kick ()
{
	int	i;

	KickMem = (struct MemList *) (*ExecBase)->KickMemPtr;
	KickTag = (long *) (*ExecBase)->KickTagPtr;
	printf ("KickMemPtr = $%08x\n", KickMem);
	while (KickMem) {
		printf ("  MemList = $%08x \"%s\"\n", KickMem,
			KickMem->ml_Node.ln_Name ? KickMem->ml_Node.ln_Name :
			"*unnamed*");
		for (i = 0; i < KickMem->ml_NumEntries; ++i) {
			printf ("    [%d] Addr = $%08x, Length = $%x\n",
				i, KickMem->ml_ME[i].me_Un.meu_Addr,
				KickMem->ml_ME[i].me_Length);
		}
#if 0	/* don't deallocate memory - it may still be in use */
		Forbid ();
		for (i = 0; i < KickMem->ml_NumEntries; ++i) {
			FreeMem (KickMem->ml_ME[i].me_Un.meu_Addr,
			    KickMem->ml_ME[i].me_Length);
		}
		Permit ();
#endif
		printf (" .. Removed\n");
		KickMem = (struct MemList *) KickMem->ml_Node.ln_Succ;
	}
	Forbid ();
	(*ExecBase)->KickMemPtr = NULL;
	(*ExecBase)->KickTagPtr = NULL;
	
}
