/* ------------------------------------------------------------------------ */
/* LHa for UNIX    															*/
/*				crcio.c -- crc input / output								*/
/*																			*/
/*		Modified          		Nobutaka Watazaki							*/
/*																			*/
/*	Ver. 1.14 	Source All chagned				1995.01.14	N.Watazaki		*/
/* ------------------------------------------------------------------------ */
#include "lha.h"

/* ------------------------------------------------------------------------ */
static unsigned short crctable[UCHAR_MAX + 1];
static unsigned char subbitbuf, bitcount;
#ifdef EUC
static int      putc_euc_cache;
#endif
static int      getc_euc_cache;

/* ------------------------------------------------------------------------ */
void
make_crctable( /* void */ )
{
	unsigned int    i, j, r;

	for (i = 0; i <= UCHAR_MAX; i++) {
		r = i;
		for (j = 0; j < CHAR_BIT; j++)
			if (r & 1)
				r = (r >> 1) ^ CRCPOLY;
			else
				r >>= 1;
		crctable[i] = r;
	}
}

/* ------------------------------------------------------------------------ */
#ifdef NEED_INCREMENTAL_INDICATOR
static void
put_indicator(count)
	long int        count;
{
	if (!quiet && indicator_threshold) {
		while (count > indicator_count) {
			putchar('o');
			fflush(stdout);
			indicator_count += indicator_threshold;
		}
	}
}
#endif

/* ------------------------------------------------------------------------ */
unsigned short
calccrc(p, n)
	unsigned char  *p;
	unsigned int    n;
{
	reading_size += n;
#ifdef NEED_INCREMENTAL_INDICATOR
	put_indicator(reading_size);
#endif
	while (n-- > 0)
		UPDATE_CRC(*p++);
	return crc;
}

/* ------------------------------------------------------------------------ */
void
fillbuf(n)			/* Shift bitbuf n bits left, read n bits */
	unsigned char   n;
{
	while (n > bitcount) {
		n -= bitcount;
		bitbuf = (bitbuf << bitcount) + (subbitbuf >> (CHAR_BIT - bitcount));
		if (compsize != 0) {
			compsize--;
			subbitbuf = (unsigned char) getc(infile);
		}
		else
			subbitbuf = 0;
		bitcount = CHAR_BIT;
	}
	bitcount -= n;
	bitbuf = (bitbuf << n) + (subbitbuf >> (CHAR_BIT - n));
	subbitbuf <<= n;
}

/* ------------------------------------------------------------------------ */
unsigned short
getbits(n)
	unsigned char   n;
{
	unsigned short  x;

	x = bitbuf >> (2 * CHAR_BIT - n);
	fillbuf(n);
	return x;
}

/* ------------------------------------------------------------------------ */
void
putcode(n, x)			/* Write rightmost n bits of x */
	unsigned char   n;
	unsigned short  x;
{
	while (n >= bitcount) {
		n -= bitcount;
		subbitbuf += x >> (USHRT_BIT - bitcount);
		x <<= bitcount;
		if (compsize < origsize) {
            if (fwrite(&subbitbuf, 1, 1, outfile) == 0) {
				/* fileerror(WTERR, outfile); */
			    fatal_error("Write error in crcio.c(putcode)\n");
				/* exit(errno); */
			}
			compsize++;
		}
		else
			unpackable = 1;
		subbitbuf = 0;
		bitcount = CHAR_BIT;
	}
	subbitbuf += x >> (USHRT_BIT - bitcount);
	bitcount -= n;
}

/* ------------------------------------------------------------------------ */
void
putbits(n, x)			/* Write rightmost n bits of x */
	unsigned char   n;
	unsigned short  x;
{
	x <<= USHRT_BIT - n;
	while (n >= bitcount) {
		n -= bitcount;
		subbitbuf += x >> (USHRT_BIT - bitcount);
		x <<= bitcount;
		if (compsize < origsize) {
            if (fwrite(&subbitbuf, 1, 1, outfile) == 0) {
				/* fileerror(WTERR, outfile); */
			    fatal_error("Write error in crcio.c(putbits)\n");
				/* exit(errno); */
			}
			compsize++;
		}
		else
			unpackable = 1;
		subbitbuf = 0;
		bitcount = CHAR_BIT;
	}
	subbitbuf += x >> (USHRT_BIT - bitcount);
	bitcount -= n;
}

/* ------------------------------------------------------------------------ */
int
fread_crc(p, n, fp)
	unsigned char  *p;
	int             n;
	FILE           *fp;
{
	if (text_mode)
		n = fread_txt(p, n, fp);
	else
		n = fread(p, 1, n, fp);

	calccrc(p, n);
	return n;
}

