/**********************************************************
*
*       MKRES.C - convert absolute external reference
*                 object files to resident library 
*                 definition hunks
*
*       Copyright (c) 1989 Eric Salter
*       (whatever that means)
*
*       Freely redistributable for non-commercial use only
*       No waranties/guarantees - Use at OWN RISK!
*
*       Pardon my C!
*
**********************************************************/

#include "stdio.h"
#include "exec/types.h"

#define HUNK_NULL       0L
#define HUNK_RESIDENT   0x3E6 /* hunk known only to alink */
#define HUNK_UNIT       0x3E7
#define HUNK_NAME       0x3E8
#define HUNK_CODE       0x3E9
#define HUNK_DATA       0x3EA
#define HUNK_EXT        0x3EF
#define HUNK_END        0x3F2
#define HUNK_EOF        0x3F3 /* never occurs in object file */

#define EXT_ABS         2 /* absolute external */
#define EXT_RES         3 /* resident definition */
#define EXT_SYMB        0 /* same as HUNK_NULL */

#define OPEN_ERR        1 /* open file error */
#define EOF_ERR         2 /* unexpected EOF error */
#define HUNK_ERR        3 /* unknown hunk */
#define USE_ERR         4 /* bad command line args */
#define EXT_ERR         5 /* unknown EXT type */

void    ShipOut(), CopyName(), CopyExt(), Error();
ULONG   GetLong();
FILE    *infp, *outfp, *NIL;

void main(argc, argv)

int     argc;
char    **argv;

{
  enum	{false, true} first_ext;
  ULONG hunk,value;

   infp = stdin;	/* defaults */
   outfp = stdout;
	
   /* Make some use of the NIL: device to make logic clear */
	
   if ((NIL = fopen("NIL:","w")) == NULL)
       Error(OPEN_ERR,"NIL");

   /* go open our I/O channels */
	
   if (argc == 3) {
       if ((infp = fopen(*++argv, "r")) == NULL)
           Error(OPEN_ERR, *argv);
       if ((outfp = fopen(*++argv,"w")) == NULL)
           Error(OPEN_ERR, *argv);
   } else
       Error(USE_ERR,0);

   first_ext = true;
   while ((hunk = GetLong(infp)) != HUNK_EOF) {
       switch (hunk) {
           case HUNK_UNIT: ShipOut(hunk,outfp);
                           CopyName(infp,outfp);
                           break;
           case HUNK_NAME: ShipOut(hunk,outfp);
                           CopyName(infp,outfp);
                           first_ext = true;   /* can expect new EXT hunks */
                           break;
           case HUNK_CODE: CopyName(infp, NIL);
                           break;
           case HUNK_DATA: CopyName(infp, NIL);
                           break;
           case HUNK_EXT : if (first_ext) {
                               ShipOut(HUNK_RESIDENT,outfp);
                               first_ext = false;
                           }
                           ShipOut(hunk,outfp);
                           value = EXT_ABS;
                           while (value != EXT_SYMB) {
                               if ((value = getc(infp)) == EOF) 
                                   Error(EOF_ERR, "while parsing HUNK_EXT");
                               switch (value) {
                                   case EXT_ABS:   value = EXT_RES; /* change EXT type */
                                                   ungetc(value,infp); /* put it back */
                                                   CopyExt(infp,outfp); /* go copy hunk */
                                                   break;
                                   case EXT_SYMB:  ungetc(value,infp); /* put char back */
                                                   (void)GetLong(infp); /* dumy read to align again */
                                                   ShipOut(0L,outfp); /* send out what we would have read */
                                                   break;
                                   default:        Error(EXT_ERR,value);
                               }
                           }
                           break;
           case HUNK_NULL: ShipOut(hunk, outfp);
                           break;
           case HUNK_END : ShipOut(hunk, outfp);
                           break;
           default       : Error(HUNK_ERR,0);
       }
   }

   /* cleanup */

   if (outfp != stdout)
       fclose(outfp);
   if (infp != stdin)
       fclose(infp);
   fclose(NIL);
}	

/*
   GetLong - read in longword from file and return
   value will detect End of file and return HUNK_EOF
*/

ULONG	GetLong(in_fp)

FILE   *in_fp;

{
  ULONG    value, temp;
  UWORD    count;
  
   value = 0;
   for (count = 0; count < 4; count++) {
       if ((temp = getc(in_fp)) == EOF) {
           value = HUNK_EOF;
           break;
       }
       value <<= 8; /* make room for next char */
       value |= temp; /* save new char */
   }
   return value;
}

/*
   ShipOut - sends longword out to stream in high-byte first order.
*/

void ShipOut(value, out_fp)

ULONG  value;
FILE   *out_fp;

{
  short int count;

   for (count = 3; count >= 0; count--)
       putc((value >> (count * 8)) & 0xFF, out_fp);
}

/*
	CopyName -  reads length of name and copies N longwords from
           input to output streams (including the length).
           Length is assumed to be in first 24 bits of length.
*/

void CopyName(in_fp, out_fp)

FILE   *in_fp, *out_fp;

{
  ULONG length, charblock, count;
  
   if ((length = GetLong(in_fp)) == HUNK_EOF)
       Error(EOF_ERR, "while reading block length");
   ShipOut(length, out_fp);
   length &= 0xFFFFFF; /* mask off EXT type bits from length */
   for (count = 0; count < length ;count++) {
       if ((charblock = GetLong(in_fp)) == HUNK_EOF)
           Error(EOF_ERR,"while reading hunk body");
       ShipOut(charblock, out_fp);
   }
}

/*
   CopyExt - copies an hunk_ext from input to output streams.
*/

void CopyExt(in_fp, out_fp)

FILE   *in_fp, *out_fp;

{
  register ULONG value;

   CopyName(in_fp, out_fp);
   if ((value = GetLong(in_fp)) == HUNK_EOF)
       Error(EOF_ERR, "while reading Ext symbol value");
   ShipOut(value,out_fp);
}

/*
   Error - when all else fails
*/

void Error(type,string)

UBYTE  type;
char   *string;

{
   switch(type) {
   case OPEN_ERR:  fprintf(stderr,"Unable to open file %s - ",string);
                   break;
   case EOF_ERR :  fprintf(stderr,"Unexpected end of file %s - ");
                   break;
   case HUNK_ERR:  fputs("Unrecognised hunk encountered - ",stderr);
                   break;
   case USE_ERR :  fputs("Usage - MKRES infile outfile - ",stderr);
                   break;
   case EXT_ERR :  fputs("Illegal EXT type - ",stderr);
   default      :  fputs("Unknown error - ",stderr);
   }
   fputs("Terminating\n",stderr);
   if (outfp != stdout)
       fclose(outfp);
   if (infp != stdin)
       fclose(infp);
   if (NIL != NULL)
       fclose(NIL);
   exit(10);
}
