/*
 * PSP Software Development Kit - https://github.com/pspdev
 * -----------------------------------------------------------------------
 * Licensed under the BSD license, see LICENSE in PSPSDK root for details.
 *
 *  psp-build-exports.c - Simple program to build an export file.
 *
 * Copyright (c) 2005 James Forshaw <tyranid@gmail.com>
 *
 */

#include <stdio.h>
#include <getopt.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <ctype.h>

#include "sha1.h"

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#define MAX_LIB_NAME 27
#define MAX_LIB_FUNCS 65535
#define MAX_LIB_VARS  255
#define MAX_LIB_ENTRY_NAME 127
#define MAX_ERROR 1024
#define MAX_LINE  1024
#define SYSTEM_LIB_NAME "syslib"

enum PspConfigMode
{
	PSP_BUILD_UNKNOWN,
	PSP_BUILD_EXPORTS,
	PSP_BUILD_STUBS,
	PSP_BUILD_STUBS_NEW,
};

struct psp_export
{
	struct psp_export *pNext;
	unsigned int nid;
	char name[MAX_LIB_ENTRY_NAME+1];
};

struct psp_alias
{
	struct psp_alias *pNext;
	char name[MAX_LIB_ENTRY_NAME+1];
	char alias[MAX_LIB_ENTRY_NAME+1];
};

struct psp_lib
{
	struct psp_lib *pNext;
	char name[MAX_LIB_NAME+1];
	unsigned short ver;
	unsigned short attr;
	int funcCount;
	struct psp_export *pFuncHead;
	int varCount;
	struct psp_export *pVarHead;
	struct psp_alias *pAliasHead;
};

struct export_cmd
{
	const char *name;
	int params;
	int (*handler)(char ** params);
};

/* Specifies that the current usage is to the print the pspsdk path */
static enum PspConfigMode g_outputmode;
static int g_verbose = 0;
static const char *g_infile;
static char g_errstring[MAX_ERROR] = "No Error";
static struct psp_lib *g_libhead = NULL;
static struct psp_lib *g_currlib = NULL;
static int g_libcount = 0;

void free_export_chain(struct psp_export *pHead)
{
	struct psp_export *pNext;

	pNext = pHead;

	while(pNext != NULL)
	{
		struct psp_export *pTemp;

		pTemp = pNext->pNext;
		free(pNext);
		pNext = pTemp;
	}
}

void free_alias_chain(struct psp_alias *pHead)
{
	struct psp_alias *pNext;

	pNext = pHead;

	while(pNext != NULL)
	{
		struct psp_alias *pTemp;

		pTemp = pNext->pNext;
		free(pNext);
		pNext = pTemp;
	}
}

void free_lib_data(void)
{
	struct psp_lib *pNext;

	if(g_currlib != NULL)
	{
		free_export_chain(g_currlib->pFuncHead);
		free_export_chain(g_currlib->pVarHead);
		free_alias_chain(g_currlib->pAliasHead);
		free(g_currlib);
		g_currlib = NULL;
	}

	pNext = g_libhead;

	while(pNext != NULL)
	{
		struct psp_lib *pTemp;

		pTemp = pNext->pNext;
		free_export_chain(pNext->pFuncHead);
		free_export_chain(pNext->pVarHead);
		free_alias_chain(pNext->pAliasHead);
		free(pNext);
		pNext = pTemp;
	}

	g_libhead = NULL;
}

const char *find_alias(struct psp_alias *pHead, const char *name)
{
	struct psp_alias *pNext;
	const char *szAlias = NULL;

	pNext = pHead;
	while(pNext)
	{
		if(strcmp(name, pNext->name) == 0)
		{
			szAlias = pNext->alias;
			break;
		}

		pNext = pNext->pNext;
	}

	return szAlias;
}

static struct option arg_opts[] = 
{
	{"build-exports", no_argument, NULL, 'b'},
	{"build-stubs", no_argument, NULL, 's'},
	{"build-stubs-new", no_argument, NULL, 'k'},
	{"verbose", no_argument, NULL, 'v'},
	{ NULL, 0, NULL, 0 }
};

