/*
***************************************************************************
*
* Datei:
*	  RSysHunks.c
*
* Inhalt:
*
*      --- Globale Routinen ---
*
*    void HunkStruct ( void );
*    void PrintHunkStruct ( char *file );
*
*      --- Lokale  Routinen ---
*
*    static int jumpinfile ( ULONG d );
*    static int readlong ( ULONG *d );
*    static int readtext ( char *d , long count );
*    static long currentpos ( void );
*    static struct hunkentry *makehunkentry ( struct hunkentry *last , char *name );
*    static void MakeHunkList ( char *file );
*    static void ScanHunks ( char *filename );
*
* Bemerkungen:
*	  Ermittlung der Hunkstruktur einer ausführbaren oder ladbaren Datei.
*
* Erstellungsdatum:
*	  07-Jul-93 	Rolf Böhme
*
* Änderungen:
*	  07-Jul-93 	Rolf Böhme	  Erstellung
*
***************************************************************************
*/

#include "RSys.h"

static struct hunkentry
{
	struct Node denode;
	char *name;
	struct hunkentry *next;
};

static struct Remember *hekey = NULL;

extern struct Window *TreeWnd;
extern struct Gadget *TreeGadgets[3],*TreeGList;

extern int decnt,breakit;

extern UBYTE *TreeWdt;
extern UBYTE *TreeWdt1;

static struct hunkentry *loop, *firstde;
static struct List Tree;

static enum ext_types1
{
	ext_def = 1,
	ext_abs,
	ext_res
};

static char *ext_names1[] =
{
	"dummy   ",
	"ext_def ",
	"ext_abs ",
	"ext_res "
};

static enum ext_types2
{
	ext_ref32 = 129,
	ext_common,
	ext_ref16,
	ext_ref8,
	ext_dref32,
	ext_dref16,
	ext_dref8
};

static char *ext_names2[] =
{
	"ext_ref32  ",
	"ext_common ",
	"ext_ref16  ",
	"ext_ref8   ",
	"ext_dref32 ",
	"ext_dref16 ",
	"ext_dref8  "
};

static enum hunktypes
{
	hunk_unit = 0x3e7,
	hunk_name,						  /* 0x3e8 */
	hunk_code,						  /* 0x3e9 */
	hunk_data,						  /* 0x3ea */
	hunk_bss,						  /* 0x3eb */
	hunk_reloc32,					  /* 0x3ec */
	hunk_reloc16,					  /* 0x3ed */
	hunk_reloc8,					  /* 0x3ee */
	hunk_ext,						  /* 0x3ef */
	hunk_symbol,					  /* 0x3f0 */
	hunk_debug, 					  /* 0x3f1 */
	hunk_end,						  /* 0x3f2 */
	hunk_header,					  /* 0x3f3 */
	dummy,							  /* 0x3f4 */
	hunk_overlay,					  /* 0x3f5 */
	hunk_break, 					  /* 0x3f6 */
	dummy1,							  /* 0x3f7 */
	dummy2,							  /* 0x3f8 */
	dummy3,							  /* 0x3f9 */
	hunk_lib,						  /* 0x3fa */
	hunk_libindex					  /* 0x3fb */
};

char *names[]=
{
	"hunk_unit",
	"hunk_name",
	"  hunk_code",
	"  hunk_data",
	"  hunk_bss",
	"    hunk_reloc32",
	"    hunk_reloc16",
	"    hunk_reloc8 ",
	"    hunk_ext",
	"    hunk_symbol",
	"  hunk_debug",
	"hunk_end",
	"hunk_header",
	"dummy",
	"hunk_overlay",
	"hunk_break  ",
	"dummy",
	"dummy",
	"dummy",
	"lib_hunk",
	"lib_index"
};

static BPTR file;

	/*
	 * makehunkentry() erzeugt einen Listeneintrag aus einem Hunk
	 * einer ausführbaren Datei
	 */
