/*
 * VisPatch :  Quake level patcher for water visibility.
 *
 * Copyright (C) 1997-2006  Andy Bay <IMarvinTPA@bigfoot.com>
 * Copyright (C) 2006-2024  O. Sezer <sezero@users.sourceforge.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to:
 *
 * Free Software Foundation, Inc.
 * 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
*/

#include "arch_def.h"
#include "compiler.h"
#include "q_stdinc.h"
#include "q_endian.h"
#include "utilslib.h"
#include "vispatch.h"

static visdat_t		*visdat;
static pakentry_t	NewPakEnt[MAX_FILES_IN_PACK];
static unsigned int	NPcnt, numvis;
static int		usepak = 0;
static int		mode = 0;	/* 0: patch, 2: patch without overwriting, 1: extract	*/

static FILE	*InFile, *OutFile, *fVIS;

#define		TEMP_FILE_NAME		"~vistmp.tmp"
static char	Path[MAX_OSPATH];	/* where we shall work: getcwd(), changed by -dir	*/
static char	Path2[MAX_OSPATH];	/* temporary filename buffer				*/
static char	TempFile[MAX_OSPATH];	/* name of our temporary file on disk			*/
static char	VIS[MAX_OSPATH] = "vispatch.dat";	/* vis data filename. changed by -data	*/
static char	FoutPak[MAX_OSPATH] = "pak*.pak";	/* name of the output pak file		*/
static char	File[MAX_OSPATH] = "pak*.pak";		/* filename pattern we'll be globbing	*/
static char	CurName[VISPATCH_IDLEN + 6];	/* name of the currently processed file.	*/


/*============================================================================*/

/* Functions used for vis data loading: */
static void loadvis (FILE *fp);
static void freevis (void);

/* Functions used for patching process: */
static int BSPFix (int InitOFFS);
static int PakFix (int Offset);
static int OthrFix(int Offset, int Length);
static int ChooseLevel(char *FileSpec, int Offset, int length);

/* Functions used for extraction process: */
static int PakNew (int Offset);
static int BSPNew (int Offset);
static int ChooseFile (char *FileSpec, int Offset, int length);

/*============================================================================*/

#define CLOSE_ALL						\
do {								\
	if (InFile)						\
		fclose(InFile);					\
	if (OutFile)						\
		fclose(OutFile);				\
	if (fVIS)						\
		fclose(fVIS);					\
} while (0)

#define FWRITE_ERROR						\
do {								\
	CLOSE_ALL;						\
	remove(TempFile);					\
	freevis();						\
	Error("Errors during fwrite: Not enough disk space?");	\
} while (0)


/*============================================================================*/

static void usage (void)
{
	printf("Usage: vispatch <file> [arguments]\n\n");
/*
	printf(" -h, -help  or  --help : Prints this message.\n\n");
*/
	printf("<file> : Level filename pattern, bsp or pak type, wildcards\n");
	printf("\t are allowed, relative paths not allowed. See the\n");
	printf("\t examples.\n\n");
	printf(" -dir  : Specify the directory that the level files are in.\n\n");
	printf(" -data : Specify the vis data file. Wildcards are not allowed.\n\n");
	printf(" -new  : Tells the program not to overwrite the original level\n");
	printf("\t files.  In case of pak files, the patched the levels\n");
	printf("\t will be put into a new pak file. In case of standalone\n");
	printf("\t maps files, the original maps are renamed to name.bak,\n");
	printf("\t and the patched ones are saved as name.bsp .\n");
	printf("\t Can not be used in combination with -extract.\n\n");
	printf(" -extract: Retrieve all the vis data from the given file.\n");
	printf("\t Can not be used in combination with -new.\n\n");
#if 1
	printf("See the vispatch.txt file for further information.\n\n");
#else
	printf("Press Enter to continue...\n");

	while (('\n' != getchar())) { }

	printf("Examples:\n");
	printf("vispatch map25.bsp\n");
	printf("  Patch and overwrite map25.bsp using vis data from the default\n");
	printf("  data file vispatch.dat\n\n");
	printf("vispatch map25.bsp -data map25.vis\n");
	printf("  Use map25.vis as the data file instead of vispatch.dat\n\n");
	printf("vispatch -dir hipnotic -data hipnotic.vis\n");
	printf("  Patch pak or bsp type level files under the directory named\n");
	printf("  hipnotic, using  hipnotic.vis  as the data.\n\n");
	printf("vispatch somefile.ext -extract\n");
	printf("  Retrieve all vis data from somefile.ext and put it into a file\n");
	printf("  named vispatch.dat  (You must backup any previous one!.)\n\n");
	printf("vispatch somefile.ext -data some.dat -extract\n");
	printf("  Retrieve all vis data from the somefile.ext and dump it into\n");
	printf("  a file named some.dat instead of vispatch.dat\n\n");
	printf("vispatch -dir hipnotic -extract\n");
	printf("  Retrieve all vis data from the files in the directory\n");
	printf("  hipnotic, and dump it into a file named vispatch.dat\n");
#endif
}


