/***
****  QuBE Version 0.2a
****
****  Cheap Quake Binary Editor
****
****  Copyright 1996 by Sean Werkema
****
****  Main startup file, command line parsing, etc. --- get the ball rolling.
****
****  Compilation notes...  QUBE_MSDOS and QUBE_UNIX have special meaning
****  in this thing.  Each indicates the appropriate operating system.	More
****  can be added as necessary later.
****
****  Another nice addition would be QUBE_BIG and QUBE_LITTLE for specifying
****  little-endian vs. big-endian...  Anyone willing to code this mess?
****
****  I make no guarantees on the quality of this code.  You can freely use
****  it, modify it, tweak it, whatever you want, just as long as you obey
****  one basic command:  Whatever you create, don't call it QuBE.  QuBE is
****  my name for this morass of code, and it stays mine.  Other than that
****  you can do anything you damn well please.
****
****  This code is copyrighted only so that nobody else will copyright it;
****  I wrote it, and I don't want to be restricted from my own code.
****
****  Contributors:
****	Sean Werkema			Started QuBE, wrote 99% of it so far.
****
****  I am deeply indebted to whoever it was that originally posted the .PAK
****  extracter on the Net, because that gave me enough clues to start this.
****  The .PAK extraction routines are based roughly on the original .PAK
****  posting, with some new error checking and reformatting.  Whoever wrote
****  the .PAK thing, would you please contact me so I can throw in your name
****  as a contributor?  Thanks.
****
****  Enough comments.	Let's do this thing.
***/

#include "qube.h"
#include <errno.h>
#include <stdarg.h>

#include "image.h"
#include "gfx.h"
#include "entities.h"
#include "tree.h"
#include "pak.h"
#include "vertex.h"
#include "edge.h"

/* Automatically casted name of an Action function */

#define A(n) ((void (*)(int argnum, char **argv))(n))

/* The input file in all its glory */

struct headertag header;
FILE *fi;
int filenamearg;
int justcreated;
int verbose;

/* This is an action.  It's what to do with a command-line switch */

typedef struct {
	char *option;
	long int count;
	void (*action)(int argnum, char **argv);
        char *cmdline;
	char *name;
	char *info;
} Action;

typedef struct {
	Action *act;
	int arg;
} CommandArgument;

/* Everything that can be should be kept static, especially private
   functions and data, like the switch-list and its manipulators.  This
   isn't OOP code, but we can try to make it close.  */

static void ShowFormat(void);
static int switchcmp(char *option, char *string);
static void Verbosity(void);

static CommandArgument CommandArg[256];
static int CommandCount;

/* Command line args go here.  This is where everything starts.  */

