#define NAME         "xfdDecrunchAddr"
#define DISTRIBUTION "(Freeware) "
#define REVISION     "2"

/* Programmheader

	Name:		xfdDecrunchAddr
	Author:		SDI
	Distribution:	Freeware
	Description:	deep decrunch address crunched executables
	Compileropts:	-
	Linkeropts:	-gsi -l amiga

 1.0   01.06.98 : first version
 1.1   04.06.98 : added ADDHEAD keyword
 1.2   09.08.98 : bug fix in help text
*/

#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/xfdmaster.h>
#include <dos/doshunks.h>
#include <exec/memory.h>
#include "SDI_defines.h"

#define SDI_TO_ANSI
#include "SDI_ASM_STD_protos.h"

#define PARAM "SOURCE/A,DEST=TO,HEADER/N,ADDHEAD/S,DECRADDR,JMPADDR"

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

struct Args {
  STRPTR source;
  STRPTR dest;
  LONG * header;
  ULONG  addhead;
  STRPTR decraddr;
  STRPTR jmpaddr;
};

STRPTR exthelp =
  "SOURCE\t the address crunched file\n"
  "DEST=TO\t the destination file (when not given source is overwritten)\n"
  "HEADER\t 0 = no header, saved as data file\n"
  "\t 1 = executable header (hunk information only)\n"
  "\t 2 = executable header with one BSS hunk of size 4\n"
  "\t 3 = simple header\n"
  "\t 4 = professional header\n"
  "ADDHEAD\t the source is not decrunch, but gets added a header only\n"
  "DECRADDR the address where file should be stored in memory (for ADDHEAD)\n"
  "JMPADDR\t the JumpIn address of file (for ADDHEAD)\n"
  "Both DECRADDR and JMPADDR must be hexadecimal numbers.\n";

STRPTR MakeHeader(APTR, ULONG, ULONG *, ULONG, ULONG, ULONG);
ULONG DecrunchAddr(APTR, ULONG, STRPTR, ULONG);

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)))
  {
    LONG head = 0;
    struct Args args;
    struct RDArgs *rda;

    args.dest = args.jmpaddr = args.decraddr = 0;
    args.header = &head;
    args.addhead = 0;

    DOSBase = dosbase;
    if((rda = (struct RDArgs *) AllocDosObject(DOS_RDARGS, 0)))
    {
      rda->RDA_ExtHelp = exthelp;
      if(ReadArgs(PARAM, (LONG *) &args, rda))
      {
        ULONG jmpaddr;
        ULONG decraddr = 0;
        STRPTR err = 0;
        ULONG fh;

	if(*args.header < 0 || *args.header > 4)
	  args.header = &head;
	if(!args.dest)
	  args.dest = args.source;
	if(args.decraddr)
	  decraddr = strtol(args.decraddr, &err, 16);
	if(!err && args.jmpaddr)
	  jmpaddr = strtol(args.jmpaddr, &err, 16);
	else
	  jmpaddr = decraddr;

	if(err)
	  SetIoErr(ERROR_BAD_NUMBER);
	else if(args.addhead && !*args.header)
	  Printf("You need to specify a header type.\n");
	else if(args.addhead && !args.decraddr && *args.header > 2)
	  Printf("You need to specify at least the decrunch address.\n");
        else if((fh = Open(args.source, MODE_OLDFILE)))
        {
          struct FileInfoBlock *fib;

          if((fib = (struct FileInfoBlock *) AllocDosObject(DOS_FIB, 0)))
          {
            if(ExamineFH(fh, fib))
            {
              STRPTR inmem;
                
              if((inmem = (STRPTR) AllocMem(fib->fib_Size, MEMF_ANY)))
              {
		if(Read(fh, inmem, fib->fib_Size) == fib->fib_Size)
		{
		  Close(fh); fh = 0;

		  if(args.addhead)
		  {
		    STRPTR res;
		    ULONG ressize;

		    Printf("Loaded - Size %ld, DecrAddr %08lx, JmpAddr %08lx\n",
		    fib->fib_Size, decraddr, jmpaddr);

            	    if((res = MakeHeader(inmem, fib->fib_Size, &ressize,
            	    *args.header, jmpaddr, decraddr)))
	    	    {

	  	      if((fh = Open(args.dest, MODE_NEWFILE)))
	  	      {
		        if(Write(fh, res, ressize) == ressize)
		        {
	      		  Printf("File succesfully saved.\n");
	      		  ret = 0;
	      		}
	    		else
	    		  Printf("Saving file failed.\n");
	    	      }
		      else
	  		Printf("Could not open destination file.\n");
	              FreeMem(res, ressize);
	            }
		  }
		  else
		  {
	       	    struct xfdMasterBase *xfdmasterbase;

	            if((xfdmasterbase = (struct xfdMasterBase *)
	  	    OpenLibrary("xfdmaster.library", 37)))
		    {
	              xfdMasterBase = xfdmasterbase;
                      if(DecrunchAddr(inmem, fib->fib_Size, args.dest,
                      *args.header))
                        ret = 0;
	              CloseLibrary((struct Library *) xfdmasterbase);
          	    }
          	  }
                }
                FreeMem(inmem, fib->fib_Size);
              }
            }
            FreeDosObject(DOS_FIB, fib);
          }
          if(fh)
            Close(fh);
	}
      
        FreeArgs(rda);
      }
      FreeDosObject(DOS_RDARGS, rda);
    }
    if(ret)
      PrintFault(IoErr(), 0);
    CloseLibrary((struct Library *) dosbase);
  }
  return ret;
}

