/* -------------------------------------------------------------------- */
/*	DISCARD.CPP 				Citadel 								*/
/* -------------------------------------------------------------------- */
/*		This file contains all the code to deal with discardable data	*/
/* -------------------------------------------------------------------- */
#include "ctdl.h"
#pragma hdrstop

/* -------------------------------------------------------------------- */
/*								Contents								*/
/* -------------------------------------------------------------------- */
/*	checkDataVer	checks version of CTDL.DAT							*/
/*	readData		reads from CTDL.DAT 								*/
/*	discardData 	discards memory 									*/
/* -------------------------------------------------------------------- */

enum discardableType
	{
	DD_STRING,		DD_RAWDATA, 	DD_STRCMP,		DD_STRCMPK,
	DD_STRCMPT, 	DD_STRCMPA, 	DD_STRCMPX,

	DD_LAST
	};

#include "comp.h"
#include "compx.h"

class DataDecompressor : CompressionEngine
	{
	uchar *Ptr;

public:
	DataDecompressor(CEDataType DataType) : CompressionEngine(DataType)
		{
		Ptr = NULL;
		}

	virtual void WriteByte(uchar ToWrite) { ToWrite = ToWrite; }

	virtual uchar GetByte(void)
		{
		return (*(Ptr++));
		}

	long Decompress(void **OriginalData);
	};

long DataDecompressor::Decompress(void **OriginalData)
	{
	Ptr = (uchar *) *OriginalData;

	long FullSize = *((long *) Ptr);

	if (FullSize > UINT_MAX)
		{
		return (0);
		}

	uchar *FullData = new uchar[(size_t) FullSize];

	if (!FullData)
		{
		return (0);
		}

	SetBytesLeft(FullSize);

	Ptr += sizeof(long) / sizeof(char);

	const uchar *Buffer;
	int BytesRead, FDPos = 0;

	while ((Buffer = CompressionEngine::Decompress(&BytesRead)) != NULL)
		{
//printf("Read: %d, Left: %ld, (%s)\n", BytesRead, GetBytesLeft(), Buffer);
		int i = 0;

//printf("Read: %d, Left: %ld, (%s)\n", BytesRead, GetBytesLeft(), Buffer);
		while (BytesRead--)
			{
			FullData[FDPos + i] = Buffer[i];
			i++;
			}

		FDPos += i;
		}

	delete *OriginalData;
	*OriginalData = FullData;
//if (GetError()){
//printf("Read: %d, Left: %ld, (%s)\n", BytesRead, GetBytesLeft(), Buffer);
//getch();
//}

	return (GetError() == CEE_NOERROR ? FullSize : 0);
	}

class DataDecompressorX : CompressionEngineX
	{
	char *Ptr;
	char *FullData, *Ptr2;

	virtual int readByte(char *Read)
		{
		*Read = *(Ptr++);
		return (TRUE);
		}

	virtual void writeByte(char ToWrite)
		{
		*(Ptr2++) = ToWrite;
		}

	virtual int isEndOfInput(void) { return (TRUE); }

public:

	long Decompress(void **OriginalData);
	};

long DataDecompressorX::Decompress(void **OriginalData)
	{
	Ptr = (char *) *OriginalData;

	long FullSize = *((long *) Ptr);

	if (FullSize > UINT_MAX)
		{
		return (0);
		}

	Ptr2 = FullData = new char[(size_t) FullSize];

	if (!FullData)
		{
		return (0);
		}

	Ptr += sizeof(long) / sizeof(char);

	DecompressIt(FullSize);

	delete *OriginalData;
	*OriginalData = FullData;

	return (FullSize);
	}

static long dataOffset;

/* -------------------------------------------------------------------- */
/*	checkDataVer	checks version of CTDL.DAT							*/
/* -------------------------------------------------------------------- */
long checkDataVer(void)
	{
	citFILE f;

	char pathToCtdlDat[128];
	strcpy(pathToCtdlDat, fullExePath);

	char *ptr = strrchr(pathToCtdlDat, '\\');

	if (ptr)
		{
		strcpy(ptr + 1, ctdlDat);
		}
	else
		{
		strcpy(pathToCtdlDat, ctdlDat);
		}

	if (citOpen(pathToCtdlDat, CO_RB, &f))
		{
		long l;

		citRead(&l, sizeof(l), 1, f);
		citClose(&f);

		dataOffset = sizeof(long);

		return (l);
		}
	else
		{
		// no CTDL.DAT - use what is in the .EXE...
		if (citOpen(fullExePath, CO_RB, &f))
			{
			long l, sig;

			citSeek(f, 0, SEEK_END);
			l = citTell(f) - sizeof(l);

			do
				{
				citSeek(f, l--, SEEK_SET);
				citRead(&sig, sizeof(sig), 1, f);
				} while (sig != 0x69696969l && l > 620000l /* kludge */);

			if (sig == 0x69696969l)
				{
				l++;
				l -= sizeof(l);
				citSeek(f, l, SEEK_SET);
				citRead(&l, sizeof(l), 1, f);
				dataOffset = l + sizeof(long);
				citSeek(f, l, SEEK_SET);
				citRead(&l, sizeof(l), 1, f);
				}
			else
				{
				l = CERROR;
				}

			citClose(&f);

			return (l);
			}
		else
			{
			return (CERROR);
			}
		}
	}

