/*************************************************************************
 * Project:     data2obj.c
 * Description: Converts raw data into Amiga object format files
 * Author:      John Chasey
 * Version:     1.00
 * Commenced:   July 1990
 * Contents:    Everything - its designed to be small!
 * Comment:     Fairly basic at the mo - could be improved, with archive
 *              like switchs, adding/removing etc
 * Compiler:    DICE v2.02
 *************************************************************************/

#include <exec/types.h>
#include <libraries/dosextens.h>

#define PadToWord(a) (a = ((a & 0x01) ? (a + 1) : (a)))
#define PadToLong(a) (a = ((PadToWord(a) & 0x02) ? (a + 2) : (a)))

#define HUNK_NAME_LEN   256

#define USAGE_NONE      0x0000
#define USAGE_CHIP      0x0010
#define USAGE_ADD       0x0020
#define USAGE_REMOVE    0x0040
#define USAGE_LIST      0x0080
#define USAGE_ERRORS    0xf000
#define USAGE_ARGERROR  0x1000
#define USAGE_SWIERROR  0x2000
#define USAGE_SRCERROR  0x4000
#define USAGE_DSTERROR  0x8000

#define HUNK_MEM_PUBLIC 0x00000000
#define HUNK_MEM_FAST   0x80000000
#define HUNK_MEM_CHIP   0x40000000
#define HUNK_UNIT       0x000003e7
#define HUNK_NAME       0x000003e8
#define HUNK_CODE       0x000003e9
#define HUNK_DATA       0x000003ea
#define HUNK_BSS        0x000003eb
#define HUNK_RELOC32    0x000003ec
#define HUNK_RELOC16    0x000003ed
#define HUNK_RELOC8     0x000003ee
#define HUNK_EXT        0x000003ef
#define HUNK_SYMBOL     0x000003f0
#define HUNK_DEBUG      0x000003f1
#define HUNK_END        0x000003f2
#define HUNK_HEADER     0x000003f3
#define HUNK_OVERLAY    0x000003f5
#define HUNK_BREAK      0x000003f6
#define HUNK_DRELOC32   0x000003f7      /* Lattice Data Reloc */
#define HUNK_DRELOC16   0x000003f8      /* Lattice Data Reloc */
#define HUNK_DRELOC8    0x000003f9      /* Lattice Data Reloc */
#define HUNK_LIB_HUNK   0x000003fa      /* Lattice Library Hunk  */
#define HUNK_LIB_INDEX  0x000003fb      /* Lattice Library Index */
#define EXT_SYMB        0x00000000
#define EXT_DEF         0x01000000
#define EXT_ABS         0x02000000
#define EXT_RES         0x03000000
#define EXT_REF32       0x81000000
#define EXT_COMMON      0x82000000
#define EXT_REF16       0x83000000
#define EXT_REF8        0x84000000
#define HUNK_EXT_MASK   0xff000000
#define HUNK_MEM_MASK   0xc0000000

extern LONG Open();
extern VOID *AllocMem();

static VOID CloseDown(LONG,LONG,BYTE *,LONG);
static WORD ParseArgs(LONG,BYTE **,LONG *,LONG *,BYTE *,BYTE *);
static VOID DisplayUsage();
static BYTE *ReadData(LONG, LONG *);
static VOID WriteObject(LONG,WORD,BYTE *,BYTE *,BYTE *,LONG);
static VOID ListObject(LONG);
static VOID SkipHeaderHunk(LONG);
static VOID SkipOverlayHunk(LONG);
static VOID SkipRelocHunk(LONG);
static BOOL SkipSymbolHunk(LONG);
static VOID ReadName(LONG, BYTE *);
static LONG Skip(LONG);
static VOID WriteLongWord(LONG,LONG);

main(argc,argv)
LONG argc;
BYTE *argv[];
{
   BYTE DataName[HUNK_NAME_LEN];
   BYTE OFileName[HUNK_NAME_LEN];
   BYTE *DataAddr = NULL;
   LONG DataSize = 0;
   LONG Srcfh = NULL;
   LONG Dstfh = NULL;
   WORD i, action;
  
   for (i = 0; i < (HUNK_NAME_LEN - 1);  DataName[i++] = 0);
   for (i = 0; i < (HUNK_NAME_LEN - 1); OFileName[i++] = 0);

   printf ("\n\033[33mData2Obj v1.00\033[31m");
   printf (" © John Chasey, Zugger Software, July 1990\n\n");

   action = ParseArgs(argc,argv,&Srcfh,&Dstfh,OFileName,DataName);
   if ((action & USAGE_ERRORS) != 0) {
      DisplayUsage();
   } else {
      if (action & USAGE_LIST) {
         ListObject(Srcfh);
      } else {
         if (DataAddr = ReadData(Srcfh,&DataSize)) {
            WriteObject(Dstfh,action,OFileName,DataName,DataAddr,DataSize);
            printf ("Object file '%s' ok!\n",DataName);
         }
      }
   }

   printf ("\n");
   CloseDown(Srcfh,Dstfh,DataAddr,DataSize);
   exit(0);
}

