#define NAME	 "HunkFunc"
#define REVISION "11"

/* Programmheader

	Name:		HunkFunk
	Author:		SDI
	Distribution:	PD
	Description:	object/binary file scanner
	Compileropts:	-
	Linkeropts:	-gsi -l amiga

 1.0   12.02.98 : made with help of Olaf 'Olsen' Barthel's HunkFunk
 1.1   23.03.98 : now prints reloc infos
 1.2   23.11.98 : added new overlay format
 1.3   09.12.98 : added CHIP, FAST, ADVISORY to CODE/DATA/BSS hunks
 1.4   28.12.98 : unknown hunk now prints 8 digits
 1.5   30.08.99 : 4 new switches for command line, fixed lots of stuff
 1.6   13.09.99 : better support for Overlay files
 1.7   15.09.99 : changed overlay support, added SOVT keyword
 1.8   27.09.99 : displays extra stuff message
 1.9   03.11.99 : added number of EXT_REF's
 1.10  13.11.99 : added H&P EHF stuff
 1.11  29.11.99 : added CTRL_C to reloc print
*/

#include <proto/dos.h>
#include <proto/exec.h>
#include <exec/memory.h>
#include <dos/doshunks.h>
#include "SDI_defines.h"
#define SDI_TO_ANSI
#include "SDI_ASM_STD_protos.h"

/* CHIP, FAST and ADVISORY information is repeated in hunk itself. If
the word is used normally, the information is in second longword (size).
If it is preceeded by a '_', the information is in first longword (hunk
type). */

#define PARAM "FILE/M/A,SREL=SHOWRELOC/S,SSYM=SHOWSYMBOL/S,SEXT=SHOWEXTERN/S,SOVT=SHOWOVERLAYTABLE/S,STATS/S"

/* Some simple macros. */
#define ReadLong(Type)	  Read(FileHandle, (Type), 4)
#define ReadWord(Type)	  Read(FileHandle, (Type), 2)
#define ReadName(Longs)   Read(FileHandle, NameString, 4 * (Longs))
#define SkipLong(Longs)   Seek(FileHandle, 4 * (Longs), OFFSET_CURRENT)
#define SkipWord(Words)	  Seek(FileHandle, 2* (Words), OFFSET_CURRENT)
#define SkipByte(Bytes)	  Seek(FileHandle, (Bytes), OFFSET_CURRENT)
#define MakeName(Longs)   NameString[(Longs) * 4] = 0
#define GetHunkName(Type) HunkNames[(Type)-HUNK_UNIT]

STRPTR HunkNames[] = { /* access them with HunkNames[Type-HUNK_UNIT] */
"HUNK_UNIT",	"HUNK_NAME",	"HUNK_CODE",	"HUNK_DATA",	"HUNK_BSS", 
"HUNK_(ABS)RELOC32",	"HUNK_(REL)RELOC16",	"HUNK_(REL)RELOC8",	"HUNK_EXT",	"HUNK_SYMBOL",
"HUNK_DEBUG",	"HUNK_END",	"HUNK_HEADER",	"",		"HUNK_OVERLAY",
"HUNK_BREAK",	"HUNK_DREL32",	"HUNK_DREL16",	"HUNK_DREL8",	"HUNK_LIB",
"HUNK_INDEX",	"HUNK_RELOC32SHORT",	"HUNK_RELRELOC32",	"HUNK_ABSRELOC16",
"HUNK_DREL32EXE"
};

#define HUNK_DREL32EXE	(HUNK_ABSRELOC16+1)

#define HUNK_PPC_CODE	0x4E9
#define HUNK_RELRELOC26	0x4EC
#define EXT_RELREF26	229

struct Args {
  STRPTR * files;
  ULONG    showreloc;
  ULONG    showsymbol;
  ULONG    showextern;
  ULONG	   showoverlaytable;
  ULONG    stats;
};

struct Stats {
  ULONG Header;
  ULONG Unit;
  ULONG Lib;
  ULONG Code;
  ULONG Data;
  ULONG BSS;
  ULONG Debug;
  ULONG Symbol;
  ULONG Extern;
  ULONG Reloc;
  ULONG NumSymbol;
  ULONG NumReloc;
  ULONG NumExtern;
  ULONG CodeSize;
  ULONG DataSize;
  ULONG BSSSize;
  ULONG DebugSize;
  ULONG PPCCode;
  ULONG PPCCodeSize;
};

struct DosLibrary *DOSBase = 0;
struct ExecBase * SysBase  = 0;