static struct hunkentry *
makehunkentry(struct hunkentry *last, char *name)
{
	DPOS;

	if ((last = (struct hunkentry *) AllocRemember(ERKEY, ESIZEDE, MEMF_CLEAR | MEMF_ANY)) &&
		 (last->name = (char *)AllocRemember(ERKEY, strlen(name) + 1, MEMF_CLEAR | MEMF_ANY)))
	{
		strcpy(last->name, name);

		last->next           = NULL;
		last->denode.ln_Name = last->name;
		last->denode.ln_Type = 0;

		InitListView(TreeWnd,TreeGadgets[GD_TreeLV - GD_TreeLV],NULL,UNSETLVPOS);

		AddTail(&Tree, &last->denode);

		InitListView(TreeWnd,TreeGadgets[GD_TreeLV - GD_TreeLV],&Tree,decnt);

		decnt++;

		return last->next;
	}
	else ErrorHandle("Hunk memory", MEMORY_ERR, ALLOC_FAIL, KILL);

	return NULL;
}

	/*
	 * jumpinfile() setzt den Dateizeiger um eine Anzahl ULONG's
	 * weiter vor
	 */
static int
jumpinfile(ULONG d)
{
	int serr;

	serr = Seek(file, d * sizeof(ULONG), OFFSET_CURRENT);
	if (serr < 0) return FALSE;

	return TRUE;
}

	/*
	 * readlong() liest ein ULONG aus einer Datei aus
	 */
static int
readlong(ULONG *d)
{
	return Read(file, d, sizeof(ULONG));
}

	/*
	 * readtext() liest einen Text definierter Länge aus
	 * einer Datei aus
	 */
static int
readtext(char *d,long count)
{
	ULONG readdata[MAXSTRLEN];
	int ret = 0,i,len = 0;
	union {
		ULONG ul;
		unsigned char ut[4];
	} cv;

	if(count == 0)
	{
		strcpy(d, field[NO_FIELD]);
		return ret;
	}

	ret = Read(file, readdata, count * sizeof(ULONG));

	d[0] = STRINGEND;
	cv.ul = 0L;
	cv.ul = readdata[0];
	strncpy(d,(char *)cv.ut,4);
	len +=4;
	d[len] = STRINGEND;

	for(i = 1; i < count; i++)
	{
		cv.ul = 0L;
		cv.ul = readdata[i];
		strncat(d,(char *)cv.ut,4);
		len += 4;
		d[len] = STRINGEND;
	}

	return ret;
}

	/*
	 * currentpos() ermittelt die aktuelle Position des Dateizeigers
	 */
static long
currentpos(void)
{
	return Seek(file, 0L, OFFSET_CURRENT) - sizeof(ULONG);
}

	/*
	 * ScanHunks() ermittelt alle Hunks eines ausführbaren Files und
	 * trägt diese in eine interne Liste ein
	 */