/* Process the arguments */
int process_args(int argc, char **argv)
{
	int ret = 0;
	int ch;

	g_outputmode = PSP_BUILD_UNKNOWN;

	ch = getopt_long(argc, argv, "bsvk", arg_opts, NULL);
	while(ch != -1)
	{
		switch(ch)
		{
			case 'b' : g_outputmode = PSP_BUILD_EXPORTS;
					   ret = 1;
					   break;
			case 's' : g_outputmode = PSP_BUILD_STUBS;
					   ret = 1;
					   break;
			case 'v' : g_verbose = 1;
					   break;
			case 'k' : g_outputmode = PSP_BUILD_STUBS_NEW;
					   ret = 1;
					   break;
			default  : //fprintf(stderr, "Invalid option '%c'\n", ch);
					   break;
		};

		ch = getopt_long(argc, argv, "bsvk", arg_opts, NULL);
	}

	argc -= optind;
	argv += optind;

	if(argc < 1)
	{
		return 0;
	}

	g_infile = argv[0];

	return ret;
}

void print_help(void)
{
	fprintf(stderr, "Usage: psp-build-exports -b|-s|-k [-v] export.exp\n");
	fprintf(stderr, "Options:\n");
	fprintf(stderr, "-b, --build-exports     : Build an export file to stdout\n");
	fprintf(stderr, "-s, --build-stubs       : Build a batch of stub files for the exports\n");
	fprintf(stderr, "-k, --build-stubs-new   : Build a batch of stub files (in new format)\n");
	fprintf(stderr, "-v, --verbose           : Verbose output\n");
}

void strip_whitespace(char *dest, const char *src)
{
	assert(dest != NULL);
	assert(src != NULL);

	while(*src != 0)
	{
		if(!isspace(*src))
		{
			*dest = *src;
			dest++;
		}

		src++;
	}

	*dest = 0;
}

/*
   Parse a string of the form FUNC(arg1,arg2,...)
   Returns the number of parameters or -1 on error.
   */
int parse_string(char *line, char **params, int max_params)
{
	int pos;
	int param_start;
	char *next_param;
	int param_count;

	assert(line != NULL);
	assert(params != NULL);

	pos = 0;

	/* Parse off "function" name */
	while((isalnum(line[pos])) || (line[pos] == '_'))
	{
		pos++;
	}

	/* If we don't have any thing else than this is just the function */
	if(line[pos] == 0)
	{
		return 0;
	}

	if(line[pos] != '(')
	{
		snprintf(g_errstring, MAX_ERROR, "Invalid character %c, expected (", line[pos]);
		return -1;
	}

	/* Terminate */
	line[pos++] = 0;
	param_start = pos;

	/* Scan parameters */
	while((isalnum(line[pos])) || (line[pos] == '_') || (line[pos] == ','))
	{
		pos++;
	}

	if(line[pos] == 0)
	{
		snprintf(g_errstring, MAX_ERROR, "Missing closing brace ')'");
		return -1;
	}

	if(line[pos] != ')')
	{
		snprintf(g_errstring, MAX_ERROR, "Invalid character %c, expected ')'", line[pos]);
		return -1;
	}

	if(line[pos+1] != 0)
	{
		snprintf(g_errstring, MAX_ERROR, "Trailing characters after bracket close %d", line[pos+1]);
		return -1;
	}

	line[pos] = 0;

	param_count = 0;
	next_param = strtok(&line[param_start], ",");
	while(next_param != NULL)
	{
		if(param_count == max_params)
		{
			snprintf(g_errstring, MAX_ERROR, "Run out of space for parameters");
			return -1;
		}

		params[param_count++] = next_param;
		next_param = strtok(NULL, ",");
	}

	return param_count;
}

void build_exports_output_extern(struct psp_export *pHead)
{
	struct psp_export *pExp;

	pExp = pHead;
	while(pExp != NULL)
	{
		fprintf(stdout, "extern int %s;\n", pExp->name);
		pExp = pExp->pNext;
	}
}

void build_exports_output_nids(struct psp_export *pHead)
{
	struct psp_export *pExp;

	pExp = pHead;
	while(pExp != NULL)
	{
		fprintf(stdout, "\t0x%08X,\n", pExp->nid);
		pExp = pExp->pNext;
	}
}

void build_exports_output_names(struct psp_export *pHead)
{
	struct psp_export *pExp;

	pExp = pHead;
	while(pExp != NULL)
	{
		fprintf(stdout, "\t(unsigned int) &%s,\n", pExp->name);
		pExp = pExp->pNext;
	}
}