static Action ActionList[] = {

{ "-b",  0,     A(TreeList),    "[-b]",                 "BSP",          "Display the BSP tree in a .BSP file"                           },
{ "-dl", 0,     A(EdgeList),    "[-dl]",                "EdgeList",     "Display the list of edges"                                     },
{ "-el", 0,     A(EntList),     "[-el]",                "EntList",      "Display entities in a .BSP file as a printed list"             },
{ "-er", 1,     A(EntReplace),  "[-er filename]",       "EntReplace",   "Replace *all* entities in a .BSP file from a text file"        },
{ "-ex", 1,     A(EntXtract),   "[-ex filename]",       "EntXtract",    "Extract entities in a .BSP file to a text file"                },
{ "-f",  0,     A(ShowHeader),  "[-f]",                 "FileHeader",   "Display BSP file header information"                           },
{ "-g",  0,     A(DoGraphics),  "[-g]",                 "Graph",        "Display the level in a .BSP file (SVGA/X-Win only)"            },
{ "-h",  0,     A(ShowFormat),  "[-h]",                 "Help",         "You're looking at it, buddy"                                   },
{ "-ka", 1,     A(PakAdd),      "[-ka filename]",       "PakAdd",       "Create/add/replace file(s) into a .PAK file"                   },
{ "-kd", 1,     A(PakDelete),   "[-kd filename]",       "PakDelete",    "Delete file(s) from a .PAK file"                               },
{ "-kl", 0,     A(PakList),     "[-kl]",                "PakList",      "List all the files stored in a .PAK file"                      },
{ "-kx", 1,     A(PakXtract2),  "[-kx filename]",       "PakXtract",    "Extract one or more files from a .PAK file"                    },
{ "-kX", 0,     A(PakXtract),   "[-kX]",                "PakXtract",    "Extract a .PAK file into all its constituent files"            },
{ "-pa", 1,     A(NULL),        "[-pa filename]",       "PicAdd",       "Add a picture to a .BSP file from a .BMP file (not yet)"       },
{ "-pd", 1,     A(NULL),        "[-pd filename]",       "PicDelete",    "Delete a picture from a .BSP file (not yet)"                   },
{ "-pl", 0,     A(PicList),     "[-pl]",                "PicList",      "List all the pictures stored in a .BSP file"                   },
{ "-px", 2,     A(PicXtract),   "[-px filename size]",  "PicXtract",    "Extract a picture from a .BSP file into a .BMP file"           },
{ "-pX", 0,     A(PicXtract2),  "[-pX]",                "PicXtract",    "Extract all pictures from a .BSP file"                         },
{ "-v",  0,     A(Verbosity),   "[-v]",                 "Verbose",      "Describe everything while doing it (good for debugging)"       },
{ "-xl", 0,     A(VertexList),  "[-xl]",                "VertexList",   "Display the list of vertices"                                  },
{ "-!",  1,     A(XtractAll),   "[-! filename]",        "XtractAll",    "Extract *ALL* data in a .BSP file to separate files"           },
{ NULL,  0,     A(NULL),        NULL,                   NULL,           NULL                                                            },

};

/*
**  Main.
*/

int main(int argc, char **argv)
{
	int i, j;
	int filenum = 0;
	int createok = 0;

	verbose = 0;
        justcreated = 0;
	fi = NULL;

	fprintf(stderr, "QuBE: Version 0.2a - 3/8/1996 - Freeware - Sean Werkema\n");

	/* Start parsing in the command line, and open a file if one exists */

        for (i = 1, CommandCount = 0; i < argc; i++) {
                for (j = 0; ActionList[j].option != NULL; j++) {
                        if (switchcmp(ActionList[j].option, argv[i])) {
				CommandArg[CommandCount].arg = i;
				CommandArg[CommandCount++].act = ActionList + j;
				if (ActionList[j].action == A(ShowFormat)) ShowFormat();
				if (ActionList[j].action == A(PakAdd)) createok = 1;
                                i += ActionList[j].count;
                                break;
                        }
                }
		if (ActionList[j].option == NULL) {
			if (filenum != 0)
				Error("Hey, that's too much stuff to do - try \"qube -h\".");
			filenum = i;
                }
        }

	if (filenum) {
		if ((fi = fopen(argv[filenum], "rb")) == NULL) {
			if (!createok) Error(strerror(errno));
			fi = fopen(argv[filenum], "wb");
			fclose(fi);
			fi = fopen(argv[filenum], "r+b");
                        justcreated = 1;
                }
		else fread(&header, 4, sizeof(header), fi);
		setvbuf(fi, NULL, _IOFBF, 32768);
                filenamearg = filenum;
	}
	else Error("Sorry, Mac, I don't know what to do - try \"qube -h\".");

	/* Start acting on the stuff that was found on the command line */

	for (i = 0; i < CommandCount; i++) {
		if (CommandArg[i].act->action != NULL) {
			void (*action)(int argnum, char **argv) = CommandArg[i].act->action;

                        (*action)(CommandArg[i].arg, argv);
		}
                else Error("Sorry, but this feature hasn't been written yet.");
        }

	if (fi != NULL) fclose(fi);

	return(1);
}

/*
**  Verbosity.	Turn on the verbose flag.
*/

static void Verbosity(void)
{
	verbose = 1;
}

/*
**  Error.  Display an error and shut down.  Should use vsprintf to make
**	everything nice, but that's a bit of effort for now.
*/