/*============================================================================*/

#if defined(__MINGW32__)

/* From MinGW runtime/init.c :
 * [...] GetMainArgs (used below) takes a fourth argument
 * which is an int that controls the globbing of the command line. If
 * _CRT_glob is non-zero the command line will be globbed (e.g. *.*
 * expanded to be all files in the startup directory). In the mingw32
 * library a _CRT_glob variable is defined as being -1, enabling
 * this command line globbing by default. To turn it off and do all
 * command line processing yourself (and possibly escape bogons in
 * MS's globbing code) include a line in one of your source modules
 * defining _CRT_glob and setting it to zero, like this:
 *  int _CRT_glob = 0;
 */
int	_CRT_glob = 0;

#endif	/* __MINGW32__ */


static const char authors[] = "Andy Bay, O. Sezer";

int main (int argc, char **argv)
{
	int	ret = 0, tmp, len;
	const char	*testname;

	printf("VisPatch %d.%d.%d by %s\n",
			 VP_VER_MAJ, VP_VER_MID, VP_VER_MIN,
			 authors);

	ValidateByteorder ();

	if (Sys_getcwd(Path,sizeof(Path)) != 0)
		Error ("Unable to determine current working directory");
	printf("Current directory: %s\n", Path);

	if (argc > 1)
	{
		char *cmdbuffer = TempFile;
		for (tmp = 1; tmp < argc; tmp++)
		{
			q_strlcpy(cmdbuffer, argv[tmp], MAX_OSPATH);
			q_strlwr(cmdbuffer);
			if (cmdbuffer[0] == '/')
			{
				cmdbuffer[0] = '-';
			}
			if (strcmp(cmdbuffer, "-?"    ) == 0 ||
			    strcmp(cmdbuffer, "-h"    ) == 0 ||
			    strcmp(cmdbuffer, "-help" ) == 0 ||
			    strcmp(cmdbuffer, "--help") == 0 )
			{
				usage();
				exit (0);
			}
			else if (strcmp(cmdbuffer,"-data") == 0)
			{
				argv[tmp][0] = 0;
				if (++tmp == argc)
					Error ("You must specify the filename of visdata after -data");
				q_strlcpy (VIS, argv[tmp], sizeof(VIS));
				argv[tmp][0] = 0;
			}
			else if (strcmp(cmdbuffer,"-dir") == 0)
			{
				argv[tmp][0] = 0;
				if (++tmp == argc)
					Error ("You must specify a directory name after -dir");
				q_strlcpy (Path, argv[tmp], sizeof(Path));
				len = (int)strlen(Path) - 1;
				if (Path[len] == '/' || Path[len] == '\\')
					Path[len] = 0;
				argv[tmp][0] = 0;
				printf("Will look into %s as the pak/bsp directory..\n", Path);
			}
			else if (strcmp(cmdbuffer,"-extract") == 0)
			{
				if (mode == 2)
					Error ("-extract and -new cannot be used together");
				mode = 1;
				argv[tmp][0] = 0;
			}
			else if (strcmp(cmdbuffer,"-new") == 0)
			{
				if (mode == 1)
					Error ("-extract and -new cannot be used together");
				mode = 2;
				argv[tmp][0] = 0;
			}
			else
			{
				q_strlcpy (File, argv[tmp], sizeof(File));
			}
		}
	}

	printf("VisPatch is in mode %i\n", mode);
	printf("Will use %s as the Vis-data source\n", VIS);
	printf("Will patch files matching %s/%s\n", Path, File);
	if (mode == 1)
	{
		printf("Will extract Vis data to %s, auto-append.\n", VIS);
	}
	if (mode == 2)
	{
		testname = Sys_FindFirstFile(Path, "pak*.pak");
		tmp = 0;
		while (testname != NULL)
		{
			tmp++;
			testname = Sys_FindNextFile();
		}
		Sys_FindClose();
		q_snprintf(FoutPak, sizeof(FoutPak), "%s/pak%i.pak", Path, tmp);
		printf("The new pak file will be called %s.\n", FoutPak);
	}

	q_snprintf(TempFile, sizeof(TempFile), "%s/%s", Path, TEMP_FILE_NAME);
//	printf("%s", TempFile);

	if (mode == 0 || mode == 2)	/* we are patching */
	{
		int chk = 0;

		fVIS = fopen(VIS, "rb");
		if (!fVIS)
			Error ("couldn't find the vis source file.");

		loadvis(fVIS);

		OutFile = fopen(TempFile, "w+b");
		if (!OutFile)
			Error ("couldn't create an output file");

		testname = Sys_FindFirstFile(Path, File);

		while (testname != NULL)
		{
			q_strlcpy (File, testname, sizeof(File));
			q_snprintf(Path2, sizeof(Path2), "%s/%s", Path, File);

			InFile = fopen(Path2, "rb");
			if (!InFile)
				Error ("couldn't find the level file.");

			if (mode == 0)
			{
			/* we may be working with multiple pak files,
			 * clean any garbage from the previous runs.
			 */
				memset(NewPakEnt, 0, MAX_FILES_IN_PACK * sizeof(pakentry_t));
			}

			chk = ChooseLevel(File, 0, 100000);

			if (chk < 0)
			{
				FWRITE_ERROR;
			}

			if (mode == 0)		/* patching with overwrite enabled */
			{
				NPcnt = 0;
				fclose(OutFile);
				fclose(InFile);
				InFile = NULL;

				if (chk > 0)
				{
					remove(Path2);
					rename(TempFile, Path2);
				}

				OutFile = fopen(TempFile, "w+b");
				if (!OutFile)
					Error ("couldn't create an output file");
			}
			else if (usepak == 1)	/* mode 2: writing into a new pakfile */
			{
				fclose(InFile);
				InFile = NULL;
			}
			else if (chk > 0)	/* mode 2: writing into new bsp files */
			{
			//	printf("%i\n", chk);
				fclose(OutFile);
				fclose(InFile);
				InFile = NULL;
				q_snprintf(Path2, sizeof(Path2), "%s/%s", Path, File);
				q_strlcpy (File, Path2, sizeof(File));
				q_strrev(File);
				File[0] = 'k';
				File[1] = 'a';
				File[2] = 'b';
				q_strrev(File);
				remove(File);
				q_snprintf(Path2, sizeof(Path2), "%s/%s", Path, CurName);
				rename(Path2, File);
				rename(TempFile, Path2);
				OutFile = fopen(TempFile, "w+b");
				if (!OutFile)
					Error ("couldn't create an output file");
			}
			else	/* mode 2: ??? */
			{
				fclose(OutFile);
				fclose(InFile);
				InFile = NULL;
				OutFile = fopen(TempFile, "w+b");
				if (!OutFile)
					Error ("couldn't create an output file");
			}

			testname = Sys_FindNextFile();
		}
		Sys_FindClose();
		CLOSE_ALL;
	//	printf("%s\n", FoutPak);
		if (mode == 2 && usepak == 1)
		{
			rename(TempFile, FoutPak);
		}
		freevis();

	}
	else if (mode == 1)	/* we are extracting */
	{
		if (Sys_filesize(VIS) == -1)
			fVIS = fopen(VIS, "wb");
		else
			fVIS = fopen(VIS, "r+b");

		if (!fVIS)
			Error ("couldn't open the vis data file.");

		testname = Sys_FindFirstFile(Path, File);

		while (testname != NULL)
		{
			q_strlcpy (File, testname, sizeof(File));
			q_snprintf(Path2, sizeof(Path2), "%s/%s", Path, File);

			InFile = fopen(Path2, "r+b");
			if (!InFile)
				Error ("couldn't find the level file.");

			ret = ChooseFile(File, 0, 0);

			if (ret < 0)
			{
				FWRITE_ERROR;
			}

			testname = Sys_FindNextFile();
		}
		Sys_FindClose();
	}

	remove (TempFile);

	return 0;
}