void build_exports(void)
{
	struct psp_lib *pLib;

	fprintf(stdout, "#include <pspmoduleexport.h>\n");
	fprintf(stdout, "#define NULL ((void *) 0)\n\n");

	/* Write out the nid tables */
	pLib = g_libhead;
	while(pLib != NULL)
	{
		/* Print extern */
		build_exports_output_extern(pLib->pFuncHead);
		build_exports_output_extern(pLib->pVarHead);

		fprintf(stdout, "static const unsigned int __%s_exports[%d] __attribute__((section(\".rodata.sceResident\"))) = {\n", 
				pLib->name, (pLib->funcCount + pLib->varCount) * 2);

		/* Print nid */
		build_exports_output_nids(pLib->pFuncHead);
		build_exports_output_nids(pLib->pVarHead);
		/* Print names */
		build_exports_output_names(pLib->pFuncHead);
		build_exports_output_names(pLib->pVarHead);
		fprintf(stdout, "};\n\n");

		pLib = pLib->pNext;
	}

	fprintf(stdout, "const struct _PspLibraryEntry __library_exports[%d] __attribute__((section(\".lib.ent\"), used)) = {\n", g_libcount);
	pLib = g_libhead;
	while(pLib != NULL)
	{
		if(strcmp(pLib->name, SYSTEM_LIB_NAME) == 0)
		{
			fprintf(stdout, "\t{ NULL, ");
		}
		else
		{
			fprintf(stdout, "\t{ \"%s\", ", pLib->name);
		}

		fprintf(stdout, "0x%04X, 0x%04X, 4, %d, %d, (unsigned int *) &__%s_exports },\n", pLib->ver, pLib->attr, 
				pLib->varCount, pLib->funcCount, pLib->name);

		pLib = pLib->pNext;
	}
	fprintf(stdout, "};\n");
}

void build_stubs_output_lib(struct psp_lib *pLib)
{
	FILE *fp;
	char filename[256];

	snprintf(filename, 256, "%s.S", pLib->name);
	if(g_verbose)
	{
		fprintf(stderr, "Writing output file %s\n", filename);
	}
	fp = fopen(filename, "w");
	if(fp != NULL)
	{
		struct psp_export *pExp;

		fprintf(fp, "\t.set noreorder\n\n");
		fprintf(fp, "#include \"pspstub.s\"\n\n");
		fprintf(fp, "\tSTUB_START \"%s\",0x%08X,0x%08X\n", pLib->name, ((pLib->attr | 0x8) << 16) | pLib->ver,
						(pLib->funcCount << 16) | 5);

		pExp = pLib->pFuncHead;
		while(pExp != NULL)
		{
			const char *alias;

			alias = find_alias(pLib->pAliasHead, pExp->name);
			if(alias)
			{
				fprintf(fp, "\tSTUB_FUNC_WITH_ALIAS  0x%08X,%s,%s\n", pExp->nid, pExp->name,alias);
			}
			else
			{
				fprintf(fp, "\tSTUB_FUNC  0x%08X,%s\n", pExp->nid, pExp->name);
			}
			pExp = pExp->pNext;
		}

		fprintf(fp, "\tSTUB_END\n");

		fclose(fp);
	}
	else
	{
		fprintf(stderr, "Error, couldn't open file %s for writing\n", filename);
	}
}

void build_stubs_output_lib_new(struct psp_lib *pLib)
{
	FILE *fp;
	char filename[256];
	int i;

	snprintf(filename, 256, "%s.S", pLib->name);
	if(g_verbose)
	{
		fprintf(stderr, "Writing output file %s\n", filename);
	}
	fp = fopen(filename, "w");
	if(fp != NULL)
	{
		struct psp_export *pExp;

		fprintf(fp, "\t.set noreorder\n\n");
		fprintf(fp, "#include \"pspimport.s\"\n\n");

		fprintf(fp, "// Build files\n");
		fprintf(fp, "// %s_0000.o ", pLib->name);
		pExp = pLib->pFuncHead;
		i = 1;
		while(pExp != NULL)
		{
			fprintf(fp, "%s_%04d.o ", pLib->name, i++);
			pExp = pExp->pNext;
		}
		fprintf(fp, "\n\n");

		fprintf(fp, "#ifdef F_%s_0000\n", pLib->name);
		fprintf(fp, "\tIMPORT_START \"%s\",0x%08X\n", pLib->name, ((pLib->attr | 0x8) << 16) | pLib->ver);
		fprintf(fp, "#endif\n");

		pExp = pLib->pFuncHead;
		i = 1;
		while(pExp != NULL)
		{
			const char *alias;
			fprintf(fp, "#ifdef F_%s_%04d\n", pLib->name, i++);

			alias = find_alias(pLib->pAliasHead, pExp->name);
			if(alias)
			{
				fprintf(fp, "\tIMPORT_FUNC_WITH_ALIAS  \"%s\",0x%08X,%s,%s\n", pLib->name, pExp->nid, pExp->name, alias);
			}
			else
			{
				fprintf(fp, "\tIMPORT_FUNC  \"%s\",0x%08X,%s\n", pLib->name, pExp->nid, pExp->name);
			}

			fprintf(fp, "#endif\n");
			pExp = pExp->pNext;
		}

		fclose(fp);
	}
	else
	{
		fprintf(stderr, "Error, couldn't open file %s for writing\n", filename);
	}
}