static void
ScanHunks(char *filename)
{
	ULONG data,
			data2,
			data3,
			type,
			typeofmem,
			type_of_hunk,
			lasthunk = hunk_name;
	char	datatext[120];
	int	error,
			i,
			z;
	char	buffer[MAXFULLNAME],
			blanks[10] = "  ",
         *seek_err = "Seek() error!",
			*memtype[3] = {"MEMF_CHIP","MEMF_FAST"};
	int	ext_type,failure = 0;

	DPOS;

	sprintf(buffer, "File name : %s", filename);
	loop = makehunkentry(loop, buffer);

	sprintf(buffer, "File size : %ld", SizeOfFile(filename));
	loop = makehunkentry(loop, buffer);

	if (file = Open((UBYTE *) filename, MODE_OLDFILE))
	{
		error = readlong(&data);

		while ((error > 0) && NOT(ClickedCloseGadget()))
		{
			type = data - hunk_unit;
			type_of_hunk = ((data << 8) >> 8);

			if ((type_of_hunk >= hunk_unit) && (type_of_hunk <= hunk_libindex) &&
				 (type_of_hunk != hunk_end)  && (type_of_hunk != hunk_break))
			{
				loop = makehunkentry(loop, (char *)field[BLANK_FIELD]);
				sprintf(buffer, "%s (o: \$%lx,\#%ld t: \$%lx,\#%ld)",
						  names[type], currentpos(),
						  currentpos(),data,data);
				loop = makehunkentry(loop, buffer);
			}

			switch (type_of_hunk)
			{
				case hunk_unit:
				case hunk_name:
					error = readlong(&data);

					if((error > 0) && data)
					{
						error = readtext(datatext,data);
						sprintf(buffer, "  Name: %s",(char *)datatext);
					}
					else strcpy(buffer, "  Name: -");

					loop = makehunkentry(loop, buffer);
					break;

				case hunk_code:
				case hunk_bss:
				case hunk_data:
					typeofmem = ((data & (1L<<30)) ? 0 : 1);

					error = readlong(&data);

					sprintf(buffer, "    Size: \$%lx, \#%ld (%s)", data * 4, data * 4,memtype[typeofmem]);
					loop = makehunkentry(loop, buffer);

					if (type_of_hunk != hunk_bss)
						if (NOT(jumpinfile(data))) loop = makehunkentry(loop, seek_err);

					break;

				case hunk_reloc32:
				case hunk_reloc16:
				case hunk_reloc8:
					error = readlong(&data);
					while ((error > 0) && data != 0L)
					{
						data2 = data;
						error = readlong(&data3);
						sprintf(buffer, "     \#%-3ld offsets in Hunk \#%-3ld", data, data3);
						loop = makehunkentry(loop, buffer);

						if (NOT(jumpinfile(data2)))
						{
							loop = makehunkentry(loop, seek_err);
							break;
						}

						error = readlong(&data);
					}

					break;

				case hunk_ext:
					error = readlong(&data);
					z = 0;

					while(data && (error > 0))
					{
						ext_type = (int)(data >> 24);
						data = (data << 8) >> 8;

						switch(ext_type)
						{
							case ext_def:
							case ext_abs:
							case ext_res:
								error = readtext(datatext,data);
								error = readlong(&data2);
								sprintf(buffer, "      %s: %-22s val: %ld",
											ext_names1[ext_type-ext_def+1],datatext, data2);

								loop = makehunkentry(loop, buffer);
								break;

							case ext_ref32:
							case ext_dref32:
								error = readtext(datatext,data);
								error = readlong(&data2);
								sprintf(buffer, "      %s: %-22s refs: %ld",
											ext_names2[ext_type-ext_ref32],datatext,data2);

								loop = makehunkentry(loop, buffer);

								if (NOT(jumpinfile(data2))) loop = makehunkentry(loop, seek_err);
								break;

							case ext_common:
								error = readlong(&data);
								error = readlong(&data2);
								sprintf(buffer, "      %s (size): %ld  refs: %ld",
										  ext_names2[ext_type-ext_ref32],data,data2);
								loop = makehunkentry(loop, buffer);

								if (NOT(jumpinfile(data2))) loop = makehunkentry(loop, seek_err);

								break;

							case ext_ref16:
							case ext_ref8:
							case ext_dref16:
							case ext_dref8:
								error = readtext(datatext,data);
								error = readlong(&data2);
								sprintf(buffer, "      %s: %-22s refs: %ld",
											ext_names2[ext_type-ext_ref32],datatext, data2);

								loop = makehunkentry(loop, buffer);

								if (NOT(jumpinfile(data2))) loop = makehunkentry(loop, seek_err);
								break;

							default:
								loop = makehunkentry(loop, "Unknown external reference!");
								break;
						}

						z++;
						error = readlong(&data);
					}

					sprintf(buffer, "      External refs: %ld",z);
					loop = makehunkentry(loop, buffer);
					break;

				case hunk_symbol:
					error = readlong(&data);
					z=0;
					while(data && (error > 0))
					{
						data = (data << 8) >> 8;
						error = readtext(datatext,data);
						error = readlong(&data);
						sprintf(buffer, "      Symbol: %-22s  val: %ld",
								  datatext,data);
						loop = makehunkentry(loop, buffer);

						error = readlong(&data);
						z++;
					}

					sprintf(buffer, "      External symbols: %ld", z);
					loop = makehunkentry(loop, buffer);
					break;

				case hunk_debug:
					error = readlong(&data);
					sprintf(buffer, "  \$%lx, \#%ld",data * 4, data * 4);
					loop = makehunkentry(loop, buffer);

					if (NOT(jumpinfile(data))) loop = makehunkentry(loop, seek_err);

					break;

				case hunk_end:
					if(lasthunk >= hunk_code && lasthunk <= hunk_debug) strcpy(blanks,"  ");
					else
						strcpy(blanks,"      ");

					sprintf(buffer, "%s%s (o: \$%lx,\#%ld t: \$%lx,\#%ld)",
							  blanks,names[type], currentpos(), currentpos(),data,data);

					loop = makehunkentry(loop, buffer);
					break;

				case hunk_header:
					error = readlong(&data);

					while ((error > 0) && data != 0L)
						error = readlong(&data);

					error = readlong(&data);
					sprintf(buffer, "  Count hunks : \#%ld",data);
					loop = makehunkentry(loop, buffer);

					error = readlong(&data2);
					sprintf(buffer, "  First hunk  : \#%ld", data2);
					loop = makehunkentry(loop, buffer);

					error = readlong(&data3);
					sprintf(buffer, "  Last hunk   : \#%ld", data3);
					loop = makehunkentry(loop, buffer);

					for (i = data2; error && i <= data3; i++)
					{
						error = readlong(&data);
						if (error > 0)
						{
							sprintf(buffer, "    Hunk \#%ld, Length \$%lx, \#%ld",
									  i, data * 4, data * 4);
							loop = makehunkentry(loop, buffer);
						}
						else
						{
							sprintf(buffer, "    Hunk \#%ld, Length ??", i);
							loop = makehunkentry(loop, buffer);
						}
					}

					break;

				case hunk_overlay:
					error = readlong(&data);
					sprintf(buffer, "  Table size: \$%lx, \#%ld",data, data);
					loop = makehunkentry(loop, buffer);

					if (NOT(jumpinfile(data))) loop = makehunkentry(loop, seek_err);

					break;

				case hunk_break:
					loop = makehunkentry(loop, (char *)field[BLANK_FIELD]);
					sprintf(buffer, "%s (o: \$%lx,\#%ld t: \$%lx,\#%ld)",
							  names[type], currentpos(), currentpos(),data,data);
					loop = makehunkentry(loop, buffer);

					break;

				case hunk_lib:
				case hunk_libindex:
					error = readlong(&data);
					sprintf(buffer, "\$%lx, \#%ld", data * 4, data * 4);
					loop = makehunkentry(loop, buffer);

					if (NOT(jumpinfile(data))) loop = makehunkentry(loop, seek_err);

					break;

				default:
					loop = makehunkentry(loop, (char *)field[BLANK_FIELD]);
					sprintf(buffer, "%s: \#%ld (\$%lX)", "Unknown hunk type position",
							  currentpos(),currentpos());
					loop = makehunkentry(loop, buffer);
					failure++;

					if(failure >= 5) Seek(file, 0L, OFFSET_END);
					break;
			}

			lasthunk = type_of_hunk;

			error = readlong(&data);
		}

		Close(file);
	}

	return;
}

	/*
	 * MakeHunkList() erzeugt aus einem ausführbaren File eine
	 * Liste aus den Hunks, aus denen das Executable besteht. Zuvor
	 * werden die Gadgets deaktiviert
	 */