#define DCAD 0x00	/* decrunch address */
#define JMAD 0x00	/* Jump address */
#define BSIZ 0x00	/* BCPL size */
#define DSIZ 0x00	/* decimal size */
#define HNKS 0x00	/* BCPL hunksize */

#define HEADER1BSIZ1	 20
#define HEADER1BSIZ2	 28
#define HEADER2BSIZ1	 20
#define HEADER2BSIZ2	 32
#define HEADER3HNKS1	 20	/* BSIZ + 0xE */
#define HEADER3HNKS2	 28	/* BSIZ + 0xE */
#define HEADER3BSIZ	 48
#define HEADER3DCAD	 38
#define HEADER3JMAD	 82
#define HEADER4DSIZ	 94
#define HEADER4DCAD	 88
#define HEADER4JMAD	168

UBYTE header1[] = {
0x00, 0x00, 0x03, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, BSIZ, BSIZ, BSIZ, BSIZ,
0x00, 0x00, 0x03, 0xE9, BSIZ, BSIZ, BSIZ, BSIZ,
};

UBYTE header2[] = {
0x00, 0x00, 0x03, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, BSIZ, BSIZ, BSIZ, BSIZ,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xE9, BSIZ, BSIZ, BSIZ, BSIZ,
};

UBYTE header3[] = {
0x00, 0x00, 0x03, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, HNKS, HNKS, HNKS, HNKS, 
0x00, 0x00, 0x03, 0xE9, HNKS, HNKS, HNKS, HNKS, 0x48, 0xE7, 0x80, 0xC0,
0x43, 0xF9, DCAD, DCAD, DCAD, DCAD, 0x41, 0xFA, 0x00, 0x2C, 0x20, 0x3C, 
BSIZ, BSIZ, BSIZ, BSIZ, 0xB1, 0xC9, 0x67, 0x14, 0x65, 0x08, 0x22, 0xD8,
0x53, 0x80, 0x66, 0xFA, 0x60, 0x0A, 0xD1, 0xC0, 0xD3, 0xC0, 0x23, 0x20, 
0x53, 0x80, 0x66, 0xFA, 0x4C, 0xDF, 0x03, 0x01, 0x4E, 0xF9, JMAD, JMAD,
JMAD, JMAD, 0x00, 0x00, 
};

