home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d5xx / d579 / bintohunk.lha / BinToHunk / BinToHunk.c < prev    next >
C/C++ Source or Header  |  1991-12-22  |  14KB  |  533 lines

  1.  
  2. /* BinToObj -- Version 1.0 -- Copyright (C) 1991 by Ray Burr
  3.    Converts a raw binary file to an Amiga Hunk format object file. */
  4.  
  5. /*
  6.   Copyright Notice (I'd use the GNU GPL but it's bigger than this
  7.   source file):
  8.  
  9.   This program may be freely copied and redistributed under the condition
  10.   that the source code and documentation is distributed with it.  Modified
  11.   versions may be distributed if this notice is left here unchanged and
  12.   the history, including credit to the original author, is clearly
  13.   documented.
  14. */
  15.  
  16. /*
  17.   HISTORY:
  18.  
  19.   (911201 ryb) Created.
  20.  
  21. */
  22.  
  23. #include <stddef.h>    /* Needed for size_t in Lattice C Version 5.04 */
  24. #include <stdlib.h>
  25. #include <stdio.h>
  26. #include <string.h>
  27.  
  28. /* Hacks to make perfect :-) ANSI C code work under Lattice C. */
  29.  
  30. #ifndef EXIT_SUCCESS
  31. #define EXIT_SUCCESS 0
  32. #define EXIT_FAILURE 20
  33. #endif
  34.  
  35. #ifndef SEEK_END
  36. #define SEEK_END 2
  37. #endif
  38.  
  39. /* More space efficient than isdigit() in some C library implemintations. */
  40. #define CHAR_DIGIT_P(c) ((c) >= '0' && (c) <= '9')
  41.  
  42. #define HUNK_UNIT    0x03e7
  43. #define HUNK_DATA    0x03ea
  44. #define HUNK_EXT     0x03ef
  45. #define HUNK_END     0x03f2
  46.  
  47. #define EXT_DEF      0x01
  48. #define EXT_ABS      0x02
  49.  
  50. #define HUNK_FAST (1 << 31)
  51. #define HUNK_CHIP (1 << 30)
  52.  
  53. #define TRUE 1
  54. #define FALSE 0
  55.  
  56. #define BUFFER_SIZE 4096
  57.  
  58. char *program_name = "BinToHunk";
  59.  
  60. /* Common error messages. */
  61. char *write_error_msg = "Error writing output file";
  62. char *length_error_msg = "Couldn't determine input file's length";
  63.  
  64. /* Define `BOOTSTRAP' if you don't already have a working BinToHunk
  65.    executable to convert usage.txt to usage.o. */
  66. #ifndef BOOTSTRAP
  67. /* Usage message in file usage.o which was created by BinToHunk.
  68.    (Couldn't resist it.) */
  69. extern char usage_message[];
  70. #else
  71. /* Temporary definition to make linker happy. */
  72. char *usage_message = "*** BOOTSTRAP Version ***\n";
  73. #endif
  74.  
  75. /* Print an error message after in I/O error and exit with error status. */
  76.  
  77. static void
  78. pfail (const char *message)
  79. {
  80.   fprintf (stderr, "%s: ", program_name);
  81.   perror (message);
  82.   exit (EXIT_FAILURE);
  83. }
  84.  
  85. /* Print an error message and exit with error status. */
  86.  
  87. static void
  88. fail (const char *message)
  89. {
  90.   fprintf (stderr, "%s: %s", program_name, message);
  91.   exit (EXIT_FAILURE);
  92. }
  93.  
  94. /* Allocate memory and handle failure. */
  95.  
  96. static void *
  97. xmalloc (size_t size)
  98. {
  99.   void *block;
  100.   block = malloc (size);
  101.   if (!block)
  102.     fail ("Couldn't allocate memory");
  103.   return block;
  104. }
  105.  
  106. /* Write the 32-bit value LONGWORD to the stream STREAM in big-endian byte
  107.    order and handle errors. */
  108.  
  109. #ifdef __GNUC__
  110. __inline__
  111. #endif
  112. static void
  113. putl (FILE *stream, long longword)
  114. {
  115.   long lw = longword;
  116.   /* This assumes big endian byte order. */
  117.   if (fwrite (&lw, sizeof (long), 1, stream) < 1)
  118.     pfail (write_error_msg);
  119. }
  120.  
  121. /* Write LENGTH bytes of VALUE to OUTFILE. */
  122.  
  123. #ifdef __GNUC__
  124. __inline__
  125. #endif
  126. static void
  127. write_pad (FILE *outfile, int value, size_t length)
  128. {
  129.   while (length-- > 0)
  130.     if (fputc (value, outfile) == EOF)
  131.       pfail (write_error_msg);
  132. }
  133.  
  134. /* Write LENGTH bytes to OUTFILE representing TERMINATOR in big-endian byte
  135.    order.  TERMINATOR is a two's-compliment signed value and will be sign
  136.    extended. */
  137.  
  138. #ifdef __GNUC__
  139. __inline__
  140. #endif
  141. static void
  142. write_terminator (FILE *outfile, int terminator, int length)
  143. {
  144.   if (length == 0)
  145.     return;
  146.   if (length > sizeof (int))
  147.     {
  148.       write_pad (outfile, (terminator < 0 ? -1 : 0), length - sizeof (int));
  149.       length = 4;
  150.     }
  151.   /* This assumes big endian byte order. */
  152.   if (fwrite (&terminator + 4 - length, 1, length, outfile) < length)
  153.     pfail (write_error_msg);
  154. }
  155.  
  156. /* Determine length of file STREAM was opened with.  Moves file position
  157.    to the begining.  Exits with a message if it is unsuccessful. */
  158.  
  159. static size_t
  160. file_length (FILE *stream)
  161. {
  162.   long length;
  163.  
  164.   if (fseek (stream, 0, SEEK_END))
  165.     pfail (length_error_msg);
  166.   length = ftell (stream);
  167.   if (length < 0)
  168.     pfail (length_error_msg);
  169.   rewind (stream);
  170.   return (size_t) length;
  171. }
  172.  
  173. /* Copy INFILE to OUTFILE until INFILE reaches end-of-file.  This function
  174.    allocates a buffer who's size is determined by the BUFFER_SIZE macro
  175.    symbol.  I/O errors cause a call to pfail(). */
  176.  
  177. static void
  178. copy_data (FILE *infile, FILE *outfile)
  179. {
  180.   void *buffer;
  181.   size_t bytes_read;
  182.  
  183.   buffer = xmalloc (BUFFER_SIZE);
  184.   while (!feof (infile))
  185.     {
  186.       bytes_read = fread (buffer, 1, BUFFER_SIZE, infile);
  187.       if (ferror (infile))
  188.         pfail ("Error reading input file");
  189.       if (bytes_read <= 0)
  190.         continue;
  191.       fwrite (buffer, 1, bytes_read, outfile);
  192.       if (ferror (outfile))
  193.         pfail (write_error_msg);
  194.     }
  195.   free (buffer);
  196. }
  197.  
  198. /* Write NAME to OUTFILE as a name in the format used in Amiga Hunk object
  199.    files; a longword of length in longwords, and a name padded to a longword
  200.    boundry with zeros.  EXT_TYPE is a value that will be put in the first
  201.    (most significant) byte of the length longword. */
  202.  
  203. static void
  204. write_name (FILE *outfile, const char *name, int ext_type)
  205. {
  206.   size_t name_length, pad_length;
  207.  
  208.   name_length = strlen (name);
  209.   pad_length = 3 - (name_length + 3) % 4;
  210.   putl (outfile, ((name_length + 3) / 4) | (ext_type << 24));
  211.   if (fwrite (name, 1, name_length, outfile) < name_length)
  212.     pfail (write_error_msg);
  213.   write_pad (outfile, 0, pad_length);
  214. }
  215.  
  216. /* Display a usage message and exit with success status. */
  217.  
  218. static void
  219. usage (void)
  220. {
  221.   fputs (usage_message, stderr);
  222.   exit (EXIT_SUCCESS);
  223. }
  224.  
  225. /* Displays MESSAGE, if it is non-NULL, and a message saying how to get
  226.    usage information.  Exits with failure status. */
  227.  
  228. static void
  229. fail_with_usage (const char *message)
  230. {
  231.   if (message)
  232.     fprintf (stderr, "%s: %s\n", program_name, message);
  233.   fprintf (stderr, "Type `%s -?' for usage.\n", program_name);
  234.   exit (EXIT_FAILURE);
  235. }
  236.  
  237. /* Point of entry. */
  238.  
  239. void
  240. main (int argc, char *argv[])
  241. {
  242.   FILE *infile, *outfile;    /* Streams for the input and output files. */
  243.   char *infile_name;        /* Name of the input file. */
  244.   char *outfile_name;        /* Name of the output file. */
  245.   char *block_ident;        /* Name of the indentifier for the array. */
  246.   char *block_length_ident;    /* Name of the indentifier specifying the
  247.                    length of the array. */
  248.   long terminator;        /* The value of the terminator object. */
  249.   long term_length;        /* Number of bytes in the terminator.  Zero
  250.                                    means no terminator. */
  251.   long memory_mode;        /* Tells the loader where to load the hunk. */
  252.   int nolength_flag;        /* Flag: Don't define a length symbol. */
  253.   int absolute_flag;        /* Flag: Use an absolute symbol definition for
  254.                    the length. */
  255.   char *base;            /* Base of input filename. (dir/base.txt) */
  256.   size_t base_length;        /* Length of base input filename. */
  257.   int i, arg_count;        /* Counters for arg processing. */
  258.   size_t pad_length;        /* Number of bytes to make data an even number
  259.                    of longwords. */
  260.   size_t length;        /* The length of the input file in bytes. */
  261.   size_t object_size;        /* The size of one item in the array. */
  262.   size_t num_objects;        /* Number of objects read in. */
  263.   size_t total_length;        /* Length of data before longword padding. */
  264.   int object_pad_length;    /* Number of bytes to pad data to an
  265.                    even number of objects. */
  266.   int data_lw_length;        /* Total number of longwords in data. */
  267.  
  268.   /* Set some defaults. */
  269.   nolength_flag = FALSE;
  270.   absolute_flag = FALSE;
  271.   term_length = 0;
  272.   terminator = 0;
  273.   object_size = 1;
  274.   memory_mode = 0;
  275.  
  276.   infile_name = NULL;
  277.   outfile_name = NULL;
  278.   block_ident = NULL;
  279.   block_length_ident = NULL;
  280.  
  281.   /* Format users hard disk if no arguments are given. */
  282.   if (argc <= 1)
  283.     usage ();
  284.  
  285.   /* Parse argument line. */
  286.   arg_count = 0;
  287.   for (i = 1; i < argc; ++i)
  288.     {
  289.       if (!strcmp (argv[i], "?"))
  290.         usage ();
  291.       if (argv[i][0] == '-')
  292.     {
  293.       /* Deal with options. */
  294.       switch (argv[i][1])
  295.         {
  296.         case 'c':
  297.         case 'C':
  298.           memory_mode |= HUNK_CHIP;
  299.           goto no_arg;
  300.         case 'f':
  301.         case 'F':
  302.           memory_mode |= HUNK_FAST;
  303.           goto no_arg;
  304.         case 'l':
  305.         case 'L':
  306.           nolength_flag = TRUE;
  307.           goto no_arg;
  308.         case 'a':
  309.         case 'A':
  310.           absolute_flag = TRUE;
  311.           goto no_arg;
  312.         case 't':
  313.         case 'T':
  314.           term_length = -1;    /* Turns into the length of one object. */
  315.           terminator = 0;    /* The default. */
  316.               if (argv[i][2] == '\0')
  317.         break;
  318.           if (!CHAR_DIGIT_P (argv[i][2])
  319.                   && !(argv[i][2] == '-' && CHAR_DIGIT_P (argv[i][3])))
  320.         fail_with_usage ("Invalid argument to -t");
  321.           terminator = atoi (argv[i] + 2);
  322.           break;
  323.         case 's':
  324.         case 'S':
  325.           if (!CHAR_DIGIT_P (argv[i][2]))
  326.         fail_with_usage ("Invalid argument to -s");
  327.           object_size = atoi (argv[i] + 2);
  328.           if (object_size <= 0)
  329.         fail_with_usage ("Bad value for -s option");
  330.           break;
  331.         case '?':
  332.           usage ();
  333.           break;
  334.         no_arg:
  335.           /* Verify that no argument to the option was given. */
  336.           if (argv[i][2] != 0)
  337.         {
  338.           fprintf (stderr,
  339.                "%s: Option `-%c' does not take an argument\n",
  340.                program_name, argv[i][1]);
  341.           fail_with_usage (NULL);
  342.         }
  343.           break;
  344.         case '\0':
  345.         default:
  346.           fprintf (stderr, "%s: Bad option `%s'\n",
  347.                        program_name, argv[i]);
  348.           fail_with_usage (NULL);
  349.           break;
  350.         }
  351.     }
  352.       else
  353.     {
  354.       /* Assign non-option arguments to variables. */
  355.       switch (arg_count)
  356.             {
  357.         case 0:
  358.           infile_name = argv[i];
  359.           break;
  360.         case 1:
  361.           outfile_name = argv[i];
  362.           break;
  363.         case 2:
  364.           block_ident = argv[i];
  365.           break;
  366.         case 3:
  367.           block_length_ident = argv[i];
  368.           break;
  369.         default:
  370.           fail_with_usage ("Too many arguments");
  371.           break;
  372.             }
  373.           ++arg_count;
  374.     }
  375.     }
  376.  
  377.   if (arg_count == 0)
  378.     fail_with_usage ("No output filename");
  379.  
  380.   /* A negative TERM_LENGTH means use units of object_size bytes. */
  381.   if (term_length < 0)
  382.     term_length = object_size * -term_length;
  383.  
  384.   /* Find the base name of INFILE_NAME. */
  385.   base = strrchr (infile_name, '/');
  386.   if (base == NULL)
  387.     base = strrchr (infile_name, ':');
  388.   if (base == NULL)
  389.     base = infile_name;
  390.  
  391.   /* Find the length of the base name of INFILE_NAME. */
  392.   {
  393.     char *dot;
  394.     dot = strrchr (infile_name, '.');
  395.     if (dot == NULL)
  396.       base_length = strlen (base);
  397.     else
  398.       base_length = dot - base;
  399.   }
  400.  
  401.   /* Default output filename is BASE with ".o" appened. */
  402.   if (outfile_name == NULL)
  403.     {
  404.       outfile_name = xmalloc (base_length + 3);
  405.       memcpy (outfile_name, base, base_length);
  406.       strcpy (outfile_name + base_length, ".o");
  407.     }
  408.  
  409.   /* Default BLOCK_IDENT is BASE with "_" prepended. */
  410.   if (block_ident == NULL)
  411.     {
  412.       block_ident = xmalloc (base_length + 2);
  413.       block_ident[0] = '_';
  414.       memcpy (block_ident + 1, base, base_length);
  415.       block_ident[base_length + 1] = '\0';
  416.     }
  417.  
  418.   /* Default BLOCK_LENGTH_IDENT is BASE with "_" prepended and "_length"
  419.      appended. */
  420.   if (block_length_ident == NULL && !nolength_flag)
  421.     {
  422.       block_length_ident = xmalloc (strlen (block_ident) + 8);
  423.       strcpy (block_length_ident, block_ident);
  424.       strcat (block_length_ident, "_length");
  425.     }
  426.  
  427.   /* Open output file in binary mode. */
  428.   outfile = fopen (outfile_name, "wb");
  429.   if (outfile == NULL)
  430.     pfail ("Can't open output file");
  431.  
  432.   /* Start un-named program unit. */
  433.   putl (outfile, HUNK_UNIT);
  434.   putl (outfile, 0);
  435.  
  436.   /* Start data block. */
  437.   putl (outfile, HUNK_DATA);
  438.  
  439.   /* Open input file in binary mode. */
  440.   infile = fopen (infile_name, "rb");
  441.   if (infile == NULL)
  442.     pfail ("Can't open input file");
  443.  
  444.   /* Find length of input file. */
  445.   length = file_length (infile);
  446.  
  447.   /* Calculate the number of objects.  Any bytes left over are counted as
  448.      an object. */
  449.   num_objects = (length + object_size - 1) / object_size;
  450.  
  451.   /* Calculate the number of bytes needed to make the array's length an
  452.      even multiple of OBJECT_SIZE. */
  453.   object_pad_length = num_objects * object_size - length;
  454.  
  455.   /* Find the total length of all the data used in the data block. */
  456.   total_length = length + object_pad_length + term_length;
  457.  
  458.   /* Find the total number of longwords in the data block. */
  459.   data_lw_length = (total_length + 3) / 4;
  460.  
  461.   /* Find the number of bytes needed to make the data block an even number
  462.      of longwords. */
  463.   pad_length = data_lw_length * 4 - total_length;
  464.  
  465.   /* Account for length variable. */
  466.   if (block_length_ident != NULL && !absolute_flag)
  467.     data_lw_length += 1;
  468.  
  469.   /* Output the length of the data block and attach memory attribute flags. */
  470.   putl (outfile, data_lw_length | memory_mode);
  471.  
  472.   /* If needed, output the length of the array in objects as the first
  473.      longword of the data block. */
  474.   if (block_length_ident && !absolute_flag)
  475.     putl (outfile, num_objects);
  476.  
  477.   /* Copy the array from the input file. */
  478.   copy_data (infile, outfile);
  479.  
  480.   /* Close the input file. */
  481.   if (fclose (infile))
  482.     pfail ("Error closing input file");
  483.  
  484.   /* Align to object size. */
  485.   write_pad (outfile, 0, object_pad_length);
  486.  
  487.   /* Write the (possibly zero length) terminator. */
  488.   write_terminator (outfile, terminator, term_length);
  489.  
  490.   /* Align to longword. */
  491.   write_pad (outfile, 0, pad_length);
  492.  
  493.   /* Now start the external symbol block. */
  494.   putl (outfile, HUNK_EXT);
  495.  
  496.   /* Write a symbol data unit defining BLOCK_IDENT as a symbol
  497.      relative to the start of the data block. */
  498.   write_name (outfile, block_ident, EXT_DEF);
  499.   putl (outfile, (block_length_ident && !absolute_flag) ? 4 : 0);
  500.  
  501.   /* If an array length symbol is wanted... */
  502.   if (block_length_ident)
  503.     {
  504.       if (absolute_flag)
  505.     {
  506.           /* Write a symbol data unit defining BLOCK_LENGTH_IDENT as an
  507.          absolute symbol defining the number of objects. */
  508.       write_name (outfile, block_length_ident, EXT_ABS);
  509.       putl (outfile, num_objects);
  510.     }
  511.       else
  512.     {
  513.           /* Write a symbol data unit defining BLOCK_LENGTH_IDENT as a
  514.          symbol relative to the start of the data block. */
  515.           write_name (outfile, block_length_ident, EXT_DEF);
  516.       putl (outfile, 0);
  517.     }
  518.     }
  519.  
  520.   /* End the external symbol block. */
  521.   putl (outfile, 0);
  522.  
  523.   /* End the hunk and the program unit. */
  524.   putl (outfile, HUNK_END);
  525.  
  526.   /* Close the output file. */
  527.   if (fclose (outfile))
  528.     pfail ("Error closing output file");
  529.  
  530.   /* Later. */
  531.   exit (EXIT_SUCCESS);
  532. }
  533.