/* -------------------------------------------------------------------- */
/*	readData		reads from CTDL.DAT 								*/
/* -------------------------------------------------------------------- */
discardable *readData(int Entry, int StartSubEntry, int EndSubEntry)
	{
	assert(StartSubEntry >= 0);
	assert(EndSubEntry >= StartSubEntry);

	citFILE f;
	long l;

	compactMemory();

	if (!dataOffset)
		{
		return (NULL);
		}
	else if (dataOffset == sizeof(long))
		{
		char pathToCtdlDat[128];
		strcpy(pathToCtdlDat, fullExePath);

		char *ptr = strrchr(pathToCtdlDat, '\\');

		if (ptr)
			{
			strcpy(ptr + 1, ctdlDat);
			}
		else
			{
			strcpy(pathToCtdlDat, ctdlDat);
			}

		if (!citOpen(pathToCtdlDat, CO_RB, &f))
			{
			return (NULL);
			}
		}
	else 
		{
		if (!citOpen(fullExePath, CO_RB, &f))
			{
			return (NULL);
			}
		}

	citSeek(f, dataOffset, SEEK_SET);
	citRead(&l, sizeof(l), 1, f);

	if (Entry < l)
		{
		discardable *base = NULL, *cur;

		citSeek(f, sizeof(l) * Entry, SEEK_CUR);
		citRead(&l, sizeof(l), 1, f);
		citSeek(f, l + dataOffset - sizeof(long), SEEK_SET);

		citRead(&l, sizeof(l), 1, f);
		int SubCounter = 0;

		for (; l; l--)
			{
			if (++SubCounter > StartSubEntry)
				{
				if (SubCounter - 1 > EndSubEntry)
					{
					break;
					}

				cur = (discardable *) addLL((void **) &base, sizeof(* base));

				if (cur)
					{
					citRead(&cur->type, sizeof(cur->type), 1, f);
					citRead(&cur->length, sizeof(cur->length), 1, f);

					cur->data = new char[(uint) cur->length];
					if (cur->data)
						{
						citRead(cur->data, (uint) cur->length, 1, f);

						if (cur->type == DD_STRCMP ||
								cur->type == DD_STRCMPK ||
								cur->type == DD_STRCMPT ||
								cur->type == DD_STRCMPA)
							{
							DataDecompressor DDcmp(
								cur->type == DD_STRCMP ? CEDT_NORMAL :
								cur->type == DD_STRCMPK ? CEDT_KEYWORDS :
								cur->type == DD_STRCMPA ? CEDT_ALPHA :
								CEDT_TEXT);

							long FullSize = DDcmp.Decompress(&(cur->data));

							if (!FullSize)
								{
//cPrintf("%u !1\n", (uint)cur->length);
								discardData(base);
								citClose(&f);
								return (NULL);
								}

							cur->type = DD_STRING;
							cur->length = FullSize;
							}
						else if (cur->type == DD_STRCMPX)
							{
							long FullSize;
							DataDecompressorX *DDcmp = new DataDecompressorX;

							if (DDcmp)
								{
								FullSize = DDcmp->Decompress(&(cur->data));

								cur->type = DD_STRING;
								cur->length = FullSize;

								delete DDcmp;
								}
							else
								{
								FullSize = 0;
								}

							if (!FullSize)
								{
								discardData(base);
								citClose(&f);
								return (NULL);
								}
							}

						switch (cur->type)
							{
							case DD_STRING:
								{
								uint i, j;
								char *c;

								for (c = (char *) cur->data, i = 0, j = 0;
										(long) i < cur->length; i++, c++)
									{
									if (!(*c))
										{
										j++;
										}
									}

								cur->aux = new char*[j];

								if (cur->aux)
									{
									int k;

									for (c = (char *) cur->data, i = j = 0, k = TRUE;
											(long) i < cur->length; i++, c++)
										{
										if (k)
											{
											((char **) cur->aux)[j++] = c;
											k = FALSE;
											}

										if (!*c)
											{
											k = TRUE;
											}
										}
									}
								else
									{
									discardData(base);
									citClose(&f);
									return (NULL);
									}

								break;
								}
							}
						}
					else
						{
						discardData(base);
						citClose(&f);
						return (NULL);
						}
					}
				else
					{
					discardData(base);
					citClose(&f);
					return (NULL);
					}
				}
			else
				{
				discardable d;

				citRead(&d.type, sizeof(d.type), 1, f);
				citRead(&d.length, sizeof(d.length), 1, f);
				citSeek(f, d.length, SEEK_CUR);
				}
			}

		citClose(&f);
		return (base);
		}
	else
		{
		citClose(&f);
		return (NULL);
		}
	}

/* -------------------------------------------------------------------- */
/*	discardData 	discards memory 									*/
/* -------------------------------------------------------------------- */
void discardData(discardable *data)
	{
	discardable *cur;

	for (cur = data; cur; cur = (discardable *) getNextLL(cur))
		{
		delete [] cur->data;
		delete [] cur->aux;
		}

	disposeLL((void **) &data);
	}