void build_stubs(void)
{
	struct psp_lib *pLib;

	pLib = g_libhead;

	while(pLib != NULL)
	{
		if(strcmp(pLib->name, SYSTEM_LIB_NAME))
		{
			if(g_outputmode == PSP_BUILD_STUBS)
			{
				build_stubs_output_lib(pLib);
			}
			else
			{
				build_stubs_output_lib_new(pLib);
			}
		}

		pLib = pLib->pNext;
	}
}

int validate_number(const char *str, unsigned int *num)
{
	char *endp;

	assert(str != NULL);
	assert(num != NULL);

	if(!isxdigit(*str))
	{
		snprintf(g_errstring, MAX_ERROR, "'%s' is an invalid number", str);
		return 0;
	}

	*num = strtoul(str, &endp, 0);
	if(*endp != 0)
	{
		snprintf(g_errstring, MAX_ERROR, "'%s' has trailing non-numeric characters", str);
		return 0;
	}

	return 1;
}

int psp_begin_exports(char **params)
{
	/* Do nothing */
	return 1;
}

int psp_end_exports(char **params)
{
	if(g_currlib != NULL)
	{
		/* Error, missing end of exports */
	}

	return 1;
}

int psp_export_start(char **params)
{
	unsigned int ver;
	unsigned int attr;

	if(g_currlib != NULL)
	{
		snprintf(g_errstring, MAX_ERROR, "Previous library '%s' not ended", g_currlib->name);
		return 0;
	}

	if(validate_number(params[1], &ver) == 0)
	{
		return 0;
	}

	if(validate_number(params[2], &attr) == 0)
	{
		return 0;
	}

	g_currlib = (struct psp_lib *) malloc(sizeof(struct psp_lib));
	if(g_currlib == NULL)
	{
		snprintf(g_errstring, MAX_ERROR, "Could not allocate memory for library");
		return 0;
	}

	memset(g_currlib, 0, sizeof(struct psp_lib));
	g_libcount++;

	strncpy(g_currlib->name, params[0], MAX_LIB_NAME);
	g_currlib->ver = ver;
	g_currlib->attr = attr;

	return 1;
}

int psp_export_end(char **params)
{
	struct psp_lib *pNext;

	if(g_currlib == NULL)
	{
		snprintf(g_errstring, MAX_ERROR, "No matching start command for library end");
		return 1;
	}

	/* Add to the end of the chain */
	if(g_libhead == NULL)
	{
		g_libhead = g_currlib;
	}
	else
	{
		pNext = g_libhead;
		while(pNext->pNext != NULL)
		{
			pNext = pNext->pNext;
		}

		pNext->pNext = g_currlib;
	}

	g_currlib = NULL;

	return 1;
}

int internal_do_export(const char *name, unsigned int nid, struct psp_export **pHead)
{
	struct psp_export *pNew;

	assert(name != NULL);
	assert(pHead != NULL);

	pNew = (struct psp_export *) malloc(sizeof(struct psp_export));

	if(pNew != NULL)
	{
		struct psp_export *pNext;

		memset(pNew, 0, sizeof(struct psp_export));
		strncpy(pNew->name, name, MAX_LIB_ENTRY_NAME);
		pNew->nid = nid;

		if(*pHead == NULL)
		{
			*pHead = pNew;
		}
		else
		{
			pNext = *pHead;
			while(pNext->pNext != NULL)
			{
				pNext = pNext->pNext;
			}

			pNext->pNext = pNew;
		}
	}
	else
	{
		snprintf(g_errstring, MAX_ERROR, "Could not allocate memory for export %s", name);
		return 0;
	}

	return 1;
}

