/*
***************************************************************************
*
* Datei:
*    RSysHunks.c
*
* Inhalt:
*    void PrintHunkStruct(char *file);
*    void HunkStruct(void);
*
* 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 "RSysDebug.h"
#include "RSysFunc.h"

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

static struct Remember *hekey = NULL;

#define RKEY   ((struct Remember **)&hekey)
#define SIZEDE sizeof(struct hunkentry)

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

extern int decnt,breakit;

extern UBYTE *TreeWdt;
extern UBYTE *TreeWdt1;

enum
{
   GD_TreeLV,
   GD_SaveGad,
   GD_KindCY
};

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 char hunkblanks[] =
{
   "", "",
   "  ", "  ", "  ",
   "    ", "    ", "    ", "    ", "    ", "    ",
   "", "", "",
   "  ", "  ",
   "", "", "",
   "  ", "  "
};
*/
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(RKEY, SIZEDE,
                                                  MEMF_CLEAR | MEMF_ANY)) &&
       (last->name = (char *)AllocRemember(RKEY, 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],NULL,UNSETLVPOS);

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

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

      decnt++;

      return (last->next);
   }
   else
      ErrorHandle(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] = "  ",
         *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() error!");

               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() error!");
                     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() error!");
                        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() error!");

                        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() error!");
                        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() error!");

               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() error!");

               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() error!");

               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], FALSE);
   EnableGadget(TreeWnd, TreeGadgets[GD_KindCY], FALSE);

   ScanHunks(file);

   SetWindowTitles(TreeWnd, TreeWdt1, NOSCREENTITLECHANGE);
   EnableGadget(TreeWnd, TreeGadgets[GD_SaveGad], TRUE);
   EnableGadget(TreeWnd, TreeGadgets[GD_KindCY], 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;
   APTR  req;
   int id;

   DPOS;

   Flags.quit_hunk = 0;

   NewList(&Tree);

   if (NOT(OpenTreeWindow(NULL, TRUE)))
   {
      if (SysWnd)
      {
         req = LockWindow(SysWnd);
         PrintInfo("Scanning Hunks", SPEAK, 0);
      }

      decnt = 0;

      if(file)
      {
         InitListView(TreeWnd,TreeGadgets[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;

                  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],NULL,UNSETLVPOS);

                        if (NOT(IsListEmpty(&Tree)))
                        {
                           NewList(&Tree);
                           FreeRemember(RKEY, TRUE);
                        }

                        decnt = 0;
                        breakit = FALSE;

                        if (GetFile(TreeWnd,NULL,NULL,NULL,
                                    "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],NULL,UNSETLVPOS);

      if (NOT(IsListEmpty(&Tree)))
      {
         NewList(&Tree);
         FreeRemember(RKEY, TRUE);
      }

      CloseASysWindow(&TreeWnd, &TreeGList, NULL);

      if (SysWnd)
      {
         UnlockWindow(req);
         RefreshMainWindowPattern();
         RefreshList(LastID);
      }
   }
   else
      ErrorHandle(WINDOW_ERR, OPEN_FAIL, NO_KILL);

   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;
}