UBYTE header4[] = {
0x00, 0x00, 0x03, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE9, 0x00, 0x00, 0x00, 0x3F,
0x60, 0x16, 0x00, 0x00, 0x00, 0x00, 0xAB, 0xCD, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x48, 0xE7, 0xF0, 0xC2, 0x43, 0xFA, 0x00, 0xA6, 0x70, 0x21, 0x2C, 0x78,
0x00, 0x04, 0x4E, 0xAE, 0xFD, 0xD8, 0x41, 0xFA, 0xFF, 0xD4, 0x20, 0x80,
0x67, 0x76, 0x43, 0xF9, DCAD, DCAD, DCAD, DCAD, 0x20, 0x3C, DSIZ, DSIZ, 
DSIZ, DSIZ, 0x4E, 0xAE, 0xFF, 0x34, 0x4A, 0x80, 0x66, 0x08, 0x41, 0xFA,
0x00, 0x88, 0x61, 0x64, 0x60, 0x4E, 0x22, 0x3A, 0xFF, 0xB8, 0x24, 0x3C, 
0x00, 0x00, 0x01, 0x30, 0x76, 0xFF, 0x2C, 0x7A, 0xFF, 0xA4, 0x4E, 0xAE,
0xFF, 0xBE, 0x22, 0x3A, 0xFF, 0xA4, 0x24, 0x3A, 0xFF, 0xCC, 0x26, 0x3A, 
0xFF, 0xCE, 0x4E, 0xAE, 0xFF, 0xD6, 0xB6, 0x80, 0x67, 0x08, 0x41, 0xFA,
0x00, 0x6E, 0x61, 0x34, 0x60, 0x0E, 0x4C, 0xDF, 0x43, 0x0F, 0x4E, 0xB9,
JMAD, JMAD, JMAD, JMAD, 0x9E, 0xFC, 0x00, 0x1C, 0x22, 0x7A, 0xFF, 0xA6,
0x20, 0x3A, 0xFF, 0xA8, 0x2C, 0x78, 0x00, 0x04, 0x4E, 0xAE, 0xFF, 0x2E,
0x22, 0x7A, 0xFF, 0x62, 0x2C, 0x78, 0x00, 0x04, 0x4E, 0xAE, 0xFE, 0x62,
0xDE, 0xFC, 0x00, 0x1C, 0x70, 0x00, 0x4E, 0x75, 0x76, 0x00, 0x16, 0x18,
0x24, 0x08, 0x2C, 0x7A, 0xFF, 0x48, 0x4E, 0xAE, 0xFF, 0xC4, 0x22, 0x00,
0x4E, 0xEE, 0xFF, 0xD0, 0x64, 0x6F, 0x73, 0x2E, 0x6C, 0x69, 0x62, 0x72,
0x61, 0x72, 0x79, 0x00, 0x15, 0x4E, 0x6F, 0x20, 0x6D, 0x65, 0x6D, 0x6F,
0x72, 0x79, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6C, 0x61, 0x62, 0x6C, 0x65,
0x2E, 0x0A, 0x14, 0x45, 0x72, 0x72, 0x6F, 0x72, 0x20, 0x6C, 0x6F, 0x61,
0x64, 0x69, 0x6E, 0x67, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2E, 0x0A, 0x00,
0x00, 0x00, 0x03, 0xF2, 0x00, 0x00, 0x03, 0xF5, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0xF6, 
};

/* Do not free destination memory, when ressize == 0 */
STRPTR MakeHeader(APTR mem, ULONG size, ULONG *ressize, ULONG type,
ULONG jmp, ULONG decr)
{
  ULONG newsize = 0, longsize;
  STRPTR newmem = 0;

  longsize = (size+3)&(~3);

  switch(type)
  {
  case 0: newmem = mem; break;
  case 1:
    newsize = longsize+sizeof(header1)+4;
    if((newmem = (STRPTR) AllocMem(newsize, MEMF_ANY|MEMF_CLEAR)))
    {
      CopyMem(header1, newmem, sizeof(header1));
      CopyMem(mem, newmem+sizeof(header1), size);
      *((ULONG *)(newmem+sizeof(header1)+longsize)) = HUNK_END;
      *((ULONG *)(newmem+HEADER1BSIZ1)) = longsize>>2;
      *((ULONG *)(newmem+HEADER1BSIZ2)) = longsize>>2;
    }
    break;
  case 2:
    newsize = longsize+sizeof(header2)+16;
    if((newmem = (STRPTR) AllocMem(newsize, MEMF_ANY|MEMF_CLEAR)))
    {
      CopyMem(header2, newmem, sizeof(header2));
      CopyMem(mem, newmem+sizeof(header2), size);
      *((ULONG *)(newmem+sizeof(header2)+longsize))    = HUNK_END;
      *((ULONG *)(newmem+sizeof(header2)+longsize+4))  = HUNK_BSS;
      *((ULONG *)(newmem+sizeof(header2)+longsize+8))  = 0;
      *((ULONG *)(newmem+sizeof(header2)+longsize+12)) = HUNK_END;
      *((ULONG *)(newmem+HEADER2BSIZ1)) = longsize>>2;
      *((ULONG *)(newmem+HEADER2BSIZ2)) = longsize>>2;
    }
    break;
  case 3:
    newsize = longsize+sizeof(header3)+4;
    if((newmem = (STRPTR) AllocMem(newsize, MEMF_ANY|MEMF_CLEAR)))
    {
      CopyMem(header3, newmem, sizeof(header3));
      CopyMem(mem, newmem+sizeof(header3), size);
      *((ULONG *)(newmem+sizeof(header3)+longsize)) = HUNK_END;
      *((ULONG *)(newmem+HEADER3HNKS1)) = (longsize>>2) + 0x0E;
      *((ULONG *)(newmem+HEADER3HNKS2)) = (longsize>>2) + 0x0E;
      *((ULONG *)(newmem+HEADER3BSIZ))  = longsize>>2;
      *((ULONG *)(newmem+HEADER3DCAD))  = decr;
      *((ULONG *)(newmem+HEADER3JMAD))  = jmp;
    }
    break;
  case 4:
    newsize = longsize+sizeof(header4);
    if((newmem = (STRPTR) AllocMem(newsize, MEMF_ANY|MEMF_CLEAR)))
    {
      CopyMem(header4, newmem, sizeof(header4));
      CopyMem(mem, newmem+sizeof(header4), size);
      *((ULONG *)(newmem+HEADER4DSIZ))  = size;
      *((ULONG *)(newmem+HEADER4DCAD))  = decr;
      *((ULONG *)(newmem+HEADER4JMAD))  = jmp;
    }
    break;
  }

  if(newmem)
    *ressize = newsize;
  return newmem;
}