int psp_export_func_nid(char **params)
{
	unsigned int nid;

	if(g_currlib == NULL)
	{
		snprintf(g_errstring, MAX_ERROR, "Cannot export function, not in a library definition");
		return 0;
	}

	if(validate_number(params[1], &nid) == 0)
	{
		return 0;
	}

	if(g_currlib->funcCount < MAX_LIB_FUNCS)
	{
		if(internal_do_export(params[0], nid, &g_currlib->pFuncHead))
		{
			g_currlib->funcCount++;
			return 1;
		}
	}
	else
	{
		snprintf(g_errstring, MAX_ERROR, "Too many functions defined, cannot export another");
	}

	return 0;
}

int psp_export_func_hash(char **params)
{
	unsigned int nid;
	unsigned char hash[SHA1_DIGEST_SIZE];

	if(g_currlib == NULL)
	{
		snprintf(g_errstring, MAX_ERROR, "Cannot export function, not in a library definition");
		return 0;
	}

	sha1(hash, (unsigned char *) params[0], strlen(params[0]));
	nid = hash[0] | (hash[1] << 8) | (hash[2] << 16) | (hash[3] << 24);

	if(g_currlib->funcCount < MAX_LIB_FUNCS)
	{
		if(internal_do_export(params[0], nid, &g_currlib->pFuncHead))
		{
			g_currlib->funcCount++;
			return 1;
		}
	}
	else
	{
		snprintf(g_errstring, MAX_ERROR, "Too many functions defined, cannot export another");
	}

	return 0;
}

int psp_export_var_nid(char **params)
{
	unsigned int nid;

	if(g_currlib == NULL)
	{
		snprintf(g_errstring, MAX_ERROR, "Cannot export variable, not in a library definition");
		return 0;
	}

	if(validate_number(params[1], &nid) == 0)
	{
		return 0;
	}

	if(g_currlib->varCount < MAX_LIB_VARS)
	{
		if(internal_do_export(params[0], nid, &g_currlib->pVarHead))
		{
			g_currlib->varCount++;
			return 1;
		}
	}
	else
	{
		snprintf(g_errstring, MAX_ERROR, "Too many variables defined, cannot export another");
	}

	return 0;
}

int psp_export_var_hash(char **params)
{
	unsigned int nid;
	unsigned char hash[SHA1_DIGEST_SIZE];

	if(g_currlib == NULL)
	{
		snprintf(g_errstring, MAX_ERROR, "Cannot export variable, not in a library definition");
		return 0;
	}

	sha1(hash, (unsigned char *) params[0], strlen(params[0]));
	nid = hash[0] | (hash[1] << 8) | (hash[2] << 16) | (hash[3] << 24);

	if(g_currlib->varCount < MAX_LIB_VARS)
	{
		if(internal_do_export(params[0], nid, &g_currlib->pVarHead))
		{
			g_currlib->varCount++;
			return 1;
		}
	}
	else
	{
		snprintf(g_errstring, MAX_ERROR, "Too many variables defined, cannot export another");
	}

	return 0;
}

int psp_export_alias(char **params)
{
	struct psp_alias *pAlias;

	if(g_currlib == NULL)
	{
		snprintf(g_errstring, MAX_ERROR, "Cannot alias name, not in a library definition");
		return 0;
	}

	pAlias = (struct psp_alias *) malloc(sizeof(struct psp_alias));
	if(pAlias == NULL)
	{
		snprintf(g_errstring, MAX_ERROR, "Cannot allocate memory for alias");
		return 0;
	}

	memset(pAlias, 0, sizeof(*pAlias));
	strncpy(pAlias->name, params[0], MAX_LIB_ENTRY_NAME);
	strncpy(pAlias->alias, params[1], MAX_LIB_ENTRY_NAME);

	if(g_currlib->pAliasHead == NULL)
	{
		g_currlib->pAliasHead = pAlias;
	}
	else
	{
		struct psp_alias *pNext = g_currlib->pAliasHead;

		while(pNext->pNext)
		{
			pNext = pNext->pNext;
		}

		pNext->pNext = pAlias;
	}

	fprintf(stderr, "Alias %s->%s\n", params[0], params[1]);

	return 1;
}

struct export_cmd commands[] = 
{
	{ "PSP_BEGIN_EXPORTS", 0, psp_begin_exports },
	{ "PSP_END_EXPORTS", 0, psp_end_exports },
	{ "PSP_EXPORT_START", 3, psp_export_start },
	{ "PSP_EXPORT_END", 0, psp_export_end },
	{ "PSP_EXPORT_FUNC_NID", 2, psp_export_func_nid },
	{ "PSP_EXPORT_FUNC_HASH", 1, psp_export_func_hash },
	{ "PSP_EXPORT_FUNC", 1, psp_export_func_hash },
	{ "PSP_EXPORT_VAR_NID", 2, psp_export_var_nid },
	{ "PSP_EXPORT_VAR", 1, psp_export_var_hash },
	{ "PSP_EXPORT_VAR_HASH", 1, psp_export_var_hash },
	{ "PSP_EXPORT_ALIAS", 2, psp_export_alias },
	{ NULL, 0, NULL }
};