STRPTR GetTabs(STRPTR);
void ShowStats(struct Stats *);
LONG ProcessFile(ULONG, ULONG, ULONG, ULONG, ULONG, struct Stats *);

ULONG start(void)
{
  ULONG ret = RETURN_FAIL;
  struct DosLibrary *dosbase;
  SysBase = (*((struct ExecBase **) 4));

  { /* test for WB and reply startup-message */
    struct Process *task;
    if(!(task = (struct Process *) FindTask(0))->pr_CLI)
    {
      WaitPort(&task->pr_MsgPort);
      Forbid();
      ReplyMsg(GetMsg(&task->pr_MsgPort));
      return RETURN_FAIL;
    }
  }

  if((dosbase = (struct DosLibrary *) OpenLibrary("dos.library", 37)))
  {
    struct Args args;
    struct RDArgs *rda;

    DOSBase = dosbase;

    memset(&args, 0, sizeof(struct Args));
    if((rda = ReadArgs(PARAM, (LONG *) &args, 0)))
    {
      ret = 0;
      while(*args.files)
      {
        ULONG err = 0, fh;

        if((fh = Open(*args.files, MODE_OLDFILE)))
        {
	  struct Stats stats;

          Printf("File '%s':\n", *args.files);
          memset(&stats, 0, sizeof(struct Stats));
          err = ProcessFile(fh, args.showreloc, args.showsymbol, args.showextern, args.showoverlaytable, &stats);
          if(args.stats)
            ShowStats(&stats);
          Close(fh);
        }
        if(CTRL_C)
          SetIoErr(ERROR_BREAK);
        if(!err)
          PrintFault(IoErr(), *args.files);
        ++args.files;
      }
    }
    
    if(ret)
      PrintFault(IoErr(), 0);

    CloseLibrary((struct Library *) dosbase);
  }
  return ret;
}

STRPTR GetTabs(STRPTR NameString)
{
  ULONG i = 0;

  while(*(NameString++) && i < 24)
   ++i;
  i >>= 3;
  
  return "\t\t\t"+i;
}

void ShowStats(struct Stats *stats)
{
  Printf("STATISTICS:\nFile type\t");
  if(stats->PPCCode)
    Printf("PPC-WOS Objectfile");
  else if(stats->Header)
    Printf("Executable");
  else if(stats->Lib)
    Printf("New style link library");
  else if(stats->Unit > 1)
    Printf("Old style link library");
  else
    Printf("Objectfile");
  Printf("\n");

  Printf("HUNK_CODE\t%6ld with total %6ld ($%06lx) Bytes\n", stats->Code, stats->CodeSize, stats->CodeSize);
  if(stats->PPCCode)
    Printf("HUNK_PPC_CODE\t%6ld with total %6ld ($%06lx) Bytes\n", stats->PPCCode, stats->PPCCodeSize, stats->PPCCodeSize);
  Printf("HUNK_DATA\t%6ld with total %6ld ($%06lx) Bytes\n", stats->Data, stats->DataSize, stats->DataSize);
  Printf("HUNK_BSS\t%6ld with total %6ld ($%06lx) Bytes\n", stats->BSS, stats->BSSSize, stats->BSSSize);
  Printf("HUNK_DEBUG\t%6ld with total %6ld ($%06lx) Bytes\n", stats->Debug, stats->DebugSize, stats->DebugSize);
  Printf("HUNK_EXT\t%6ld with total %6ld entr%s\n", stats->Extern, stats->NumExtern, stats->NumExtern == 1 ? "y" : "ies");
  Printf("HUNK_RELOC\t%6ld with total %6ld entr%s\n", stats->Reloc, stats->NumReloc, stats->NumReloc == 1 ? "y" : "ies");
  Printf("HUNK_SYMBOL\t%6ld with total %6ld entr%s\n", stats->Symbol, stats->NumSymbol, stats->NumSymbol == 1 ? "y" : "ies");
}

/* return 0 on error */
LONG ProcessFile(ULONG FileHandle, ULONG showreloc, ULONG showsymbol, ULONG showextern, ULONG showoverlaytable,
struct Stats *stats)
{
  ULONG Type, Data, i;
  UBYTE NameString[257];