/*************************************************************************
 * Function:    CloseDown
 * Parameters:
 *    fh1     - Pointer to the file handle to close
 *    fh2     - Pointer to the file handle to close
 *    dataptr - Pointer to data to free
 *    datalen - Pointer to data length
 * Returns:     Void
 * Action:      Deallocate resources used
 *************************************************************************/

static VOID CloseDown(fh1,fh2,dataptr,datalen)
LONG fh1,fh2;
BYTE *dataptr;
LONG datalen;
{
   if (fh1 != NULL) Close(fh1);
   if (fh2 != NULL) Close(fh2);
   if (dataptr != NULL) FreeMem(dataptr,datalen);
}

/*************************************************************************
 * Function:    ParseArgs
 * Parameters:
 *    argcount - Number of arguments passed to data2obj
 *    argarray - Pointer to array of argument strings
 *    sfh      - Address of source file handle pointer
 *    dfh      - Address of destination file handle pointer
 * Returns:     Bitmasked WORD (16 bits) of what action to take
 * Action:      Parse the arguments passed to data2obj
 *************************************************************************/

static WORD ParseArgs(argcount,argarray,sfh,dfh,objname,datanam)
LONG argcount;
BYTE **argarray;
LONG *sfh, *dfh;
BYTE *objname, *datanam;
{
   WORD arg = 1;
   WORD usage = USAGE_NONE;
   WORD filemode = MODE_NEWFILE;
   WORD xtraargs = 3;

   if (argcount < 3) {
      usage |= USAGE_ARGERROR;
   } else {
      /*** Parse switches ***/
      while (argarray[arg][0] == '-') {
         if (argarray[arg][2] != '\0') {
            printf ("Error! Switch %s not recognised\n",argarray[arg]);
            usage |= USAGE_SWIERROR;
         } else {
            switch(tolower(argarray[arg][1])) {
               case('c'): printf ("Data to go into Chip Memory.\n");
                          usage |= USAGE_CHIP;
                          xtraargs = 3;
                          break;
               case('a'): printf ("Adding into existing Object File.\n");
                          usage |= USAGE_ADD;
                          xtraargs = 3;
                          break;
               case('r'): /*** Remove ***/ 
                          usage |= USAGE_REMOVE;
                          xtraargs = 2;
                          break;
               case('l'): printf ("Listing Amiga Binary file contents:\n");
                          usage |= USAGE_LIST;
                          xtraargs = 1;
                          break;
               default:   printf ("Error! Switch %s not recognised\n",argarray[arg]);
                          usage |= USAGE_SWIERROR;
                          xtraargs = 99;
                          break;
            }
         }
         arg++;
      }
      if ((argcount - arg) < xtraargs) {
         usage |= USAGE_ARGERROR;
      } else {
         if (usage & USAGE_LIST) {
            if ((*sfh = Open(argarray[arg],MODE_OLDFILE)) == NULL) {
               usage |= USAGE_SRCERROR;
               printf ("Error! - Could not open Object File %s\n",argarray[arg]);
            }
         } else {
            strcpy(datanam,argarray[arg]);
            printf ("Data name is '%s'\n",argarray[arg]);
            arg++;
            if ((*sfh = Open(argarray[arg],MODE_OLDFILE)) == NULL) {
               usage |= USAGE_SRCERROR;
               printf ("Error! - Could not open Raw Data File %s\n",argarray[arg]);
            }
            arg++;
            if (usage & USAGE_ADD) filemode = MODE_OLDFILE;
            if ((*dfh = Open(argarray[arg],filemode)) == NULL) {
               usage |= USAGE_DSTERROR;
               printf ("Error! - Could not open Object File %s\n",argarray[arg]);
            }
            strcpy(objname,argarray[arg]);
            arg++;
         }
      }               
   }
   return(usage);
}

/*************************************************************************
 * Function:    DisplayUsage()
 * Parameters:  None
 * Returns:     Void
 * Action:      Display to stdout details of data2obj usage
 *************************************************************************/