int process_command(const char *cmd, char ** params, int param_count)
{
	int i;

	i = 0;

	while(commands[i].name != NULL)
	{
		if(strcmp(cmd, commands[i].name) == 0)
		{
			assert(commands[i].handler != NULL);
			if(param_count != commands[i].params)
			{
				snprintf(g_errstring, MAX_ERROR, "Incorrect number of parameters, have %d, expect %d\n", 
						param_count, commands[i].params);
				break;
			}
			else
			{
				return commands[i].handler(params);
			}
		}

		i++;
	}

	if(commands[i].name == NULL)
	{
		snprintf(g_errstring, MAX_ERROR, "Unknown command '%s'", cmd);
	}

	return 0;
}

int load_exports(void)
{
	FILE *fp;
	int line_no = 1;

	fp = fopen(g_infile, "r");
	if(fp != NULL)
	{
		char curr_line[MAX_LINE];
		char strip_line[MAX_LINE];

		while(fgets(curr_line, MAX_LINE, fp))
		{
			strip_whitespace(strip_line, curr_line);
			if((strip_line[0] != 0) && (strip_line[0] != '#'))
			{
				int ret;
				char *params[16];

				ret = parse_string(strip_line, params, 16);
				if(ret >= 0)
				{
					if(g_verbose)
					{
						int i;

						fprintf(stderr, "Line %d, Name: %s ", line_no, strip_line);
						for(i = 0; i < ret; i++)
						{
							fprintf(stderr, "Arg %d: %s ", i, params[i]);
						}
						fprintf(stderr, "\n");
					}

					if(process_command(strip_line, params, ret) == 0)
					{
						fprintf(stderr, "Error, line %d: %s\n", line_no, g_errstring);
						return 0;
					}
				}
				else
				{
					fprintf(stderr, "Error, line %d: %s\n", line_no, g_errstring);
					return 0;
				}
			}

			line_no++;
		}

		if(g_currlib)
		{
			fprintf(stderr, "Error, reached end of file while still exporting a library\n");
			return 0;
		}
	}
	else
	{
		fprintf(stderr, "Could not open file %s\n", g_infile);
		return 0;
	}

	return 1;
}

void dump_exports(void)
{
	struct psp_lib *pLib;
	int count;

	pLib = g_libhead;
	count = 1;

	while(pLib != NULL)
	{
		fprintf(stderr, "Export %d '%s', ver %04X, attr %04X\n", count, pLib->name, pLib->ver, pLib->attr);
		fprintf(stderr, "Funcs %d-%p, Vars %d-%p\n", pLib->funcCount, pLib->pFuncHead,  pLib->varCount, pLib->pVarHead);

		if(pLib->pFuncHead != NULL)
		{
			int func;
			struct psp_export *pFunc;

			func = 0;
			pFunc = pLib->pFuncHead;
			while(pFunc != NULL)
			{
				fprintf(stderr, "Function %d - '%s', nid %08X\n", func++, pFunc->name, pFunc->nid);
				pFunc = pFunc->pNext;
			}
		}

		if(pLib->pVarHead != NULL)
		{
			int var;
			struct psp_export *pVar;

			var = 0;
			pVar = pLib->pVarHead;
			while(pVar != NULL)
			{
				fprintf(stderr, "Variable %d - '%s', nid %08X\n", var++, pVar->name, pVar->nid);
				pVar = pVar->pNext;
			}
		}



		count++;
		pLib = pLib->pNext;
	}
}

int main(int argc, char **argv)
{
	if(process_args(argc, argv))
	{
		if(load_exports())
		{
			if(g_verbose)
			{
				dump_exports();
			}
			switch(g_outputmode)
			{
				case PSP_BUILD_EXPORTS: build_exports();
										break;
										/* Do the same for both */
				case PSP_BUILD_STUBS_NEW: 
				case PSP_BUILD_STUBS  : build_stubs();
										break;
				default				  : /* Argh */
										break;
			};
		}

		free_lib_data();
	}
	else
	{
		print_help();
	}

	return 0;
}
