/***
****  QuBE --- .PAK file editing routines.
****
****  Note that these are based partially on that posting on the Net.
****  Would the author of that please contact me so I can give him some
****  credit?
***/

#include "qube.h"
#include "pak.h"

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>

/* A single filename entry in a .PAK file */

typedef struct {
	unsigned char filename[56];
	long pos, len;
} pakentry;

/* The .PAK file entries.  512 of them.  This should be dynamically allocated
   in a linked list or something.  Any volunteers? */

static pakentry entries[512];
static long entrycount = 0;

static void extract(char *filename);
static void makepath(unsigned char *path);

/*
**  PakXtract.	Extract *all* files from a .PAK file.
*/

void PakXtract(void)
{
	extract("*");
}

/*
**  PakXtract2.  Extract file(s) from a .PAK file.
*/

void PakXtract2(int argnum, char **argv)
{
	extract(argv[argnum+1]);
}

static void extract(char *filename)
{
	FILE *fo;
	long table, tablelen, dummy;
	long i, j, k;
	unsigned char *temp;
	int readsize;

	/* Make sure it's a .PAK file --- they start with "PACK" */

        if (header.id != 0x4B434150)
		Error("Not a valid .PAK file");

	/* Go back to the beginning, since we already read a header */

	fseek(fi, 0, SEEK_SET);
	fread(&dummy, sizeof(long), 1, fi);
	fread(&table, sizeof(long), 1, fi);
	fread(&tablelen, sizeof(long), 1, fi);

        fseek(fi, table, SEEK_SET);

	/* Load in the filename table.	Like I said, this ought to be a
	   linked list or something. */

	for (i = 0; i < tablelen / 64; i++)
		fread(entries+i, sizeof(pakentry), 1, fi);

	/* Temporary small buffer for copying files.  We can't just allocate
	   all the space because DOS has these memory limitation thingys. */

	temp = Qmalloc(32768);

	/* Do it. */

	for (i = 0; i < tablelen / 64; i++) {

		/* Match it. */
		if (MatchName(filename, entries[i].filename)) {

			/* Print it. */

			if (verbose)
				printf("Extracting %s, address %08lX (size %ld)\n",
					entries[i].filename, entries[i].pos, entries[i].len);

			/* Locate it. */

			makepath(entries[i].filename);
			if ((fo = fopen(entries[i].filename, "wb")) == NULL)
				Error("Unable to get permission to write files.");
			fseek(fi, entries[i].pos, SEEK_SET);

			/* Write it. */

			for (j = 0L; j < entries[i].len; j += 32768L) {
				if (entries[i].len - j >= 32768) k = 32768;
				else k = entries[i].len - j;
				readsize = fread(temp, sizeof(char), k, fi);
				fwrite(temp, sizeof(char), readsize, fo);
			}

			fclose(fo);
		}
        }
	Qfree(temp);
}

/*
**  PakList.  List all files in a .PAK file.  Based on the above subroutine;
**	see it for comments on how it works.
*/

void PakList(void)
{
	FILE *fo;
	long pos, len, dummy;
	long i, j, k;
	unsigned char *temp;
	int readsize;

        if (header.id != 0x4B434150)
		Error("Not a valid .PAK file");

	fseek(fi, 0, SEEK_SET);
	fread(&dummy, sizeof(long), 1, fi);
	fread(&pos, sizeof(long), 1, fi);
	fread(&len, sizeof(long), 1, fi);
	fseek(fi, pos, SEEK_SET);

	for (i = 0; i < len / 64; i++)
		fread(entries+i, sizeof(pakentry), 1, fi);

	if (verbose)
		printf("%-8s %-8s %-60s\n", "Address", "Size", "FileName");

	for (i = 0; i < len / 64; i++) {
		if (verbose)
			printf("%08lX %-8ld %s\n", entries[i].pos, entries[i].len, entries[i].filename);
		else printf("%s\n", entries[i].filename);
        }
}

/*
**  PakDelete.	Zorch file(s) from a .PAK file.
*/