static VOID DisplayUsage()
{
   printf ("Usage:    Data2Obj [Switches] <Name> <RawFile> <ObjFile>\n");
   printf ("\nSwitches:          -c Force Data into Chip RAM");
   printf ("\n                   -a Add Data into existing Object File");
   printf ("\n                   -l List Contents of Amiga Binary File");
   printf ("\n");
}      

/*************************************************************************
 * Function:    ReadData
 * Parameters:
 *    fh      - Pointer to the file handle of source file
 *    datalen - Pointer to LONG to contain the data length
 * Returns:     Pointer to the data if read okay, else NULL
 * Action:      Read the raw data from a source file into memory,
 *              allocating memory as required.
 *************************************************************************/

static BYTE *ReadData(fh,datalen)
LONG fh;
LONG *datalen;
{
   BYTE *dataptr;
   LONG length;

   Seek(fh,0,OFFSET_END);
   length = Seek(fh,0,OFFSET_BEGINNING);
   *datalen = length;

   PadToLong(length);

   if ((dataptr = AllocMem(length,0)) == NULL) {
      return(NULL);
   }
   
   /*** Make sure padded data is 0 ***/
   dataptr[length - 1] = 0;
   dataptr[length - 2] = 0;
   dataptr[length - 3] = 0;

   if ((Read(fh,dataptr,length) + 3) < length) {
      FreeMem(dataptr,length);
      return(NULL);
   }

   return(dataptr);
}

/*************************************************************************
 * Function:    WriteObject
 * Parameters:
 *    fh      - Pointer to the file handle of destination file
 *    flags   - Bitmask to indicate exact operation
 *    objname - Pointer to array containing Object File name
 *    datanam - Pointer to array containing Data Symbol name
 *    dataptr - Pointer to Raw data to write into object file
 *    datalen - Size of data in bytes
 * Returns:     void
 * Action:      Writes raw data out in Amiga Binary object file format
 * Comment:     Writes the object file filename as the Program Unit Header
 *              Block name.
 *              Does not check the return codes from Write();
 *************************************************************************/

static VOID WriteObject(fh,flags,objname,datanam,dataptr,datalen)
LONG fh;
WORD flags;
BYTE *objname;
BYTE *datanam;
BYTE *dataptr;
LONG datalen;
{
   LONG objnamelen, datanamlen;

   if (flags & USAGE_ADD) {
      Seek(fh,0,OFFSET_END);
   } else {
      /*** Program Unit Header Block ***/
      WriteLongWord(fh,HUNK_UNIT);
      objnamelen = strlen(objname);
      PadToLong(objnamelen);
      WriteLongWord(fh,objnamelen >> 2);
      Write(fh,objname,objnamelen);
   }

   /*** Hunk Data ***/
   if (flags & USAGE_CHIP)
      WriteLongWord(fh,HUNK_MEM_CHIP | HUNK_DATA);
   else
      WriteLongWord(fh,HUNK_MEM_PUBLIC | HUNK_DATA);
   WriteLongWord(fh,datalen >> 2);
   Write(fh,dataptr,datalen);

   /*** Hunk External Symbol ***/   
   WriteLongWord(fh,HUNK_EXT);
   datanamlen = strlen(datanam);
   PadToLong(datanamlen);
   WriteLongWord(fh,EXT_DEF|(datanamlen >> 2)); /*** ext_def Type Byte ***/
   Write(fh,datanam,datanamlen);
   WriteLongWord(fh,0);                          /*** Offset into hunk ***/
   WriteLongWord(fh,0);
 
   /*** End Hunk ***/
   WriteLongWord(fh,HUNK_END);
}

/*************************************************************************
 * Function:    ListObject
 * Parameters:
 *    fh      - Pointer to the file handle of source file
 * Returns:     void
 * Action:      Displays (or will do) details on all the Amiga Binary code
 *              hunks met in the file
 * Comment:     Does not handle load files, or Lattice special hunks at
 *              the mo.
 *************************************************************************/

