
/* Uncompress program.  Adapted from a version written by Keith Calkins
   that was last modified Nov 29, 1996.  Written in C by Tom Watson
   Jan 08,'97.  */
   
/* How to use:
	The program has ONE parameter.  It is the file name of the Sigma format
	compressed file.  It creates a text file with the suffix ".co" added to
	the original file that is the uncompressed file.  The program allows the 
input
	to be either 108 bytes (without sequence numbers) or 120 bytes (with
	sequence numbers) as long as the compressed data is intact.  The de-
blocker
	program takes the record size of the original file when it is written out, 
so
	this is a necessary feature.  When writing the compressed file, trailing 
blanks
	are removed, as this can take up considerable space, and the line 
terminator
	'\n' is inserted.  On various systems this will add <nl> (Unix), <cr/lf> 
MS-DOS
	or <cr> Mac's MPW.  It should be proper for the system.  If there are any
	questions, or comments, please contact the author at "tsw@johana.com" 
(home
	email address), or "tsw@3do.com" (present employer's address).
	*/
	
/* Please note that some debugging stuff has been "#ifdef'd" out.  I used it to 
make
   sure the program was operational.  Some of the trailing bits in a record 
gets
   very tricky at times.  */

/* The author would like to know of your successes/failures/etc. in using this
   program.  Notes to the email addresses above are appreciated.  Please do not
   publish/distrubute this program without notifying the author.  I'd just like
   to know where it has been.  Tom Watson Jan 10, 1997 */
   
#include <stdio.h>
#include <string.h>

/* #define DEBUG */
/* #define DEBUG1 */

#include "ebcdictw.h"     /* Translate table */

#define REC_LEN 108		/* Standard compressed record length, with no
						   sequence numbers (col 73-80). */
#define ID_BYTE 0x18	/* Binary record type (0x38/0x18) */

/* Global thing to translate */
static unsigned char alpha[] = 
		"        0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.<(+|&$*);~-/,%_>:'=";
int chars_in_debug = 0;

#ifdef DEBUG
FILE *debug;
static void test_debug(int incr);
#endif

int get_bits (FILE *file_ref, int num_bits);

int main (int argc, char *argv[])
{
FILE *input;
FILE *output = NULL;
int six_bit;
int end_file;
int rec_pos;
int i;
unsigned char out_rec[8192];
char out_file_name[200];		/* It may have a path!! */

if (argc != 2)
	{
	fprintf (stderr, "File specification??\n");
	exit (1);
	}
input = fopen (argv[1], "rb");
if (input == NULL)
	{
	fprintf (stderr, "Input file error.  Name? \n");
	exit (1);
	}

strcpy (out_file_name, argv[1]);
strcat (out_file_name, ".so");
output = fopen (out_file_name, "w");
if (output == NULL)
	{
	fprintf (stderr, "Can't open output file named: %s\n", out_file_name);
	exit (1);
	}

#ifdef DEBUG
debug = fopen ("debug_file", "w");
#endif
	
end_file = 0;
rec_pos = 0;					/* Position in record */
/* Gather the input, send to output */
while (!end_file)
	{
	six_bit = get_bits (input, 6);

#ifdef DEBUG
	fprintf (debug, "RP: %d ", rec_pos);
	test_debug (7);
	if (rec_pos > 100)			/* I'm getting an error here?!? */
		{
		fprintf (stderr, "Record position out of range: %d\n", rec_pos);
		exit (1);
		}
#endif
	switch (six_bit)
		{
	  case 0:		/* Padding */
		break;
		
	  case 1:
		fprintf (stderr, "Illegal 6 bit goodie (0x%02X)\n", six_bit);
		break;
		
	  case 2:
		/* End of record */
		/* Strip off blanks, so we won't take up so much room!! */
		for (i = 0; i < rec_pos; i++)
			{
			if (out_rec[rec_pos - i - 1] != ' ')
				break;
			}
#ifdef DEBUG
		fprintf (debug, "I: %d ", i);
		test_debug (6);
#endif
		out_rec[rec_pos - i] = '\n';
		fwrite (out_rec, 1, rec_pos - i + 1, output);
		rec_pos = 0;		/* So we can fill it up from the beginning!! */
		break;
		
	  case 3:
		/* End of file */
		end_file = 1;
		break;
		
	  case 4:
		/* Get the next 8 bits */
#ifdef DEBUG
		fprintf (debug, "8B");
#endif
		out_rec[rec_pos++] = ebcdic [get_bits (input, 8)];
		break;
		
	  case 5:
		/* n + 1 blanks */
		six_bit = get_bits (input, 6);
		six_bit += 1;
		for (i = 0; i < six_bit; i++)
			out_rec[rec_pos++] = ' ';
		break;
	  
	  case 6:
		/* n + 65 blanks */
		six_bit = get_bits (input, 6);
		six_bit += 65;
		for (i = 0; i < six_bit; i++)
			out_rec[rec_pos++] = ' ';
		break;
		
	  default:
		out_rec[rec_pos++] = alpha[six_bit];

#ifdef DEBUG		
		fputc (alpha[six_bit], debug);
		fputc (' ', debug);
		test_debug (2);
#endif
		break;
		}
	}
fclose (output);
fclose (input);
return 0;
}