  while(!CTRL_C)
  {
    if((i = ReadLong(&Type)) != 4)
    {
      if(i)
        Printf("There %s %ld extra byte%s at end of file.\n", i == 1 ? "is" : "are", i, i == 1 ? "" : "s");
      return 1;
    }

    if(Type == HUNK_DREL32 && stats->Header)
      Type = HUNK_DREL32EXE;

    /* Look which type it is. */
    switch(Type & 0xFFFF)
    {
    case HUNK_UNIT:
      ++stats->Unit;
      ReadLong(&Data);
      if(Data)
        ReadName(Data);
      MakeName(Data);
      Printf("%s\t\"%s\"\n\n", GetHunkName(Type), NameString);
      break;
    case HUNK_NAME:
      ReadLong(&Data);
      if(Data)
        ReadName(Data);
      MakeName(Data);
      Printf("%s\t\"%s\"\n", GetHunkName(Type), NameString);
      break;
    case HUNK_LIB:
      ++stats->Lib;
      ReadLong(&Data);
      Printf("%s\t%6ld ($%06lx) Bytes\n\n", GetHunkName(Type), Data, Data);
      break;
    case HUNK_INDEX:
      {
        UWORD dat2;

        ReadLong(&Data);
        Printf("%s\t%6ld ($%06lx) Bytes\n", GetHunkName(Type), Data, Data);
        ReadWord(&dat2);
        SkipByte((Data*4)-2);
	Printf("  Textsize =\t%6ld Bytes\n\n", dat2);
      }
      break;
    case HUNK_CODE: case HUNK_DATA: case HUNK_BSS:
      ReadLong(&Data);
      i = Data;
      Data <<= 2;
      Data &= 0x7FFFFFFF;
      switch(Type&0xFFFF)
      {
      case HUNK_CODE: ++stats->Code; stats->CodeSize += Data; SkipByte(Data); break;
      case HUNK_DATA: ++stats->Data; stats->DataSize += Data; SkipByte(Data); break;
      case HUNK_BSS: ++stats->BSS; stats->BSSSize += Data; break;
      }
      Printf("%s\t%6ld ($%06lx) Bytes", GetHunkName(Type), Data, Data);
      if(Type & HUNKF_CHIP)		Printf(" _CHIP");
      if(Type & HUNKF_FAST)		Printf(" _FAST");
      if(Type & HUNKF_ADVISORY) 	Printf(" _ADVISORY");
      if(i & HUNKF_CHIP)		Printf(" CHIP");
      if(i & HUNKF_FAST)		Printf(" FAST");
      if(i & HUNKF_ADVISORY) 		Printf(" ADVISORY");
      Printf("\n");
      break;
    case HUNK_PPC_CODE:
      ReadLong(&Data);
      i = Data;
      Data <<= 2;
      Data &= 0x7FFFFFFF;
      ++stats->PPCCode; stats->PPCCodeSize += Data;
      SkipByte(Data);
      Printf("HUNK_PPC_CODE\t%6ld ($%06lx) Bytes", Data, Data);
      if(Type & HUNKF_CHIP)		Printf(" _CHIP");
      if(Type & HUNKF_FAST)		Printf(" _FAST");
      if(Type & HUNKF_ADVISORY) 	Printf(" _ADVISORY");
      if(i & HUNKF_CHIP)		Printf(" CHIP");
      if(i & HUNKF_FAST)		Printf(" FAST");
      if(i & HUNKF_ADVISORY) 		Printf(" ADVISORY");
      Printf("\n");
      break;
    case HUNK_RELRELOC32: case HUNK_ABSRELOC16: case HUNK_RELRELOC26:
    case HUNK_RELOC32: case HUNK_RELOC16: case HUNK_RELOC8:
    case HUNK_DREL32: case HUNK_DREL16: case HUNK_DREL8:
      if(Type == HUNK_RELRELOC26)
        Printf("HUNK_RELRELOC26\n");
      else
        Printf("%s\n", GetHunkName(Type));
      ++stats->Reloc;

      do
      {
	ReadLong(&Data);
	if(Data)
	{
	  ULONG dat2 = 0, dat3;
	  ReadLong(&dat2);
	  stats->NumReloc += Data;
	  Printf("  Summary\t%6ld entr%s to hunk %ld\n", Data,
	  Data == 1 ? "y" : "ies", dat2);
	  if(showreloc)
	  {
	    for(dat3 = Data; dat3 && !CTRL_C; --dat3)
	    {
	      ReadLong(&dat2);
	      Printf("  Offset\t\t$%08lx\n", dat2);
	    }
	  }
	  else
	    SkipLong(Data);
	}
      } while(!CTRL_C && Data);
      break;
    case HUNK_RELOC32SHORT: case HUNK_DREL32EXE:
      {
        UWORD Data = 0;
        ULONG nums = 0;
        Printf("%s\n", GetHunkName(Type));
        ++stats->Reloc;

        do
        {
	  UWORD dat2 = 0;
	  ULONG dat3;
	  ReadWord(&Data);
	  if(Data)
	  {
            ReadWord(&dat2);
            stats->NumReloc += Data;
	    Printf("  Summary\t%6ld entr%s to hunk %ld\n", Data,
	    Data == 1 ? "y" : "ies", dat2);
	    if(showreloc)
	    {
	      for(dat3 = Data; dat3 && !CTRL_C; --dat3)
	      {
	        ReadWord(&dat2);
	        Printf("  Offset\t\t$%04lx\n", dat2);
	      }
	    }
	    else
	      SkipWord(Data);
	    nums += Data;
	  }
        } while(!CTRL_C && Data);
        if(!(nums & 1))
          SkipWord(1);
      }
      break;
    case HUNK_EXT:
      Printf("HUNK_EXT\n");
      ++stats->Extern;

      do
      {
	LONG i;

        ReadLong(&Data);
        if(!Data)
	  break;
	++stats->NumExtern;
	if(showextern)
	{
          switch((Data >> 24) & 0xFF)
	  {
	  case EXT_DEF:       Printf("  EXT_DEF"); break;
	  case EXT_ABS:       Printf("  EXT_ABS"); break;
	  case EXT_RES:       Printf("  EXT_RES"); break;
	  case EXT_REF32:     Printf("  EXT_REF32"); break;
	  case EXT_COMMON:    Printf("  EXT_COMMON"); break;
	  case EXT_REF16:     Printf("  EXT_REF16"); break;
	  case EXT_REF8:      Printf("  EXT_REF8"); break;
	  case EXT_DEXT32:    Printf("  EXT_DEXT32"); break;
	  case EXT_DEXT16:    Printf("  EXT_DEXT16"); break;
	  case EXT_DEXT8:     Printf("  EXT_DEXT8"); break;
	  case EXT_RELREF32:  Printf("  EXT_RELREF32"); break;
	  case EXT_RELREF26:  Printf("  EXT_RELREF26"); break;
	  case EXT_RELCOMMON: Printf("  EXT_RELCOMMON"); break;
	  default: Printf("  EXT_??? ($%02x)\n",(Data >> 24) & 0xFF);
	    break;
          }
        }

	/* Is it followed by a symbol name? */

	if(Data & 0xFFFFFF)
	{
	  ReadName(Data & 0xFFFFFF);
	  MakeName(Data & 0xFFFFFF);
	  if(showextern)
            Printf("\t%s", NameString);
        }

	/* Remember extension type. */
        i = (Data >> 24) & 0xFF;

	/* Display value of symbol. */
        if(i == EXT_DEF || i == EXT_ABS || i == EXT_RES)
        {
	  if(!(Data & 0xFFFFFF) && showextern)
	    Printf("\t???");

	  ReadLong(&Data);
	  if(showextern)
	    Printf("%s= $%08lx", GetTabs(NameString), Data);
	}

	/* Skip relocation information. */
        if(i == EXT_REF32 || i == EXT_REF16 || i == EXT_REF8 ||
        i == EXT_DEXT32 || i == EXT_DEXT16 || i == EXT_DEXT8 ||
        i == EXT_RELREF32 || i == EXT_RELREF26)
	{
	  ReadLong(&Data);
	  if(showextern)
	    Printf("%s= %ld entr%s", GetTabs(NameString), Data, Data == 1 ? "y" : "ies");
	  SkipLong(Data);
	}

	/* Display size of common block. */
        if(i == EXT_COMMON || i == EXT_RELCOMMON)
	{
	  ReadLong(&Data);

	  if(showextern)
	    Printf("\tSize = %ld Bytes", Data << 2);
	  ReadLong(&Data);
	  SkipLong(Data);
	}
	if(showextern)
          Printf("\n");
      } while(!CTRL_C);
      break;
    case HUNK_SYMBOL:
      Printf("HUNK_SYMBOL\n");
      ++stats->Symbol;

      do
      {
	ReadLong(&Data);

	if(!Data)
	  break;

	++stats->NumSymbol;
	/* Display name. */
        ReadName(Data & 0xFFFFFF);
        MakeName(Data & 0xFFFFFF);

	if(showsymbol)
          Printf("  Symbol\t%s", NameString);

	/* Display value. */
        ReadLong(&Data);

	if(showsymbol)
	  Printf("%s= $%08lx\n", GetTabs(NameString), Data);
      } while(!CTRL_C);
      break;
    case HUNK_DEBUG:
      ++stats->Debug;
      ReadLong(&Data);
      SkipLong(Data);
      Data <<= 2;
      stats->DebugSize += Data;
      Printf("HUNK_DEBUG\t%6ld ($%06lx) Bytes\n", Data, Data);
      break;
    case HUNK_END:
      Printf("HUNK_END\n\n");
      break;
    case HUNK_HEADER:
      Printf("HUNK_HEADER\n");

      ++stats->Header;

      do
      {
	ReadLong(&Data);

	/* Display names of resident libraries. */
        if(!Data)
          break;

	ReadName(Data);
	MakeName(Data);

	Printf("  Name =\t%s\n", NameString);
      } while(!CTRL_C);

      if(!CTRL_C)
      {
        LONG i,From,To;

        ReadLong(&Data);

	Printf("  Numhunks =\t%6ld (", Data);
        ReadLong(&From);
        Printf("%ld to ", From);
        ReadLong(&To);
        Printf("%ld)\n",To);

        /* Display hunk lengths/types. */
        for(i = 0; !CTRL_C && i < To - From + 1; ++i)
        {
	  ReadLong(&Data);
          Printf("  Hunk %03ld =\t%6ld ($%06lx) Bytes", i,
          (Data & 0x3FFFFFFF) << 2, (Data & 0x3FFFFFFF) << 2);

	  if((Data & HUNKF_CHIP) && (Data & HUNKF_FAST))
	  {	/* extended type */
	    ULONG t;
	    ReadLong(&t);
	    Printf(" MEMTYPE($%08lx)", t);
	  }
	  else
	  {
            if(Data & HUNKF_CHIP)	Printf(" CHIP");
            if(Data & HUNKF_FAST)	Printf(" FAST");
          }
          if(Data & HUNKF_ADVISORY)	Printf(" ADVISORY");

	  Printf("\n");
        }
        Printf("\n");
      }
      break;
    case HUNK_OVERLAY:
      {
        LONG TabSize, Data, Data2 = 0;
        ULONG *ovt = 0;
        Printf("HUNK_OVERLAY\n");
        ReadLong(&TabSize);

	Printf("  TableSize\t%6ld ($%06lx) Bytes\n", TabSize*4, TabSize*4);

	if(TabSize)
	{
	  ReadLong(&Data2);
	  if(showoverlaytable && (ovt = (ULONG *) AllocVec(TabSize*4, MEMF_ANY)))
	    Read(FileHandle, ovt, TabSize*4);
	  else
	    SkipLong(TabSize);
	}
	ReadLong(&Data);
	if(!TabSize && Data == HUNK_BREAK)
	{
	  Data = Seek(FileHandle, 0, OFFSET_END);
	  Data = Seek(FileHandle, 0, OFFSET_END) - Data;
	  Printf("HUNK_BREAK\n\nLeft Space\t%6ld ($%06lx) Bytes\n", Data, Data);
	}
	else if(TabSize && Data >= HUNK_UNIT && Data <= HUNK_ABSRELOC16)
	{
	  Seek(FileHandle, -4, OFFSET_CURRENT);
	  Printf("  Levels\t%6ld\n", Data2-2);
	  Printf("  Entries\t%6ld\n", (TabSize-Data2+1) >> 3);
	}
	else
	{
	  Data = Seek(FileHandle, 0, OFFSET_END);
	  Data = Seek(FileHandle, 0, OFFSET_END) - Data;
	  Printf("  DataSize\t%6ld ($%06lx) Bytes\n", Data+8, Data+8);
	  if(ovt)
	  {
	    for(Data = TabSize-1; Data; --Data)
	      ovt[Data] = ovt[Data-1];
	    ovt[0] = Data2;
	  }
	}
	if(ovt)
	{
	  Data2 = 0;
	  while(Data2 < TabSize)
	  {
	    Printf("  Data $%04lx\t", Data2*4);
	    for(Data = 0; Data < 4; ++Data)
	    {
	      if(Data2 < TabSize)
	      {
	        Printf("%08lx%s", ovt[Data2], Data2 < TabSize-1 ? " " : "");
	        ++Data2;
	      }
	    }
	    Printf("\n");
	  }
	  FreeVec(ovt);
	}
	Printf("\n");
      }
      break;
    case HUNK_BREAK:
      Printf("HUNK_BREAK\n\n");
      break;
    default:
      Printf("HUNK_??? ($%08lx) - Aborting!\n\n", Type);
      return 1;
    }
  }

  return 0;
}