void PakDelete(int argnum, char **argv)
{
	FILE *fo;
	long pos, len, dummy;
	long newpos, newlen;
        long i, j, k;
	unsigned char *temp;
	int readsize;

        if (header.id != 0x4B434150)
		Error("Not a valid .PAK file");

	fseek(fi, 0, SEEK_SET);
	fread(&dummy, sizeof(long), 1, fi);
	fread(&pos, sizeof(long), 1, fi);
	fread(&len, sizeof(long), 1, fi);
	fseek(fi, pos, SEEK_SET);

	for (i = 0; i < len / 64; i++)
		fread(entries+i, sizeof(pakentry), 1, fi);

	if ((fo = fopen("$$$_temp.pak", "wb")) == NULL)
		Error("Unable to get permission to write files.");

        k = fwrite(&dummy, 1, sizeof(long), fo);
	k += fwrite(&pos, 1, sizeof(long), fo);
	k += fwrite(&len, 1, sizeof(long), fo);

	if (k < 12) {
		fclose(fo);
		unlink("$$$_temp.pak");
		Error("Insufficient disk space");
	}

	newpos = 12L;

	temp = Qmalloc(32768);

        for (i = 0; i < len / 64; i++) {
		if (MatchName(argv[argnum+1], entries[i].filename)) {
			if (verbose)
				printf("Deleting %s\n", entries[i].filename, entries[i].pos, entries[i].len);
		}
                else {
			fseek(fi, entries[i].pos, SEEK_SET);
			if (verbose)
				printf("Keeping %s\n", entries[i].filename, entries[i].pos, entries[i].len);
                        entries[i].pos = newpos;
                        for (j = 0L; j < entries[i].len; j += 32768L) {
				if (entries[i].len - j >= 32768) k = 32768;
				else k = entries[i].len - j;
				readsize = fread(temp, sizeof(char), k, fi);
				newpos += (k = fwrite(temp, sizeof(char), readsize, fo));
				if (k < readsize) {
					fclose(fo);
					unlink("$$$_temp.pak");
                                        Error("Insufficient disk space");
                                }
                        }
                }
        }

	Qfree(temp);

        newlen = 0L;

	for (i = 0; i < len / 64; i++) {
		if (!MatchName(argv[argnum+1], entries[i].filename)) {
			k = fwrite(entries+i, 1, sizeof(pakentry), fo);
			if (k < sizeof(pakentry)) {
				fclose(fo);
				unlink("$$$_temp.pak");
				Error("Insufficient disk space");
			}
                        newlen++;
                }
	}

        newlen *= 64;

	fseek(fo, 0, SEEK_SET);
	fwrite(&dummy, 1, sizeof(long), fo);
	fwrite(&newpos, 1, sizeof(long), fo);
	fwrite(&newlen, 1, sizeof(long), fo);

	fclose(fo);
	fclose(fi);

	unlink(argv[filenamearg]);
	rename("$$$_temp.pak", argv[filenamearg]);
}

/*
**  PakAdd.  Zorch file(s) from a .PAK file.
*/

void PakAdd(int argnum, char **argv)
{
	FILE *fo;
	long pos, len, dummy;
	long newpos, newlen;
        long i, j, k;
	unsigned char *temp;
	int readsize;

	if (justcreated) {
		pos = 12;
		len = 0;
		header.id = dummy = 0x4B434150;
		fwrite(&dummy, sizeof(long), 1, fi);
		fwrite(&pos, sizeof(long), 1, fi);
		fwrite(&len, sizeof(long), 1, fi);
        }

        if (header.id != 0x4B434150)
		Error("Not a valid .PAK file");

	if ((fi = freopen(argv[filenamearg], "r+b", fi)) == NULL)
		Error("Unable to get permission to update files.");

        fseek(fi, 0, SEEK_SET);
	fread(&dummy, sizeof(long), 1, fi);
	fread(&pos, sizeof(long), 1, fi);
	fread(&len, sizeof(long), 1, fi);
	fseek(fi, pos, SEEK_SET);

	for (i = 0; i < len / 64; i++)
		fread(entries+i, sizeof(pakentry), 1, fi);

	fseek(fi, pos, SEEK_SET);

	newlen = 0L;

	if ((fo = fopen(argv[argnum+1], "rb")) == NULL)
		Error(strerror(errno));

	temp = Qmalloc(32768);

        while (!feof(fo)) {
                readsize = fread(temp, sizeof(char), 32768, fo);
		newlen += (k = fwrite(temp, sizeof(char), readsize, fi));
		if (k < readsize) {
			fclose(fo);
			Error("Insufficient disk space");
		}
	}
        fclose(fo);

	Qfree(temp);

	entries[len/64].pos = pos;
	entries[len/64].len = newlen;
        strcpy(entries[len/64].filename, argv[argnum+1]);
	len += 64;
        pos += newlen;

        fseek(fi, 0, SEEK_SET);
	fwrite(&dummy, sizeof(long), 1, fi);
	fwrite(&pos, sizeof(long), 1, fi);
	fwrite(&len, sizeof(long), 1, fi);
	fseek(fi, pos, SEEK_SET);

	for (i = 0; i < len / 64; i++)
		fwrite(entries+i, sizeof(pakentry), 1, fi);
}

/*
**  makepath.  Create a pathname based on a path.
*/

static void makepath(unsigned char *path)
{
	unsigned char *temp2;
	unsigned char temp[64];

	strncpy(temp, path, 64);
	temp[63] = '\0';

	temp2 = temp;

	while (*temp2) {
		if(*temp2 == '/') {
			*temp2 = '\0';
			mkdir(temp);
			*temp2 = '/';
		}
		temp2++;
	}
}