static VOID ListObject(fh)
LONG fh;
{
   BYTE hunkname[HUNK_NAME_LEN];
   LONG hunk, length;
   WORD oldhunkno = 0,hunkno = 0;

   Read(fh,&hunk,4);
   if (hunk != HUNK_UNIT && hunk != HUNK_HEADER) {
      printf ("\nNot a standard Amiga Binary file!");
   } else {
      printf ("\n\033[33mHunk 00\033[31m %08x Hunk_",hunk);
      if (hunk == HUNK_UNIT) {
         ReadName(fh,hunkname);
         printf ("Unit        '%s'",hunkname);
      } else {
         printf ("Header      ");
         SkipHeaderHunk(fh);
      }
      while (Read(fh,&hunk,4) == 4) {
         if (oldhunkno != hunkno) {
            printf ("\n\033[33mHunk %02d\033[31m ",hunkno);
            oldhunkno = hunkno;
         } else {
            printf ("\n        ");
         }
         printf ("%08X Hunk_",hunk);
         switch(~HUNK_MEM_MASK & hunk) {
            case(HUNK_NAME):     printf ("Name        ");
                                 ReadName(fh,hunkname);
                                 printf ("'%s'",hunkname);
                                 break;
            case(HUNK_CODE):     printf ("Code        ");
                                 printf ("%d Bytes",Skip(fh));
                                 break;
            case(HUNK_DATA):     printf ("Data        ");
                                 printf ("%d Bytes",Skip(fh));
                                 break;
            case(HUNK_BSS):      printf ("BSS         ");
                                 Read(fh,&length,4);
                                 printf ("%d Bytes",length << 2);
                                 break;
            case(HUNK_RELOC32):  printf ("Reloc32     "); break;
            case(HUNK_RELOC16):  printf ("Reloc16     "); break;
            case(HUNK_RELOC8):   printf ("Reloc8      "); break;
            case(HUNK_DRELOC32): printf ("DataReloc32 "); break;
            case(HUNK_DRELOC16): printf ("DataReloc16 "); break;
            case(HUNK_DRELOC8):  printf ("DataReloc8  "); break;
            case(HUNK_EXT):      printf ("ExtSymbol   ");
                                 if (!SkipSymbolHunk(fh)) return;
                                 break;
            case(HUNK_SYMBOL):   printf ("Symbol      ");
                                 if (!SkipSymbolHunk(fh)) return;
                                 break;
            case(HUNK_DEBUG):    printf ("Debug       ");
                                 Read(fh,&length,4);
                                 length <<= 2;
                                 Seek(fh,length,OFFSET_CURRENT);
                                 printf ("%d Bytes",length);
                                 break;
            case(HUNK_OVERLAY):  printf ("Overlay     Not Supported - Aborting...");
                                 return;
                                 SkipOverlayHunk(fh);
                                 break;
            case(HUNK_BREAK):    printf ("Break       ");
                                 break;
            case(HUNK_END):      printf ("End");
                                 hunkno++;
                                 break;
            default:             printf ("Unknown     Aborting...\n");
                                 return;
                                 break;
         }
         switch(~HUNK_MEM_MASK & hunk) {
            case(HUNK_RELOC32):
            case(HUNK_RELOC16):
            case(HUNK_RELOC8):
            case(HUNK_DRELOC32):
            case(HUNK_DRELOC16):
            case(HUNK_DRELOC8):  SkipRelocHunk(fh);
                                 break;
         }
         switch (HUNK_MEM_MASK & hunk) {
            case(HUNK_MEM_PUBLIC): break;
            case(HUNK_MEM_FAST):   printf (" (FAST Memory)"); break;
            case(HUNK_MEM_CHIP):   printf (" (CHIP Memory)"); break;
            default:               break;
         }
      }
   }
   printf ("\n");
}

/*************************************************************************
 * Function:    SkipHeaderHunk
 * Parameters:
 *    fh      - Pointer to the file handle of source file
 * Returns:     void;
 * Action:      Skip past an Amiga Binary 'Header' Hunk
 *************************************************************************/

static VOID SkipHeaderHunk(fh)
LONG fh;
{
   LONG length, skip;

   /*** Resident Library names ***/
   Read(fh,&length,4);
   while (length != 0) {
      Seek(fh,length << 2,OFFSET_CURRENT);
      Read(fh,&length,4);
   }
   /*** Table Size ***/
   Read(fh,&length,4);
   /*** First Hunk ***/
   Read(fh,&skip,4);
   /*** Last Hunk ***/
   Read(fh,&length,4);
   /*** Hunk Sizes ***/
   skip = (length - skip) + 1;
   Seek(fh,skip << 2,OFFSET_CURRENT);
}

/*************************************************************************
 * Function:    SkipOverlayHunk
 * Parameters:
 *    fh      - Pointer to the file handle of source file
 * Returns:     void;
 * Action:      Skip past an Amiga Binary 'Overlay' Hunk
 *************************************************************************/

static VOID SkipOverlayHunk(fh)
LONG fh;
{
   LONG length, skip, m;

   /*** Table Size ***/
   Read(fh,&length,4);
   Read(fh,&skip,4);
   skip += 3;
   Seek(fh,skip   << 2,OFFSET_CURRENT);
   Seek(fh,length << 2,OFFSET_CURRENT);
}