/* ------------------------------------------------------------------------ */
void
fwrite_crc(p, n, fp)
	unsigned char  *p;
	int             n;
	FILE           *fp;
{
	calccrc(p, n);
	if (verify_mode)
		return;

	if (fp) {
		if (text_mode) {
			if (fwrite_txt(p, n, fp))
				fatal_error("File write error\n");
		}
		else {
			if (fwrite(p, 1, n, fp) < n)
				fatal_error("File write error\n");
		}
	}
}

/* ------------------------------------------------------------------------ */
void
init_code_cache( /* void */ )
{				/* called from copyfile() in util.c */
#ifdef EUC
	putc_euc_cache = EOF;
#endif
	getc_euc_cache = EOF;
}

void
init_getbits( /* void */ )
{
	bitbuf = 0;
	subbitbuf = 0;
	bitcount = 0;
	fillbuf(2 * CHAR_BIT);
#ifdef EUC
	putc_euc_cache = EOF;
#endif
}

/* ------------------------------------------------------------------------ */
void
init_putbits( /* void */ )
{
	bitcount = CHAR_BIT;
	subbitbuf = 0;
	getc_euc_cache = EOF;
}

/* ------------------------------------------------------------------------ */
#ifdef EUC
void
putc_euc(c, fd)
	int             c;
	FILE           *fd;
{
	int             d;

	if (putc_euc_cache == EOF) {
		if (!euc_mode || c < 0x81 || c > 0xFC) {
			putc(c, fd);
			return;
		}
		if (c >= 0xA0 && c < 0xE0) {
			putc(0x8E, fd);	/* single shift */
			putc(c, fd);
			return;
		}
		putc_euc_cache = c;	/* save first byte */
		return;
	}
	d = putc_euc_cache;
	putc_euc_cache = EOF;
	if (d >= 0xA0)
		d -= 0xE0 - 0xA0;
	if (c > 0x9E) {
		c = c - 0x9F + 0x21;
		d = (d - 0x81) * 2 + 0x22;
	}
	else {
		if (c > 0x7E)
			c--;
		c -= 0x1F;
		d = (d - 0x81) * 2 + 0x21;
	}
	putc(0x80 | d, fd);
	putc(0x80 | c, fd);
}
#endif

/* ------------------------------------------------------------------------ */
int
fwrite_txt(p, n, fp)
	unsigned char  *p;
	int             n;
	FILE           *fp;
{
	while (--n >= 0) {
		if (*p != '\015' && *p != '\032') {
#ifdef EUC
			putc_euc(*p, fp);
#else
			putc(*p, fp);
#endif
		}

		prev_char = *p++;
	}
	return (ferror(fp));
}

/* ------------------------------------------------------------------------ */
int
fread_txt(p, n, fp)
	unsigned char  *p;
	int             n;
	FILE           *fp;
{
	int             c;
	int             cnt = 0;

	while (cnt < n) {
		if (getc_euc_cache != EOF) {
			c = getc_euc_cache;
			getc_euc_cache = EOF;
		}
		else {
			if ((c = fgetc(fp)) == EOF)
				break;
			if (c == '\n') {
				getc_euc_cache = c;
				++origsize;
				c = '\r';
			}
#ifdef EUC
			else if (euc_mode && (c == 0x8E || 0xA0 < c && c < 0xFF)) {
				int             d = fgetc(fp);
				if (d == EOF) {
					*p++ = c;
					cnt++;
					break;
				}
				if (c == 0x8E) {	/* single shift (KANA) */
					if ((0x20 < d && d < 0x7F) || (0xA0 < d && d < 0xFF))
						c = d | 0x80;
					else
						getc_euc_cache = d;
				}
				else {
					if (0xA0 < d && d < 0xFF) {	/* if GR */
						c &= 0x7F;	/* convert to MS-kanji */
						d &= 0x7F;
						if (!(c & 1)) {
							c--;
							d += 0x7F - 0x21;
						}
						if ((d += 0x40 - 0x21) > 0x7E)
							d++;
						if ((c = (c >> 1) + 0x71) >= 0xA0)
							c += 0xE0 - 0xA0;
					}
					getc_euc_cache = d;
				}
			}
#endif
		}
		*p++ = c;
		cnt++;
	}
	return cnt;
}

/* ------------------------------------------------------------------------ */
unsigned short
calc_header_crc(p, n)		/* Thanks T.Okamoto */
	unsigned char  *p;
	unsigned int    n;
{
	crc = 0;
	while (n-- > 0)
		UPDATE_CRC(*p++);
	return crc;
}