/*============================================================================*/

/* Functions used for patching process:
 * ChooseLevel, PakFix, BspFix, OthrFix
 */

static int ChooseLevel (char *FileSpec, int Offset, int length)
{
	int tmp = 0;

//	printf("Looking at file %s %i %i.\n", FileSpec, length, mode);
	if (strstr(q_strlwr(FileSpec),".pak"))
	{
		printf("Looking at file %s.\n", FileSpec);
		usepak = 1;
		tmp = PakFix(Offset);
	}
	else if (length > 50000 && strstr(q_strlwr(FileSpec),".bsp"))
	{
		printf("Looking at file %s.\n", FileSpec);
		q_strlcpy (CurName, FileSpec, sizeof(CurName));
		tmp = BSPFix(Offset);
	}
	else if (mode == 0 && Offset > 0)
	{
		tmp = OthrFix(Offset, length);
	}
	else if (mode == 2 && Offset > 0)
	{
		NPcnt--;
	}

//	if (tmp == 0)
//		printf("Did not process the file!\n");

	return tmp;
}

static int PakFix (int Offset)
{
	pakheader_t	Pak;
	pakentry_t	*PakEnt;
	int		numentry;
	int		ugh, pakwalk;
	int		ret = 0;
	size_t		test;

	test = fwrite(&Pak, 1, sizeof(pakheader_t), OutFile);
	if (test != sizeof(pakheader_t))
		return -1;

	fseek(InFile, Offset, SEEK_SET);
	fread(&Pak, 1, sizeof(pakheader_t), InFile);
	Pak.dirsize = LittleLong(Pak.dirsize);
	Pak.diroffset = LittleLong(Pak.diroffset);
	if (Pak.diroffset < 0 || Pak.dirsize < 0)
		Error ("pak file has invalid header; diroffset: %i, dirsize: %i", Pak.diroffset, Pak.dirsize);
	numentry = Pak.dirsize / sizeof(pakentry_t);
	PakEnt = (pakentry_t *)malloc((size_t)Pak.dirsize);
	fseek(InFile, Offset+Pak.diroffset, SEEK_SET);
	fread(PakEnt, 1, (size_t)Pak.dirsize, InFile);

	for (pakwalk = 0; pakwalk < numentry; pakwalk++)
	{
		PakEnt[pakwalk].size = LittleLong(PakEnt[pakwalk].size);
		PakEnt[pakwalk].offset = LittleLong(PakEnt[pakwalk].offset);
		q_strlcpy (NewPakEnt[NPcnt].filename, PakEnt[pakwalk].filename, sizeof(NewPakEnt[0].filename));
		q_strlcpy (CurName, PakEnt[pakwalk].filename, sizeof(CurName));

		NewPakEnt[NPcnt].size = LittleLong(PakEnt[pakwalk].size);
		ret = ChooseLevel(PakEnt[pakwalk].filename, Offset+PakEnt[pakwalk].offset, PakEnt[pakwalk].size);

		if (ret < 0)
		{
			free(PakEnt);
			return ret;
		}

		NPcnt++;
	}
	free(PakEnt);
	fflush(OutFile);
	Pak.diroffset = ftell(OutFile);
//	printf("PAK diroffset = %i, entries = %i\n", Pak.diroffset, NPcnt);
	Pak.dirsize = NPcnt * sizeof(pakentry_t);
	ugh = ftell(OutFile);
//	test = fwrite(&NewPakEnt[0], 1, (size_t)Pak.dirsize, OutFile);
	test = fwrite(NewPakEnt, 1, (size_t)Pak.dirsize, OutFile);

	if (test < (size_t)Pak.dirsize)
		return -1;

	fflush(OutFile);
//	chsize(fileno(OutFile), ftell(OutFile));
	fseek(OutFile, 0, SEEK_SET);
	Pak.dirsize = LittleLong(Pak.dirsize);
	Pak.diroffset = LittleLong(Pak.diroffset);
	test = fwrite(&Pak, 1, sizeof(pakheader_t), OutFile);
	if (test != sizeof(pakheader_t))
		return -1;

	fseek(OutFile, ugh, SEEK_SET);

	return numentry;
}