static void
MakeHunkList(char *file)
{
	loop = firstde;

	if(SysWnd) PrintInfo("Scanning Hunks", SPEAK, 0);

	SetWindowTitles(TreeWnd, (UBYTE *) "<- Cancel reading Hunks...", NOSCREENTITLECHANGE);
	EnableGadget(TreeWnd, TreeGadgets[GD_SaveGad - GD_TreeLV], FALSE);
	EnableGadget(TreeWnd, TreeGadgets[GD_KindCY - GD_TreeLV], FALSE);

	ScanHunks(file);

	SetWindowTitles(TreeWnd, TreeWdt1, NOSCREENTITLECHANGE);
	EnableGadget(TreeWnd, TreeGadgets[GD_SaveGad - GD_TreeLV], TRUE);
	EnableGadget(TreeWnd, TreeGadgets[GD_KindCY - GD_TreeLV], TRUE);

	return;
}

	/*
	 * PrintHunkStruct() bietet eine kleine Benutzeroberfläche an
	 * und ermittelt die Hunks eines Executables. Diese werden dann
	 * in einem ListView angezeigt
	 */
void
PrintHunkStruct(char *file)
{
	struct IntuiMessage *message;
	ULONG class,
			code;
	APTR	object;
	int id;

	DPOS;

   HandleHelp(MN_HunkStruct);

	Flags.quit_hunk = 0;

	NewList(&Tree);

	if (NOT(OpenTreeWindow(NULL, TRUE)))
	{
		PrintInfo("Scanning Hunks", SPEAK, 0);

      LockMainWindow(LOCK);

		decnt = 0;

		if(file)
		{
			InitListView(TreeWnd,TreeGadgets[GD_TreeLV - GD_TreeLV],NULL,UNSETLVPOS);

			breakit = FALSE;

			MakeHunkList(file);
		}

		do
		{
			Wait(1L << TreeWnd->UserPort->mp_SigBit);

			while ((message = (struct IntuiMessage *) GT_GetIMsg(TreeWnd->UserPort)) != NULL)
			{
				class = message->Class;
				code = message->Code;
				object = message->IAddress;

				GT_ReplyIMsg(message);

				switch(class)
				{
					case IDCMP_CLOSEWINDOW:
						Flags.quit_hunk = 1;
						break;

					case IDCMP_GADGETUP:
						id = ((struct Gadget *)object)->GadgetID;

                  HandleHelp((enum RSysNumbers)id);

						switch(id)
						{
							case GD_SaveGad:
								if (GetFile(TreeWnd,"RAM:","RSys-Hunk.DAT","#?.dat",
												"Select File for saving hunklist","Save"))
									SaveList(TreeWnd, (char *)_fullpath,
												(char *)"RSys-Hunk.DAT",&Tree, FALSE);
								break;

							case GD_KindCY:
								InitListView(TreeWnd,TreeGadgets[GD_TreeLV - GD_TreeLV],NULL,UNSETLVPOS);

								NewList(&Tree);
								FreeRemember(ERKEY, TRUE);

								decnt = 0;
								breakit = FALSE;

								if (GetFile(TreeWnd,"C:","unknown","#?", "Select File for Hunk list","Show"))
									MakeHunkList((char *)_fullpath);

								break;
						}
						break;

					case IDCMP_VANILLAKEY:
						if((char)code == ESC) Flags.quit_hunk = 1;
						break;
				}
			}
		} while (NOT(Flags.quit_hunk));

		InitListView(TreeWnd,TreeGadgets[GD_TreeLV - GD_TreeLV],NULL,UNSETLVPOS);

		NewList(&Tree);
		FreeRemember(ERKEY, TRUE);

		CloseASysWindow(&TreeWnd, &TreeGList, NULL);

      LockMainWindow(UNLOCK);
	}
	else ErrorHandle((char *)TreeWdt1, WINDOW_ERR, OPEN_FAIL, NO_KILL);

   PrintStatistics();

	return;
}

	/*
	 * PrintHunkStruct() kann mit einem Parameter aufgerufen werden
	 * oder ohne. Kommt der Aufruf nicht vom RSysAppIcon, so wird kein
	 * Filename übergeben und der Aufruf wird von HunkStruct() aus
	 * getätigt
	 */
void
HunkStruct(void)
{
	DPOS;

	PrintHunkStruct(NULL);
	return;
}