/*************************************************************************
 * Function:    SkipRelocHunk
 * Parameters:
 *    fh      - Pointer to the file handle of source file
 * Returns:     void;
 * Action:      Skip past an Amiga Binary 'Reloc' Hunk
 *************************************************************************/

static VOID SkipRelocHunk(fh)
LONG fh;
{
   LONG hunkno, length;
 
   Read(fh,&length,4);
   do {
      Read(fh,&hunkno,4);
      Seek(fh,length << 2,OFFSET_CURRENT);
      Read(fh,&length,4);
   } while (length != 0);
}

/*************************************************************************
 * Function:    SkipSymbolHunk
 * Parameters:
 *    fh      - Pointer to the file handle of source file
 * Returns:     TRUE if hunk skipped ok, FALSE if an unknown type of hunk
 *              was met.
 * Action:      Skip past an Amiga Binary 'Symbol' Hunk, displaying details
 *              as we go...
 * Comment:     Only displays partial data for each 'type' of symbol hunk,
 *              hopefully, the 'interesting' bits...
 *************************************************************************/

static BOOL SkipSymbolHunk(fh)
LONG fh;
{
   LONG type, symbol, length;
   BYTE hunkname[HUNK_NAME_LEN];

   printf ("Symbol Data Units:");
   Read(fh,&length,4);
   do {
      type = HUNK_EXT_MASK & length;      /*** Mask out low 3 bytes ***/
      length &= ~HUNK_EXT_MASK;           /*** Mask out high byte   ***/
      length <<= 2;
      Read(fh,hunkname,length);
      hunkname[length] = '\0';
      printf ("\n                                  ");
      switch(type) {
         case(EXT_SYMB):   printf ("Symbol Table         "); break;
         case(EXT_DEF):    printf ("Relocatable defntion "); break;
         case(EXT_ABS):    printf ("Absolute definition  "); break;
         case(EXT_RES):    printf ("Resident Library def "); break;
         case(EXT_REF32):  printf ("32-bit ref to symbol "); break;
         case(EXT_REF16):  printf ("16-bit ref to symbol "); break;
         case(EXT_REF8):   printf (" 8-bit ref to symbol "); break;
         case(EXT_COMMON): printf ("32-bit ref to COMMON ");
                           Read(fh,&length,4);
                           Read(fh,&length,4);
                           Seek(fh,length << 2,OFFSET_CURRENT);
                           break;
         default:          printf ("Unknown - Aborting...");
                           return(FALSE);
                           break;
      }
      switch(type) {
         case(EXT_SYMB):
         case(EXT_DEF):
         case(EXT_ABS):
         case(EXT_RES):    Read(fh,&symbol,4);
                           printf ("%08x %s",symbol,hunkname);
                           break;
         case(EXT_REF32):
         case(EXT_REF16):
         case(EXT_REF8):   Skip(fh);
                           printf ("         %s",hunkname);
                           break;

      }
      if (Read(fh,&length,4) == 0) length = 0;
   } while (length != 0);
   return(TRUE);
}

/*************************************************************************
 * Function:    ReadName
 * Parameters:
 *    fh      - Pointer to the file handle of source file
 *    string  - Pointer to the array to copy string into
 * Returns:     void
 * Action:      Read a Amiga Binary code format name into an array
 * Comment:     Reads the full wordlength into array, so file is in
 *              correct position for next data.
 *************************************************************************/

static VOID ReadName(fh,string)
LONG fh;
BYTE *string;
{
   LONG length;

   if (Read(fh,&length,4) == 4) {
      length <<= 2;
      Read(fh,string,length);
      string[length] = '\0';
   }
}

/*************************************************************************
 * Function:     Skip
 * Parameters:
 *    fh       - Pointer to the file handle of file to skip within
 * Returns:      The length skipped (in bytes)
 * Action:       To read the hunk length from the file, convert the length
 *               from longwords to bytes and skip that length.
 *************************************************************************/

static LONG Skip(fh)
LONG fh;
{
   LONG length;
  
   if (Read(fh,&length,4) == 4) {
      length <<= 2;
      Seek(fh,length,OFFSET_CURRENT);
   }
   return(length);
}

/*************************************************************************
 * Function:     WriteLongWord
 * Parameters:
 *    fh       - Pointer to the file handle of destination file
 *    longword - The long word (32 bits) of data to write
 * Returns:      void
 * Action:       Write to the destination file a long word of data
 * Comment:      Makes code bit neater, rather than assigning a value
 *               to a variable and writing it to the file inline.
 *************************************************************************/

static VOID WriteLongWord(fh,longword)
LONG fh;
LONG longword;
{
   Write(fh,&longword,4);
}