static int OthrFix (int Offset, int Length)
{
	int		test;
	void		*cpy;

	fseek(InFile, Offset, SEEK_SET);
	NewPakEnt[NPcnt].offset = LittleLong(ftell(OutFile));
	NewPakEnt[NPcnt].size = LittleLong(Length);
	cpy = malloc(Length);
	fread(cpy, 1, Length, InFile);
	test = (int) fwrite(cpy, 1, Length, OutFile);
	free(cpy);

	if (test != Length)
		return -1;
	return 1;
}

static int BSPFix (int InitOFFS)
{
	int		tmp, good;
	size_t		test, count;
	int		here;
	dheader_t	bspheader;
	unsigned char	*cpy;
	char	VisName[VISPATCH_IDLEN+6];
	char	pad[4] = { 0, 0, 0, 0 };

	fflush(OutFile);
	NewPakEnt[NPcnt].offset = LittleLong(ftell(OutFile));
	tmp = LittleLong(NewPakEnt[NPcnt].size);
	if (tmp == 0)
		NewPakEnt[NPcnt].size = LittleLong(Sys_filesize(File));

	fseek(InFile, InitOFFS, SEEK_SET);
	test = fread(&bspheader, 1, sizeof(dheader_t), InFile);
	if (test != sizeof(dheader_t))
		return 0;

	printf("BSP Version %d, Vis info at %d (%d bytes)\n",
		LittleLong(bspheader.version),
		LittleLong(bspheader.visilist.offset),
		LittleLong(bspheader.visilist.size));
	test = fwrite(&bspheader, 1, sizeof(dheader_t), OutFile);
	if (test != sizeof(dheader_t))
		return -1;

/* swap the header */
	for (count = 0 ; count < sizeof(dheader_t)/4 ; count++)
		((int *)&bspheader)[count] = LittleLong ( ((int *)&bspheader)[count]);

	q_strlcpy (VisName, CurName, sizeof(VisName));
	q_strrev (VisName);
	q_strlcat (VisName, "/", sizeof(VisName));
	count = strcspn(VisName, "\\/");
	memset(VisName+count, 0, sizeof(VisName)-count);
	q_strrev (VisName);
	good = 0;
	for (count = 0; count < numvis; count++)
	{
		if (!q_strcasecmp(visdat[count].File, VisName))
		{
			good = 1;
			printf("Name: %s Size: %d (# %lu)\n", VisName, visdat[count].vislen, (unsigned long)count);
			bspheader.visilist.size = visdat[count].vislen;
			bspheader.leaves.size	= visdat[count].leaflen;
			break;
		}
	}

	if (good == 0)
	{
		if (usepak == 1)
		{
			fseek(InFile, InitOFFS, SEEK_SET);
			fseek(OutFile, LittleLong(NewPakEnt[NPcnt].offset), SEEK_SET);
			if (mode == 0)
			{
				char *cc;
				tmp = LittleLong(NewPakEnt[NPcnt].size);
				if (tmp < 0)
					Error ("BSPFix: negative size!");
				cc = (char *)malloc(tmp);
				fread(cc, 1, tmp, InFile);
				test = fwrite(cc, 1, tmp, OutFile);
				free(cc);
				if (test != (size_t)tmp)
					return -1;
				return 1;
			}
			else
			{
				NPcnt--;
				return 0;
			}
		}
		/* else: Individual file and it doesn't matter. */
		return 0;
	}

/*	write the lumps not in the structrue order, but using the
	bspfile.c::WriteBSPFile() order of iD Software. align the
	offset at 4 bytes as in bspfile.c::AddLump()		*/

	cpy = (unsigned char *)malloc(bspheader.planes.size);
	fseek(InFile, InitOFFS+bspheader.planes.offset, SEEK_SET);
	fread(cpy, 1, bspheader.planes.size, InFile);
	bspheader.planes.offset = ftell(OutFile)-LittleLong(NewPakEnt[NPcnt].offset);
	test = fwrite(cpy, 1, bspheader.planes.size, OutFile);
	free(cpy);
	if (test != (size_t)bspheader.planes.size)
		return -1;
	tmp = (bspheader.planes.size + 3) & ~3;
	if (tmp > bspheader.planes.size)
	{
		tmp -= bspheader.planes.size;
		fwrite(pad, 1, tmp, OutFile);
	}

	bspheader.leaves.offset = ftell(OutFile)-LittleLong(NewPakEnt[NPcnt].offset);
	test = fwrite(visdat[count].leafdata, 1, bspheader.leaves.size, OutFile);
	if (test != (size_t)bspheader.leaves.size)
		return -1;
	tmp = (bspheader.leaves.size + 3) & ~3;
	if (tmp > bspheader.leaves.size)
	{
		tmp -= bspheader.leaves.size;
		fwrite(pad, 1, tmp, OutFile);
	}

	cpy = (unsigned char *)malloc(bspheader.vertices.size);
	fseek(InFile, InitOFFS+bspheader.vertices.offset, SEEK_SET);
	fread(cpy, 1, bspheader.vertices.size, InFile);
	bspheader.vertices.offset = ftell(OutFile)-LittleLong(NewPakEnt[NPcnt].offset);
	test = fwrite(cpy, 1, bspheader.vertices.size, OutFile);
	free(cpy);
	if (test != (size_t)bspheader.vertices.size)
		return -1;
	tmp = (bspheader.vertices.size + 3) & ~3;
	if (tmp > bspheader.vertices.size)
	{
		tmp -= bspheader.vertices.size;
		fwrite(pad, 1, tmp, OutFile);
	}

	cpy = (unsigned char *)malloc(bspheader.nodes.size);
	fseek(InFile, InitOFFS+bspheader.nodes.offset, SEEK_SET);
	fread(cpy, 1, bspheader.nodes.size, InFile);
	bspheader.nodes.offset = ftell(OutFile)-LittleLong(NewPakEnt[NPcnt].offset);
	test = fwrite(cpy, 1, bspheader.nodes.size, OutFile);
	free(cpy);
	if (test != (size_t)bspheader.nodes.size)
		return -1;
	tmp = (bspheader.nodes.size + 3) & ~3;
	if (tmp > bspheader.nodes.size)
	{
		tmp -= bspheader.nodes.size;
		fwrite(pad, 1, tmp, OutFile);
	}

	cpy = (unsigned char *)malloc(bspheader.texinfo.size);
	fseek(InFile, InitOFFS+bspheader.texinfo.offset, SEEK_SET);
	fread(cpy, 1, bspheader.texinfo.size, InFile);
	bspheader.texinfo.offset = ftell(OutFile)-LittleLong(NewPakEnt[NPcnt].offset);
	test = fwrite(cpy, 1, bspheader.texinfo.size, OutFile);
	free(cpy);
	if (test != (size_t)bspheader.texinfo.size)
		return -1;
	tmp = (bspheader.texinfo.size + 3) & ~3;
	if (tmp > bspheader.texinfo.size)
	{
		tmp -= bspheader.texinfo.size;
		fwrite(pad, 1, tmp, OutFile);
	}

	cpy = (unsigned char *)malloc(bspheader.faces.size);
	fseek(InFile, InitOFFS+bspheader.faces.offset, SEEK_SET);
	fread(cpy, 1, bspheader.faces.size, InFile);
	bspheader.faces.offset = ftell(OutFile)-LittleLong(NewPakEnt[NPcnt].offset);
	test = fwrite(cpy, 1, bspheader.faces.size, OutFile);
	free(cpy);
	if (test != (size_t)bspheader.faces.size)
		return -1;
	tmp = (bspheader.faces.size + 3) & ~3;
	if (tmp > bspheader.faces.size)
	{
		tmp -= bspheader.faces.size;
		fwrite(pad, 1, tmp, OutFile);
	}

	cpy = (unsigned char *)malloc(bspheader.clipnodes.size);
	fseek(InFile, InitOFFS+bspheader.clipnodes.offset, SEEK_SET);
	fread(cpy, 1, bspheader.clipnodes.size, InFile);
	bspheader.clipnodes.offset = ftell(OutFile)-LittleLong(NewPakEnt[NPcnt].offset);
	test = fwrite(cpy, 1, bspheader.clipnodes.size, OutFile);
	free(cpy);
	if (test != (size_t)bspheader.clipnodes.size)
		return -1;
	tmp = (bspheader.clipnodes.size + 3) & ~3;
	if (tmp > bspheader.clipnodes.size)
	{
		tmp -= bspheader.clipnodes.size;
		fwrite(pad, 1, tmp, OutFile);
	}

	cpy = (unsigned char *)malloc(bspheader.lface.size);
	fseek(InFile, InitOFFS+bspheader.lface.offset, SEEK_SET);
	fread(cpy, 1, bspheader.lface.size, InFile);
	bspheader.lface.offset = ftell(OutFile)-LittleLong(NewPakEnt[NPcnt].offset);
	test = fwrite(cpy, 1, bspheader.lface.size, OutFile);
	free(cpy);
	if (test != (size_t)bspheader.lface.size)
		return -1;
	tmp = (bspheader.lface.size + 3) & ~3;
	if (tmp > bspheader.lface.size)
	{
		tmp -= bspheader.lface.size;
		fwrite(pad, 1, tmp, OutFile);
	}

	cpy = (unsigned char *)malloc(bspheader.ledges.size);
	fseek(InFile, InitOFFS+bspheader.ledges.offset, SEEK_SET);
	fread(cpy, 1, bspheader.ledges.size, InFile);
	bspheader.ledges.offset = ftell(OutFile)-LittleLong(NewPakEnt[NPcnt].offset);
	test = fwrite(cpy, 1, bspheader.ledges.size, OutFile);
	free(cpy);
	if (test != (size_t)bspheader.ledges.size)
		return -1;
	tmp = (bspheader.ledges.size + 3) & ~3;
	if (tmp > bspheader.ledges.size)
	{
		tmp -= bspheader.ledges.size;
		fwrite(pad, 1, tmp, OutFile);
	}

	cpy = (unsigned char *)malloc(bspheader.edges.size);
	fseek(InFile, InitOFFS+bspheader.edges.offset, SEEK_SET);
	fread(cpy, 1, bspheader.edges.size, InFile);
	bspheader.edges.offset = ftell(OutFile)-LittleLong(NewPakEnt[NPcnt].offset);
	test = fwrite(cpy, 1, bspheader.edges.size, OutFile);
	free(cpy);
	if (test != (size_t)bspheader.edges.size)
		return -1;
	tmp = (bspheader.edges.size + 3) & ~3;
	if (tmp > bspheader.edges.size)
	{
		tmp -= bspheader.edges.size;
		fwrite(pad, 1, tmp, OutFile);
	}

	cpy = (unsigned char *)malloc(bspheader.models.size);
	fseek(InFile, InitOFFS+bspheader.models.offset, SEEK_SET);
	fread(cpy, 1, bspheader.models.size, InFile);
	bspheader.models.offset = ftell(OutFile)-LittleLong(NewPakEnt[NPcnt].offset);
	test = fwrite(cpy, 1, bspheader.models.size, OutFile);
	free(cpy);
	if (test != (size_t)bspheader.models.size)
		return -1;
	tmp = (bspheader.models.size + 3) & ~3;
	if (tmp > bspheader.models.size)
	{
		tmp -= bspheader.models.size;
		fwrite(pad, 1, tmp, OutFile);
	}

	cpy = (unsigned char *)malloc(bspheader.lightmaps.size);
	fseek(InFile, InitOFFS+bspheader.lightmaps.offset, SEEK_SET);
	fread(cpy, 1, bspheader.lightmaps.size, InFile);
	bspheader.lightmaps.offset = ftell(OutFile)-LittleLong(NewPakEnt[NPcnt].offset);
	test = fwrite(cpy, 1, bspheader.lightmaps.size, OutFile);
	free(cpy);
	if (test != (size_t)bspheader.lightmaps.size)
		return -1;
	tmp = (bspheader.lightmaps.size + 3) & ~3;
	if (tmp > bspheader.lightmaps.size)
	{
		tmp -= bspheader.lightmaps.size;
		fwrite(pad, 1, tmp, OutFile);
	}

	bspheader.visilist.offset = ftell(OutFile) - LittleLong(NewPakEnt[NPcnt].offset);
	test = fwrite(visdat[count].visdata, 1, bspheader.visilist.size, OutFile);
	if (test != (size_t)bspheader.visilist.size)
		return -1;
	tmp = (bspheader.visilist.size + 3) & ~3;
	if (tmp > bspheader.visilist.size)
	{
		tmp -= bspheader.visilist.size;
		fwrite(pad, 1, tmp, OutFile);
	}

	cpy = (unsigned char *)malloc(bspheader.entities.size);
	fseek(InFile, InitOFFS+bspheader.entities.offset, SEEK_SET);
	fread(cpy, 1, bspheader.entities.size, InFile);
	bspheader.entities.offset = ftell(OutFile)-LittleLong(NewPakEnt[NPcnt].offset);
	test = fwrite(cpy, 1, bspheader.entities.size, OutFile);
	free(cpy);
	if (test != (size_t)bspheader.entities.size)
		return -1;
	tmp = (bspheader.entities.size + 3) & ~3;
	if (tmp > bspheader.entities.size)
	{
		tmp -= bspheader.entities.size;
		fwrite(pad, 1, tmp, OutFile);
	}

	cpy = (unsigned char *)malloc(bspheader.miptex.size);
	fseek(InFile, InitOFFS+bspheader.miptex.offset, SEEK_SET);
	fread(cpy, 1, bspheader.miptex.size, InFile);
	bspheader.miptex.offset = ftell(OutFile)-LittleLong(NewPakEnt[NPcnt].offset);
	test = fwrite(cpy, 1, bspheader.miptex.size, OutFile);
	free(cpy);
	if (test != (size_t)bspheader.miptex.size)
		return -1;
	tmp = (bspheader.miptex.size + 3) & ~3;
	if (tmp > bspheader.miptex.size)
	{
		tmp -= bspheader.miptex.size;
		fwrite(pad, 1, tmp, OutFile);
	}

	here = ftell(OutFile);
	fflush(OutFile);

/* swap the header */
	for (count = 0 ; count < sizeof(dheader_t)/4 ; count++)
		((int *)&bspheader)[count] = LittleLong ( ((int *)&bspheader)[count]);

	fseek(OutFile, LittleLong(NewPakEnt[NPcnt].offset), SEEK_SET);
	test = fwrite(&bspheader, 1, sizeof(dheader_t), OutFile);
	if (test != sizeof(dheader_t))
		return -1;

	fseek(OutFile, here, SEEK_SET);
	NewPakEnt[NPcnt].size = LittleLong(ftell(OutFile) - LittleLong(NewPakEnt[NPcnt].offset));

	return 1;
}


