/*
   ***************************************************************************
   *
   * 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"
#include "protos.h"


static REMEMBER *hekey = NULL;


static RSYS_hunkentry *loop, *firstde;
static 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;

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

  if ((last = (RSYS_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) - (LONG) sizeof (ULONG);	/*GMD */
}

 /*
  * 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 = make_hunkentry (loop, buffer);

  sprintf (buffer, "File size : %ld", SizeOfFile (filename));
  loop = make_hunkentry (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 = make_hunkentry (loop, (char *) field[BLANK_FIELD]);
	      sprintf (buffer, "%s (o: \$%lX,\#%ld t: \$%lX,\#%ld)",
		       names[type], currentpos (),
		       currentpos (), data, data);
	      loop = make_hunkentry (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 = make_hunkentry (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 = make_hunkentry (loop, buffer);

	      if (type_of_hunk != hunk_bss)
		if (NOT (jumpinfile (data)))
		  loop = make_hunkentry (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 = make_hunkentry (loop, buffer);

		  if (NOT (jumpinfile (data2)))
		    {
		      loop = make_hunkentry (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 = make_hunkentry (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 = make_hunkentry (loop, buffer);

		      if (NOT (jumpinfile (data2)))
			loop = make_hunkentry (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 = make_hunkentry (loop, buffer);

		      if (NOT (jumpinfile (data2)))
			loop = make_hunkentry (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 = make_hunkentry (loop, buffer);

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

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

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

	      sprintf (buffer, "      External refs: %ld", z);
	      loop = make_hunkentry (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 = make_hunkentry (loop, buffer);

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

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

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

	      if (NOT (jumpinfile (data)))
		loop = make_hunkentry (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 = make_hunkentry (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 = make_hunkentry (loop, buffer);

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

	      error = readlong (&data3);
	      sprintf (buffer, "  Last hunk   : \#%ld", data3);
	      loop = make_hunkentry (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 = make_hunkentry (loop, buffer);
		    }
		  else
		    {
		      sprintf (buffer, "    Hunk \#%ld, Length ??", i);
		      loop = make_hunkentry (loop, buffer);
		    }
		}

	      break;

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

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

	      break;

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

	      break;

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

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

	      break;

	    default:
	      loop = make_hunkentry (loop, (char *) field[BLANK_FIELD]);
	      sprintf (buffer, "%s: \#%ld (\$%lX)", "Unknown hunk type position",
		       currentpos (), currentpos ());
	      loop = make_hunkentry (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)
{
  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 (WIN_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 = (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 = ((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 (WIN_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;
}
