#include <io.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "cmdlib.h" 
#include "support.h"

///////////////////////////////////////////////////////////
//
// DEFINES
//
///////////////////////////////////////////////////////////

#define HEADERID		(('K'<<24)+('C'<<16)+('A'<<8)+'P')

#define MAX_FILES_IN_PACK	4096	// maximum number of files in a pak file
#define	MAX_QPATH			64		// max length of a quake game pathname
#define	MAX_OSPATH			128		// max length of a filesystem pathname

#define	MAX_ERRORS			1024
#define ERR_NONFATAL		0
#define ERR_FATAL			1

///////////////////////////////////////////////////////////
//
// TYPES
//
///////////////////////////////////////////////////////////

//
// on disk
//
typedef struct
{
	char	name[56];
	int		filepos, filelen;
} dpackfile_t;

typedef struct 
{
	int		ident;
	int		dirofs;
	int		dirlen;
} dpackheader_t;

//
// in memory
//
typedef struct
{
	char	name[MAX_QPATH];
	int		filepos, filelen;
} packfile_t;

typedef struct
{
	char		filename[MAX_OSPATH];
	FILE		*handle;
	int			numfiles;
	packfile_t	*files;
} pack_t;



///////////////////////////////////////////////////////////
//
// VARIABLES
//
///////////////////////////////////////////////////////////

dpackheader_t	packheader;						// pack file header
dpackfile_t		packfiles[MAX_FILES_IN_PACK];	// pack file directory

FILE			*pPakFile;						// file pointer for the pack file
int				nPackFiles;						// number of files in the pack

char	directory[1024];						// directory where files are located
char	packfile[MAX_OSPATH];					// name of packfile

char	errorlist[MAX_ERRORS + 1][128];			// error list
int		nErrorCount;							// number of errors
char	szErrorMessage[128];					// temporary storage

///////////////////////////////////////////////////////////
//
// FUNCTIONS
//
///////////////////////////////////////////////////////////

//
// DeSlash
//
void DeSlash(char *str)
{
	while (*str)
	{
		if (*str == '\\') *str = '/';
		str++;
	}
}

//
// DisplayErrors -- displays any errors encountered while building pak file
//
void DisplayErrors (void)
{
	int	i;

	if (nErrorCount)
		printf ("Encountered %d Errors\n", nErrorCount);
	else
	{
		printf ("No Errors\n");
		return;
	}

	for (i = 0; i < nErrorCount; i++)
		printf ("%s\n", errorlist[i]);
}

//
// SafeExit
//
void SafeExit (void)
{
	// make sure the pak file is closed
	fclose (pPakFile);

	// display any errors encountered
	DisplayErrors ();

	// exit the program
	exit(1);
}

//
// CheckErrorCount
//
void CheckErrorCount (void)
{
	if (nErrorCount == MAX_ERRORS)
	{
		sprintf (szErrorMessage, "Reached MAX_ERRORS\n");
		strcpy (errorlist[nErrorCount++], szErrorMessage);
		SafeExit ();
	}
}

//
// AddError
//
void AddError (int type, char *message)
{
	// add the error to the list
	strcpy (errorlist[nErrorCount++], message);

	// make sure we havent reached our max number of errors
	CheckErrorCount ();

	// check if its fatal or not
	if (type == ERR_FATAL)
		SafeExit ();
}

//
// OpenPackFile -- open (creates) the pack file
//
void OpenPackFile (char *filename)
{
	// open the file
	pPakFile = fopen (filename, "wb");

	// make sure the file could be opened
	if (!pPakFile)
		AddError (ERR_FATAL, "Error: could not open file for writing!\n");

	// write the header
	fwrite (&packheader, sizeof (dpackheader_t), 1, pPakFile);
}

//
// ClosePackFile -- closes the pack file
//
void ClosePackFile (void)
{
	// fill in header information
	packheader.ident = HEADERID;
	packheader.dirofs = LittleLong (ftell(pPakFile));
	packheader.dirlen = LittleLong (nPackFiles * sizeof (dpackfile_t));

	// write the directory
	fwrite (packfiles, 1, sizeof (dpackfile_t) * nPackFiles, pPakFile);

	// rewind to beginning
	fseek (pPakFile, 0, SEEK_SET);

	// write the header
	fwrite (&packheader, 1, sizeof (dpackheader_t), pPakFile);

	// close the file
	fclose (pPakFile);

	printf ("Number of files processed: %d\n", nPackFiles);
}

//
// AddFileToPack -- adds a file to the pack file
//
int AddFileToPack (char *filename)
{
	int				length;			// length of the file
	int				count;			// number of bytes read
	int				handle;			// handle to the file
	unsigned char	*buf;			// pointer to file 

	// get a handle to the file
	handle = open (filename, O_RDONLY | O_BINARY);

	// get the length of the file
	length = filelength (handle);

	// allocate memory for reading in file
	buf = (unsigned char *) malloc (length);
	if(!buf)
	{
		sprintf (szErrorMessage, "%s: not enough memory\n", filename);
		AddError (ERR_NONFATAL, szErrorMessage);
		return 0;
	}

	// read in the file
	count = read (handle, buf, length);
	if (count != length)
	{
		sprintf (szErrorMessage, "%s: count != length (reading)\n", filename);
		AddError (ERR_NONFATAL, szErrorMessage);

		// close the file
		close (handle);
		return 0;
	}

	// close the file
	close (handle);

	// copy the filename to the directory entry
	strcpy (packfiles[nPackFiles].name, filename + strlen(directory) + 1);
	DeSlash (packfiles[nPackFiles].name);
	if (strlen (filename) > 56)
	{
		sprintf (szErrorMessage, "%s: string too long\n", filename);
		AddError (ERR_NONFATAL, szErrorMessage);
		return 0;
	}

	// fill in directory entry info
	packfiles[nPackFiles].filepos = LittleLong (ftell(pPakFile));
	packfiles[nPackFiles].filelen = LittleLong (length);
	nPackFiles++;

	// write the file data
	count = fwrite (buf, 1, length, pPakFile);
	if (count != length)
	{
		sprintf (szErrorMessage, "%s: count != length (writing)\n", filename);
		strcpy (errorlist[nErrorCount++], szErrorMessage);
		return 0;
	}

	free (buf);

	if (!(nPackFiles % 20))
		printf (".");

	return 1;
}

//
// PackFiles -- goes through all the subdirectories and adds the files to the pack
//
void PackFiles (char *directory)
{
	DoDirectoryWork (directory, "*.*", packfile, AddFileToPack);
	printf ("\n");
}

//
// main
//
void main (int argc, char **argv)
{
	// make sure argument(s) are specified
	if (argc < 2)
	{
		printf ("usage: pak <directory> <output>\n");
		printf ("<directory> - name of the directory to start grabbing files from\n");
		printf ("<output>    - name of .pak file (default is pak0.pak)\n");
		SafeExit ();
	}

	// get the directory name
	strcpy (directory, argv[1]);

	// get the pack file name
	if (argv[2])
		strcpy (packfile, argv[2]);
	else
		strcpy (packfile, "pak0.pak");

	// make sure the directory exists
	if (_access (directory, 00) == -1)
	{
		sprintf (szErrorMessage, "Error: directory \"%s\" does not exist\n", directory);
		AddError (ERR_FATAL, szErrorMessage);
	}
	
	printf ("Working..");
	// initialize vars
	nPackFiles = 0;
	nErrorCount = 0;
	
	// create the packfile
	OpenPackFile (packfile);

	// add all the files
	PackFiles (directory);

	// close the pack flie
	ClosePackFile ();

	// display error messages
	DisplayErrors ();
}