/*============================================================================*/

/* Functions used for extraction process:
 * ChooseFile, PakNew, BspNew
 */

static int ChooseFile (char *FileSpec, int Offset, int length)
{
	int tmp = 0;

	if (length == 0 && strstr(q_strlwr(FileSpec),".pak"))
	{
		printf("Looking at file %s.\n", FileSpec);
		tmp = PakNew(Offset);
	}

	if (strstr(q_strlwr(FileSpec),".bsp"))
	{
		printf("Looking at file %s.\n", FileSpec);
		q_strlcpy (CurName, FileSpec, sizeof(CurName));
		tmp = BSPNew(Offset);
	}

	return tmp;
}

static int PakNew (int Offset)
{
	pakheader_t	Pak;
	pakentry_t	*PakEnt;
	int		ret = 0;
	int		pakwalk;
	int		numentry;

	fseek(InFile, Offset, SEEK_SET);
	fread(&Pak, 1, sizeof(pakheader_t), InFile);
	Pak.dirsize = LittleLong(Pak.dirsize);
	Pak.diroffset = LittleLong(Pak.diroffset);
	numentry = Pak.dirsize / sizeof(pakentry_t);
	PakEnt = (pakentry_t *)malloc((size_t)Pak.dirsize);
	fseek(InFile, Offset+Pak.diroffset, SEEK_SET);
	fread(PakEnt, 1, (size_t)Pak.dirsize, InFile);

	for (pakwalk = 0; pakwalk < numentry; pakwalk++)
	{
		PakEnt[pakwalk].size = LittleLong(PakEnt[pakwalk].size);
		PakEnt[pakwalk].offset = LittleLong(PakEnt[pakwalk].offset);

		q_strlcpy (NewPakEnt[NPcnt].filename, PakEnt[pakwalk].filename, sizeof(NewPakEnt[0].filename));
		q_strlcpy (CurName, PakEnt[pakwalk].filename, sizeof(CurName));

		NewPakEnt[NPcnt].size = LittleLong(PakEnt[pakwalk].size);
		ret = ChooseFile(PakEnt[pakwalk].filename, Offset+PakEnt[pakwalk].offset, PakEnt[pakwalk].size);
		if (ret < 0)
		{
			free(PakEnt);
			FWRITE_ERROR;
		}
		NPcnt++;
	}
	free(PakEnt);

	return numentry;
}

