/*
** $VER: Decode.c (22.5.95) by Flavio Stanchina
*/

#include <exec/types.h>
#include <dos/dos.h>
#include <dos/stdio.h>

#include <clib/exec_protos.h>
#include <clib/utility_protos.h>
#include <clib/dos_protos.h>

#include <ctype.h>
#include <string.h>

#if defined(__SASC)
#include <proto/exec.h>
#include <proto/utility.h>
#include <proto/dos.h>
#endif

#include "FSCode.h"
#include "CRC32.h"

BPTR OpenPart(struct FSData *fsd)
{
open_next:
	/* For starters, open the input file when needed */
	if (fsd->In == 0)
	{
		fsd->PhysPart += 1;

		if (fsd->Multi)
			SPrintf(fsd->Temp, NameFmt, fsd->File, fsd->PhysPart);
		else
			strcpy(fsd->Temp, fsd->File);

		if (fsd->In = Open(fsd->Temp, MODE_OLDFILE))
		{
			SetVBuf(fsd->In, NULL, BUF_FULL, 16384);
		}
		else
		{
			if (!(fsd->Multi) || (fsd->PhysPart == 1))
				MyPrintFault(fsd, IoErr(), fsd->Temp);
			return 0;
		}
	}

	/* Search a !start or !mstrt line */
	while (FGets(fsd->In, fsd->Temp, sizeof(fsd->Temp)-1) != NULL)
	{
		STRPTR line = fsd->Temp + START_SKIP;
		LONG part, parts;

		/* !start, single-part file */
		if (Strnicmp(fsd->Temp, StartFmt, START_SKIP) == 0)
		{
			if (fsd->Part != fsd->Parts)
			{
				FPuts(fsd->StdErr, "found a single-part header, was looking for a multi-part header\n");
				return 0;
			}

			TrimLine(&line);
			strcpy(fsd->Name, line);

			fsd->Size = 0;
			fsd->CRC  = 0xFFFFFFFF; /* preload shift register, per CRC-32 spec */

			fsd->NumFound += 1;
			fsd->Part = fsd->Parts = 0;
		}
		/* !mstrt, multi-part file */
		else if (Strnicmp(fsd->Temp, MultiFmt, START_SKIP) == 0)
		{
			part  = GetDec(&line);
			parts = GetDec(&line);
			TrimLine(&line);

			if (part > parts)
			{
				FPrintf(fsd->StdErr, "found part %ld/%ld", part, parts);
				FPuts(fsd->StdErr, ", which is impossible\n");
				return 0;
			}

			fsd->Part += 1;

			if (fsd->Parts == 0)
			{
				strcpy(fsd->Name, line);

				fsd->Size = 0;
				fsd->CRC  = 0xFFFFFFFF; /* preload shift register, per CRC-32 spec */

				fsd->NumFound += 1;
				fsd->Parts = parts;
			}
			else
			{
				if (Stricmp(fsd->Name, line) != 0)
				{
					FPrintf(fsd->StdErr, "found part %ld/%ld", part, parts);
					FPrintf(fsd->StdErr, " of file \"%s\", was looking for file \"%s\"\n", line, fsd->Name);
					return 0;
				}

				if ((part != fsd->Part) || (parts != fsd->Parts))
				{
					FPrintf(fsd->StdErr, "found part %ld/%ld", part, parts);
					VFPrintf(fsd->StdErr, ", was looking for part %ld/%ld\n", &(fsd->Part));
					return 0;
				}
			}
		}
		/* nothing, try again */
		else continue;

		fsd->BufPtr = fsd->Buffer;
		fsd->BufCnt = 0;

		return fsd->In;
	}

	/* If we get here, there wasn't a !start line in this file */
	MyClose(fsd, fsd->In);
	fsd->In = 0;

	if (fsd->Multi)
		goto open_next;
	else return 0;
}

LONG FlushPart(struct FSData *fsd)
{
	if (fsd->BufCnt)
	{
		/* Try and open specified file for output. */
		if (fsd->Out == 0)
		{
			if ((fsd->Out = Open((fsd->To ? fsd->To : fsd->Name), MODE_NEWFILE)) == 0)
				return -2;
		}

		/* Update size and CRC. */
		fsd->Size += fsd->BufCnt;
		fsd->CRC = CRC32_blocks(fsd->Buffer, fsd->BufCnt, fsd->CRC);

		/* Write buffer to disk. */
		fsd->BufPtr = fsd->Buffer;
		if (Write(fsd->Out, fsd->BufPtr, fsd->BufCnt) != fsd->BufCnt) return -2;
		fsd->BufCnt = 0;

		if (CheckSignal(SIGBREAKF_CTRL_C)) return -3;
	}

	return 0;
}

