/* Algorithm (c) Ed Ross. See RDCN.doc (bottom) for more info. */
/* Amiga implementation by Niklas Sjöberg (both C and .asm)
   reachable on 2:203/415.3@Fidonet

   After this comment, source is identical to that one in CUJ Oct 92.
*/


/* This program will compress or decompress a file
 * using RDC data compression
 *
*/

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

#define HASH_LEN 4096		/* # of hash table entries */
#define BUFF_LEN 16384		/* size of disk I/O buffer */


typedef unsigned char uchar;
typedef unsigned short int uint;

uchar *hash_tbl[HASH_LEN];	/* hash table */

uchar inbuff[BUFF_LEN];		/* I/O buffers */
uchar outbuff[BUFF_LEN];

FILE *infile, *outfile;

char usage[] =
"\nUsage: RDC <c|d> <infile> <outfile>\n"
"        c = compress infile to outfile\n"
"        d = decompress infile to outfile\n";

/* compression protos */

extern int rdc_compress (uchar * inbuff, uint inbuff_len, uchar * outbuff, uchar * hash_tbl[], uint hash_len);
extern int rdc_decompress (uchar * inbuff, uint inbuff_len, uchar * outbuff);

/* -- post error message and exit */

void 
err_exit (char *fmt, ...)
{
  va_list v;
  va_start (v, fmt);
  vfprintf (stderr, fmt, v);
  va_end (v);
  exit (1);
}

/* -- compress infile to outfile */

void 
do_compress (void)
{
  int bytes_read;
  int compress_len;

  unsigned int tot_read = 0;

  /* read infile BUFF_LEN bytes at a time */

  while ((bytes_read = fread (inbuff, 1, BUFF_LEN, infile)) > 0)
    {

	tot_read += bytes_read;
      /* compress this load of bytes */

      compress_len = rdc_compress (inbuff, bytes_read, outbuff, hash_tbl, HASH_LEN);

      /* write length of compressed buffer */

      if (fwrite (&compress_len, sizeof (int), 1, outfile) != 1)
      err_exit ("Error writing block length.\n");

      /* check for negative length indicating the buffer could not be compressed */

      if (compress_len < 0)
	compress_len = 0 - compress_len;

      if (fwrite (outbuff, compress_len, 1, outfile) != 1)
	err_exit ("Error writing compressed data.\n");
	

	printf("%8d%c%s\n",tot_read,0x1b,"[A");

      /* we're done if less than full buffer was read */

      if (bytes_read != BUFF_LEN)
	break;
    }

  /* add trailer to indicate end of file */

  compress_len = 0;

  if (fwrite (&compress_len, sizeof (int), 1, outfile) != 1)
      err_exit ("Error writing trailer.\n");

}

/* -- decompress infile to outfile -- */

void 
do_decompress (void)
{
  int block_len;
  int decomp_len;

  int tot_write = 0;

  /* read infile BUFF_LEN bytes at a time */

  for (;;)
    {
      if (fread (&block_len, sizeof (int), 1, infile) != 1)
	  err_exit ("Can't read block length.\n");

      /* check for end of file flag */


      if (block_len == 0)
	return;

      if (block_len < 0)	/* copy uncompressed chars */
	{
	  decomp_len = 0 - block_len;
	  if (fread (outbuff, decomp_len, 1, infile) != 1)
	    err_exit ("Can't read uncompressed block.\n");
	}
      else
	/* decompress this buffer */
	{
	  if (fread (inbuff, block_len, 1, infile) != 1)
	    err_exit ("Can't read compressed block.\n");

	  decomp_len = rdc_decompress (inbuff, block_len, outbuff);
	  tot_write += decomp_len;

	}

      /* and write this buffer to outfile */

      if (fwrite (outbuff, decomp_len, 1, outfile) != 1)
	err_exit ("Error writing uncompressed data.\n");

	printf("%8d%c%s\n",tot_write,0x1b,"[A");

    }
}

/* -- main -- */

void 
main (int argc, char *argv[])
{
  /* check command line */

  if (argc != 4)
    err_exit (usage);

  /* open I/O files */

  if ((infile = fopen (argv[2], "rb")) == NULL)
    err_exit ("Can't open %s for input.\n", argv[2]);

  if ((outfile = fopen (argv[3], "wb")) == NULL)
    err_exit ("Can't open %s for output.\n", argv[3]);

  /* dispatch to requested function */

  switch (argv[1][0])
    {
    case 'c':
    case 'C':
      do_compress ();
      break;

    case 'd':
    case 'D':
      do_decompress ();
      break;

    default:
      err_exit (usage);
      break;
    }

  /* and close the files */

  if (fclose (infile))
    err_exit ("Error closing input file.\n");

  if (fclose (outfile))
    err_exit ("Error closing output file.\n");
printf("\nFinished!\n");
}