static int BSPNew (int InitOFFS)
{
	size_t		count, test;
	int	tes, len;
	dheader_t	bspheader;
	unsigned char	*cpy;
	char	VisName[VISPATCH_IDLEN+6];

	fseek(InFile, InitOFFS, SEEK_SET);
	test = fread(&bspheader, 1, sizeof(dheader_t), InFile);
	if (test != sizeof(dheader_t))
		return 0;

/* swap the header */
	for (count = 0 ; count < sizeof(dheader_t)/4 ; count++)
		((int *)&bspheader)[count] = LittleLong ( ((int *)&bspheader)[count]);

	printf("Version of bsp file is:  %d\n", bspheader.version);
	printf("Vis info is at %d and is %d long\n", bspheader.visilist.offset, bspheader.visilist.size);
	printf("Leaf info is at %d and is %d long\n", bspheader.leaves.offset, bspheader.leaves.size);

/* Map with no vis data: no need to do this. */
	if (bspheader.visilist.size == 0)
	{
		printf("Vis info size = 0.  Skipping...\n");
		return 1;
	}

	q_strlcpy (VisName, CurName, sizeof(VisName));
	q_strrev (VisName);
	q_strlcat (VisName, "/", sizeof(VisName));
	count = strcspn(VisName, "/\\");
	memset(VisName+count, 0, sizeof(VisName)-count);
	q_strrev (VisName);

	cpy = (unsigned char *)malloc(bspheader.visilist.size);
	fseek(InFile, InitOFFS+bspheader.visilist.offset, SEEK_SET);
	fread(cpy, 1, bspheader.visilist.size, InFile);
	len = Sys_filesize(VIS);
	if (len > -1)
		fseek(fVIS, 0, SEEK_END);

	test = fwrite(VisName, 1, VISPATCH_IDLEN, fVIS);
	if (test != VISPATCH_IDLEN)
		return -1;

	tes = bspheader.visilist.size + bspheader.leaves.size + 8;
	tes = LittleLong(tes);
	test = fwrite(&tes, 1, sizeof(int), fVIS);
	if (test != sizeof(int))
		return -1;

	tes = LittleLong(bspheader.visilist.size);
	test = fwrite(&tes, 1, sizeof(int), fVIS);
	if (test != sizeof(int))
		return -1;

	test = fwrite(cpy, 1, bspheader.visilist.size, fVIS);
	free(cpy);
	if (test != (size_t)bspheader.visilist.size)
		return -1;

	cpy = (unsigned char *)malloc(bspheader.leaves.size);
	fseek(InFile, InitOFFS+bspheader.leaves.offset, SEEK_SET);
	fread(cpy, 1, bspheader.leaves.size, InFile);
	tes = LittleLong(bspheader.leaves.size);
	test = fwrite(&tes, 1, sizeof(int), fVIS);
	if (test != sizeof(int))
	{
		free(cpy);
		return -1;
	}

	test = fwrite(cpy, 1, bspheader.leaves.size, fVIS);
	free(cpy);
	if (test != (size_t)bspheader.leaves.size)
		return -1;

	return 1;
}