int get_bits (FILE *file_ref, int num_bits)
{
static unsigned char in_rec[REC_LEN];
static int bit_pos = -1;			/* No record read in yet. */
static int rec_seq = 0;
static int bytes_in_rec;
int bytes_read;
int temp_char;
int result = 0;
int i;
int checksum;
int the_byte;
int bits_avail;
static int mask[8] = {0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF};

while (num_bits != 0)
	{
	if (bit_pos == -1 || bit_pos + num_bits > bytes_in_rec * 8)
		{
		/* We need to read a new record */
		while (1)
			{
			temp_char = fgetc (file_ref);
			if (temp_char == EOF)
				{
				fprintf (stderr, "End file encountered abnormally\n");
				exit (1);
				}
			if ((temp_char | 0x20) == 0x38)
				{
				/* We have the proper first byte of a record!! */
				ungetc (temp_char, file_ref);		/* Put it back */
				break;
				}
			}
		bytes_read = fread (in_rec, 1, REC_LEN, file_ref);
		if (bytes_read != REC_LEN)
			{
			fprintf (stderr, "Funny record correspondance\n");
			exit (1);
			}
		/* See that record sequence matches */
		if (in_rec[1] != (rec_seq & 0xFF))
			{
			fprintf (stderr, "Sequence match.  Is: 0x%02X, Should be: 
0x%02X\n",
				in_rec[1], (rec_seq & 0xFF));
			exit (1);
			}
#ifdef DEBUG			
		fprintf (debug, "S:%d ", rec_seq);
		test_debug (5);
#endif
		rec_seq++;			/* Increment the sequence number */
		/* See that things are in the correct range */
		bytes_in_rec = in_rec[3];
		if (bytes_in_rec <= 5 || bytes_in_rec > 108)
			{
			fprintf (stderr, "Record size (%d) out of range\n", in_rec[3]);
			exit (1);
			}
		/* Calculate the checksum */
		checksum = 0;
		for (i = 0; i < bytes_in_rec; i++)
			{
			if (i == 2)
				continue;			/* Not the checksum byte */
			checksum += in_rec[i];
			}
		if ((checksum & 0xFF) != in_rec[2])
			{
			fprintf (stderr, "Checksum doesn't match.  Is: 0x%02X, Should 
be: 0x%02X\n",
				checksum & 0xFF, in_rec[2]);
			exit (1);
			}
		bit_pos = 32;			/* Skip the first 4 bytes */
		}
	/* We have a position here, get some */
	the_byte = in_rec[bit_pos >> 3];
	/* fprintf (debug, "B: 0x%03X ", the_byte); */
	/* test_debug (9); */
	bits_avail = 8 - (bit_pos & 0x07);
	if (bits_avail >= num_bits)
		{
		/* We can get them all!! */
		result <<= num_bits;
		the_byte >>= bits_avail - num_bits;
		result |= the_byte & mask[num_bits - 1];
		bit_pos += num_bits;		/* We got this many */
		num_bits = 0;
		}
	else
		{
		/* Only partial in a byte */
		/* fprintf (debug, "OR: 0x%02X ", result); */
		result <<= bits_avail;
		/* fprintf (debug, "MR: 0x%02X ", result); */
		result |= the_byte & mask[bits_avail - 1];
		/* fprintf (debug, "NR: 0x%02X ", result); */
		/* test_debug (9 * 3); */
		bit_pos += bits_avail;
		num_bits -= bits_avail;
		}
#ifdef DEBUG1
	fprintf (debug, "BP: %d ", bit_pos);
	test_debug (7);
#endif
	}

#ifdef DEBUG1
fprintf (debug, "0x%02X ", result);
test_debug(5);
#endif

return result;
}

#ifdef DEBUG
static void test_debug(int incr)
{
chars_in_debug += incr;
if (chars_in_debug > 80)
	{
	fputc ('\n', debug);
	chars_in_debug = 0;
	}
fflush (debug);
}
#endif