ULONG DecrunchAddr(APTR mem, ULONG size, STRPTR dest, ULONG type)
{
  ULONG ret = 0;
  struct xfdBufferInfo *xbi;

  if((xbi = (struct xfdBufferInfo *) xfdAllocObject(XFDOBJ_BUFFERINFO)))
  {
    xbi->xfdbi_SourceBuffer = mem;
    xbi->xfdbi_SourceBufLen = size;
    xbi->xfdbi_Flags = XFDFF_RECOGEXTERN;
    if(xfdRecogBuffer(xbi) && (xbi->xfdbi_PackerFlags & XFDPFF_ADDR))
    {
      Printf("Packertype: %s\n", xbi->xfdbi_PackerName);
      if(xfdDecrunchBuffer(xbi))
      {
        ULONG ressize = 0;
	STRPTR res;

        Printf("Decrunched - Size %ld, DecrAddr %08lx, JmpAddr %08lx\n",
        xbi->xfdbi_TargetBufSaveLen, xbi->xfdbi_DecrAddress,
        xbi->xfdbi_JmpAddress);

	if((res = MakeHeader(xbi->xfdbi_TargetBuffer,
	xbi->xfdbi_TargetBufSaveLen, &ressize, type,
	xbi->xfdbi_DecrAddress, xbi->xfdbi_JmpAddress)))
	{
	  ULONG fh;

	  if((fh = Open(dest, MODE_NEWFILE)))
	  {
	    ULONG s = ressize;

	    if(!s)
	      s = xbi->xfdbi_TargetBufSaveLen;

	    if(Write(fh, res, s) == s)
	    {
	      Printf("File succesfully saved.\n"); ret = 1;
	    }
	    else
	      Printf("Saving file failed.\n");
	    Close(fh);
	  }
	  else
	    Printf("Could not open destination file.\n");

	  if(ressize)
	    FreeMem(res, ressize);
	}

        if(!xfdTestHunkStructureNew(xbi->xfdbi_TargetBuffer,
        xbi->xfdbi_TargetBufSaveLen))
          Printf("File seems to be an valid executable! "
          "HEADER=0 should be used.\n");
	else
	{
  	  struct xfdBufferInfo *xbit;

  	  if((xbit = (struct xfdBufferInfo *) xfdAllocObject(XFDOBJ_BUFFERINFO)))
          {
            xbit->xfdbi_Flags = XFDFF_RECOGEXTERN;

            xbit->xfdbi_SourceBuffer = xbi->xfdbi_TargetBuffer;
            xbit->xfdbi_SourceBufLen = xbi->xfdbi_TargetBufSaveLen;
	    if(xfdRecogBuffer(xbit))
              Printf("File crunched again with '%s'? "
              "HEADER=0 should be used.\n", xbit->xfdbi_PackerName);
            if((res = MakeHeader(xbi->xfdbi_TargetBuffer,
	    xbi->xfdbi_TargetBufSaveLen, &ressize, 1, 0, 0)))
	    {
              xbit->xfdbi_SourceBuffer = res;
              xbit->xfdbi_SourceBufLen = ressize;

	      if(xfdRecogBuffer(xbit))
                Printf("File crunched again with '%s'? "
                "HEADER=1 should be used.\n", xbit->xfdbi_PackerName);

             FreeMem(res, ressize);
            }
            if((res = MakeHeader(xbi->xfdbi_TargetBuffer,
	    xbi->xfdbi_TargetBufSaveLen, &ressize, 2, 0, 0)))
	    {
              xbit->xfdbi_SourceBuffer = res;
              xbit->xfdbi_SourceBufLen = ressize;

	      if(xfdRecogBuffer(xbit))
                Printf("File crunched again with '%s'? "
                "HEADER=2 should be used.\n", xbit->xfdbi_PackerName);

              FreeMem(res, ressize);
            }
            xfdFreeObject(xbit);
	  }
	}
        FreeMem(xbi->xfdbi_TargetBuffer, xbi->xfdbi_TargetBufLen);
      }
    }

    xfdFreeObject(xbi);
  }
  return ret;
}