/*============================================================================*/

/* Functions used for vis data loading: */

static void loadvis(FILE *fp)
{
	unsigned int	count = 0, tmp;
	char		Name[VISPATCH_IDLEN];
	int		go, len;

	fseek(fp, 0, SEEK_END);
	len = ftell(fp);
	fseek(fp, 0, SEEK_SET);

	while (ftell(fp) < len)
	{
		count++;
		fread(Name, 1, VISPATCH_IDLEN, fp);
		fread(&go, 1, sizeof(int), fp);
		go = LittleLong(go);
		fseek(fp, go, SEEK_CUR);
	}

	visdat = (visdat_t *)malloc(sizeof(visdat_t)*count);
	if (visdat == NULL)
		Error ("Not enough memory!");

	fseek(fp, 0, SEEK_SET);

	for (tmp = 0; tmp < count; tmp++)
	{
		fread(visdat[tmp].File, 1, VISPATCH_IDLEN, fp);
		fread(&visdat[tmp].len, 1, sizeof(int), fp);
		fread(&visdat[tmp].vislen, 1, sizeof(int), fp);
		visdat[tmp].len = LittleLong(visdat[tmp].len);
		visdat[tmp].vislen = LittleLong(visdat[tmp].vislen);
		numvis = tmp;
	//	printf("%i\n", visdat[tmp].vislen);
		visdat[tmp].visdata = (unsigned char *)malloc(visdat[tmp].vislen);
		if (visdat[tmp].visdata == NULL)
		{
			freevis();
			Error ("Not enough memory!");
		}
		fread(visdat[tmp].visdata, 1, visdat[tmp].vislen, fp);

		fread(&visdat[tmp].leaflen, 1, sizeof(int), fp);
		visdat[tmp].leaflen = LittleLong(visdat[tmp].leaflen);
		visdat[tmp].leafdata = (unsigned char *)malloc(visdat[tmp].leaflen);
		if (visdat[tmp].leafdata == NULL)
		{
			free(visdat[tmp].visdata);
			freevis();
			Error ("Not enough memory!");
		}
		fread(visdat[tmp].leafdata, 1, visdat[tmp].leaflen, fp);
	}

	numvis = count;
}

static void freevis (void)
{
	unsigned int		tmp;

	for (tmp = 0; tmp < numvis; tmp++)
	{
		free(visdat[tmp].visdata);
		free(visdat[tmp].leafdata);
	}
	free(visdat);
}