void Error(char *format, ...)
{
	char temp[1024];
	va_list arg_ptr;
	va_start(arg_ptr, format);
	vsprintf(temp, format, arg_ptr);

        if (fi != NULL) fclose(fi);

	fprintf(stderr, "QuBE: %s\n", temp);

#ifdef QUBE_UNIX
	/* One extra CR is needed to make things look right in UNIX */
        fprintf(stderr, "\n");  
#endif

	exit(0);
}

/*
**  ShowFormat.  Extacts the format from the ActionList, displays it, and exits.
*/

static void ShowFormat(void)
{
	int line = 0, column = 0;
	int i;
	int lc = 2;

	fprintf(stderr, "Display and change things in Quake BSP/PAK files.\n");
	lc++;

	/* Display the top command line */

	for (i = 0; ActionList[i].option != NULL; i++) {
		if (column + strlen(ActionList[i].cmdline) > 78) column = 0;
                if (column == 0) {
			if (line++ == 0) fprintf(stderr, "\nqube");
			else		 fprintf(stderr, "\n    ");
			column = 4;
			lc++;
                }
		fprintf(stderr, " %s", ActionList[i].cmdline);
		column += strlen(ActionList[i].cmdline) + 1;
        }

	/* Remind them they need to add a filename */

	if (column == 0 || column > 70) {
		if (line++ == 0) fprintf(stderr, "\nQuBE");
		else		 fprintf(stderr, "\n    ");
		lc++;
        }
	fprintf(stderr, " filename\n\n");

	/* Display the command line options, long form */

	for (i = 0; ActionList[i].option != NULL; i++) {
		if (strlen(ActionList[i].name) != 0) {
			fprintf(stderr, "  %-6s%-15s%s\n", ActionList[i].option, ActionList[i].name, ActionList[i].info);
			lc++;
			if (lc == 23) {
				fprintf(stderr, "<More>");
#ifdef QUBE_MSDOS
				ReadKeyScan();
				fprintf(stderr, "\r");
#endif
#ifdef QUBE_UNIX
				scanf("%c", &line);
#endif
                                lc = 1;
                        }
                }
	}

#ifdef QUBE_UNIX
	/* Again, one extra CR is needed to make things look right in UNIX */
        fprintf(stderr, "\n");  
#endif

	exit(0);
}

/*
**  switchcmp.	Behaves kind of like strncmp, only faster and friendlier.
*/

static int switchcmp(char *option, char *string)
{
	if (*option == '\0') return(0);

	while (*option && *option == *string) {
		option++;
		string++;
	}

	if (*option == '\0') return(1);
	else return(0);
}

/*
**  MatchName.	Wildcard matcher.  Now handles the wildcards fully, in
**	pathname format (with proper / interpretation).
**
**	Note that this is rather loosely derived from the fnmatch()
**	function from the GNU C library, which means that at least this
**	part of the code can have no restrictions on distribution.
**
**	However, since the rest of the code can't have any restrictions
**	either (by my choice), I guess that's a moot point.
*/

int MatchName(char *expr, char *string)
{
	char c;

	while ((c = *expr++) != '\0') {
		switch (c) {
		case '?':
			if (*string == '\0') return(0);
                        break;
		case '*':
			while ((c = *expr++) == '?' || c == '*') {
				if (c == '?' && *string == '\0') return(0);
				string++;
			}
                        if (c == '\0') return(1);
			expr--;
			while (*string != '\0') {
				if ((*string == c) && MatchName(expr, string)) return(1);
                                string++;
			}
                        return(0);
		default:
			if (c != *string) return(0);
			break;
		}
		string++;
        }

	if (*string == '\0') return(1);
	else return(0);
}

void *Qmalloc(long int size)
{
	void *temp = malloc(size);

	if (temp == NULL) {
		fprintf(stderr, "Unable to allocate enough memory.  %ld bytes not available.\n", size);
                exit(0);
        }

	return(temp);
}

void *Qrealloc(void *buffer, long int size)
{
	void *temp = realloc(buffer, size);

	if (temp == NULL) {
		fprintf(stderr, "Unable to allocate enough memory.  %ld bytes not available.\n", size);
		exit(0);
        }

        return(temp);
}

void Qfree(void *buffer)
{
	free(buffer);
}