/*
**
** RESULT
**   0  all ok
**  -2  error from FlushPart()
**  -4  error closing input file
*/
LONG ClosePart(struct FSData *fsd, LONG c)
{
	LONG tmp;

	if (tmp = FlushPart(fsd)) return tmp;

	/* This should be a !end line. Make sure... */
	if (c == '!')
	{
		fsd->Temp[0] = c;
		if (FGets(fsd->In, fsd->Temp+1, sizeof(fsd->Temp)-2) != NULL)
		{
			if (Strnicmp(fsd->Temp, EndFmt, 5) == 0)
			{
				/* Check size and CRC against those saved in the !end line. */
				TEXT *line = fsd->Temp+5;
				fsd->RealSize = GetDec(&line);
				fsd->RealCRC  = GetHex(&line);
				c = 0;

				if (fsd->Size != fsd->RealSize) FPuts(fsd->StdErr, "size mismatch\n");
				if (fsd->CRC  != fsd->RealCRC ) FPuts(fsd->StdErr, "CRC mismatch\n");
			}
		}
	}

	/*
	** This is getting complex. Close the output file if:
	**  - the user didn't specify an output file name;
	**  - the file is open;
	**  - we're at end of file (either single- or multi-part).
	*/
	if ((fsd->To == NULL) && (fsd->Out != 0) && (fsd->Part == fsd->Parts))
	{
		MyClose(fsd, fsd->Out);
		fsd->Out = 0;
	}

	return c;
}

/***** Skip characters outside the ASCII 33-126 range *****/
#ifdef __SASC
__inline
#endif
static LONG MyGetC(struct FSData *fsd)
{
	LONG c;

	do {
		if ((c = FGetC(fsd->In)) < 0) break;
	} while((c < '!') || (c > '~'));
	return c;
}

LONG Decode(struct FSData *fsd)
{
	LONG c;
	LONG len;
	ULONG tmp;

	while (1)
	{
		if (CheckSignal(SIGBREAKF_CTRL_C)) return -3;

		len = 0;
		tmp = 0;

		/* 1 */
		if ((c = MyGetC(fsd)) == ENDSTREAMCH) return -1;

		if (c == '!')
		{
			if ((c = ClosePart(fsd, c)) != 0)
				return c;

			if (OpenPart(fsd))
				continue;
			else return 0;
		}

		if (c != '#')
		{
			tmp += c - 42;
			tmp *= 85;
		}
		else len = 3;

		/* 2 */
		if ((c = MyGetC(fsd)) == ENDSTREAMCH) return -1;
		if (c == '!') return c;
		if (c != '#')
		{
			tmp += c - 42;
			tmp *= 85;
		}
		else len = 2;

		/* 3 */
		if ((c = MyGetC(fsd)) == ENDSTREAMCH) return -1;
		if (c == '!') return c;
		if (c != '#')
		{
			tmp += c - 42;
			tmp *= 85;
		}
		else len = 1;

		/* 4 */
		if ((c = MyGetC(fsd)) == ENDSTREAMCH) return -1;
		if (c == '!') return c;
		tmp += c - 42;
		tmp *= 85;

		/* 5 */
		if ((c = MyGetC(fsd)) == ENDSTREAMCH) return -1;
		if (c == '!') return c;
		tmp += c - 42;

		/* Put decoded data into output buffer, maybe flush buffer. */
		switch(len)
		{
			case 3: fsd->BufPtr[2] = tmp; tmp >>= 8;
			case 2: fsd->BufPtr[1] = tmp; tmp >>= 8;
			case 1: fsd->BufPtr[0] = tmp;
				fsd->BufPtr += len;
				fsd->BufCnt += len;
				/* Always flush buffer when it gets misaligned. */
				if ((c = FlushPart(fsd)) != 0) return c;
				break;

			default:
				*(LONG *)fsd->BufPtr = tmp;
				fsd->BufPtr += 4;
				fsd->BufCnt += 4;

				/* Flush buffer if it gets close to overflow. */
				if (fsd->BufCnt > FSD_BUFSIZ-4)
					if ((c = FlushPart(fsd)) != 0) return c;
		}
	}
}
