// By IQ_132
// http://neosource.1emu.net
//

// 03-07-08 revision 1
//      adjusted alignment of titles
//	added comments
//	modified alphabetizing, should reduce ram (and increase speed?)
//	added ability to put debug drivers at the end of the list

// 14-06-08 revision 0
// 	everything!

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

// Maximum number of drivers allowed in the list
#define MAX_DRIVER			4096

// Change 0 to 1 to hide debug drivers
#define HIDE_DEBUG_DRIVERS		0

// Change 0 to 1 to put debug drivers at the end of the driverlist
#define SHOW_DEBUG_DRIVERS_LAST		0

// Uncomment this line to have drivers properly alphabetized (skipping "The")
#define SKIP_THE

int main()
{
	int i, j, k;
	FILE *fz, *fp, *fo;

	char vLine[1024];
	char szLine[1024];
	char fAddress[1024];

	int driverloc = 0;

	char *titlelist[MAX_DRIVER];
	char *driverlist[MAX_DRIVER];
	char *commentlist[MAX_DRIVER];

	unsigned char *drivers    = (unsigned char*)malloc(MAX_DRIVER * 0x100);
	unsigned char *titles     = (unsigned char*)malloc(MAX_DRIVER * 0x200);
	unsigned char *drivertype = (unsigned char*)malloc(MAX_DRIVER * 0x001);		// 0 = reg, 1 = d, 2 = x
	unsigned char *comments   = (unsigned char*)malloc(MAX_DRIVER * 0x100);

	memset (drivers,    0, MAX_DRIVER * 0x100);
	memset (titles,     0, MAX_DRIVER * 0x200);
	memset (drivertype, 0, MAX_DRIVER * 0x001);
	memset (comments,   0, MAX_DRIVER * 0x100);

	for (i = 0; i < MAX_DRIVER; i++) {
		driverlist[i]  = (char*)drivers  + (i * 0x100);
		titlelist[i]   = (char*)titles   + (i * 0x200);
		commentlist[i] = (char*)comments + (i * 0x100);
	}

	fz = fopen("FBA.vcproj", "rt");
	while (1)
	{
		if (fgets(vLine, 1024, fz) == NULL)
			break;

		int nLen = (int)strlen(vLine);

		// find driver file (must start with d_...)

		int cont = 0;
		for (i = 0; i < nLen - 3; i++)
		{
			if (strncmp (vLine + i, "\\d_", 3) == 0) {
				cont = 1;
			}
		}
		if (!cont) continue;
		cont = 0;

		// now get file address
		char *relpath = "RelativePath=\".\\";
		int relpathlen = strlen (relpath);
		for (i = 0; i < nLen; i++)
		{
			if (strncmp (vLine + i, relpath, relpathlen) == 0) {
				char *addr = vLine + i + relpathlen;
				char *dest = fAddress;
				j = i;
				while (j < nLen) {
					dest[0] = addr[0];
					dest++;
					addr++;
					j++;
					if (addr[0] == '\"') {
						dest[0] = '\0';
						break;
					}
				}
				break;
			}
		}

		fp = fopen(fAddress, "rt");
		if (fp == NULL) {
			continue;
		}

		int incomment = 0;
		while (1)
		{
			if (fgets (szLine, 1024, fp) == NULL)
				break;

			int rLen = strlen(szLine);

			// check every line for comment code - slow!
			for (i = 0; i < rLen-2; i++) {
				if (szLine[i] == '/' && szLine[i+1] == '*') { //strncmp (szLine + i, "/*", 2) == 0) {
					i+=2;
					incomment = 1;
				}
				if (incomment) {
					if (szLine[i+1] == '/' && szLine[i] == '*') { //strncmp (szLine + i, "*/", 2) == 0) {
						i+=2;
						incomment = 0;
					}
				}
			}

			if (incomment) continue;
			if (rLen < 17) continue; // speedup!

			char *burndrv = "struct BurnDriver ";
			int burndrvlen = strlen(burndrv);

			char *burndrvd = "struct BurnDriverD ";
			int burndrvdlen = strlen(burndrvd);

			char *burndrvx = "struct BurnDriverX ";
			int burndrvxlen = strlen(burndrvx);

			int zLen = 0;
			for (i = 0; i < rLen; i++) {
				if (strncmp (szLine + i, burndrv , burndrvlen ) == 0)
					zLen = burndrvlen;

				if (strncmp (szLine + i, burndrvd, burndrvdlen) == 0
				 || strncmp (szLine + i, burndrvx, burndrvxlen) == 0)
					zLen = burndrvdlen;

				if (!zLen) continue;

				char *dest = driverlist[driverloc];
				char *src  = szLine + i + zLen;
				while (1){
					dest[0] = src[0];
					dest++;
					src++;

					if (src[0] == ' ' || src[0] == '=' || src[0] == '\t' || src[0] == '\n') {
						dest[0] = '\0';

						drivertype[driverloc] = (szLine[i + 17] >> 2) & 3;

						break;
					}
				}

				break;
			}

			if (strlen(driverlist[driverloc]) < 7) continue;

			// make sure this isn't in the driverlist already!
			int pastbrk = 0;
			for (i = 0; i < driverloc; i++) {
				if (strcmp (driverlist[i], driverlist[driverloc]) == 0) {
					memset (driverlist[driverloc], 0, 256);
					pastbrk = 1;
					break;
				}
			}
			if (pastbrk) continue;

			// either line with { or with set name/parent/year info
			if (fgets (szLine, 1024, fp) == NULL)
				break;

			rLen = strlen(szLine);

			// the above line was {, so get the name/parent/year info
			if (rLen < 5) {
				if (fgets (szLine, 1024, fp) == NULL)
					break;
			}

		// great! now get the title
			if (fgets (szLine, 1024, fp) == NULL)
				break;

			char *src = szLine;
			char *dst = titlelist[driverloc];

			while (1) {
				if (src[0] == '\"') break;
				src++;
			}

			src++; // past quote

			while (src[0] != '\\') {
				dst[0] = src[0];
				src++;
				dst++;
			}
			dst[0] = '\0';


		// now get comment
			while (src[0] != '\"') {
				src++;
			}

			src+=2; // past quote & comma
			while (src[0] == ' ' || src[0] == ',') {
				src++;
			}

			dst = commentlist[driverloc];

			if (src[0] == 'N' && src[1] == 'U') {
				dst[0] = '\0'; // end
			} else {
				if (src[0] == '\"') {
					src++;

					while (src[0] != '\"') {
						dst[0] = src[0];
						src++;
						dst++;
						if (src[0] == '\\' && src[1] == '\"') {
							dst[0] = '\"';
							src+=2; dst++;
						}
					}
				} else {
					dst[0] = '\0';
				}
			}

			driverloc++;

			if (driverloc == MAX_DRIVER) break;
		}

		fclose (fp);

		if (driverloc == MAX_DRIVER) break;
	}

	fclose (fz);


///////////
// change "The..." to "..., The"

#ifdef SKIP_THE
	{
		char tmp[512];

		for (i = 0; i < driverloc; i++)
		{
			if (strncmp ("The ", titlelist[i], 4) != 0)
				continue;

			strncpy (tmp, titlelist[i] + 4, strlen(titlelist[i])-3);
			strcat (tmp, ", The");

			strcpy (titlelist[i], tmp);
		}
	}
#endif

///////////
// alphabetize the list

	{
		char *titlelist2[MAX_DRIVER];
		unsigned int *drivertype2 = (unsigned int *)malloc(MAX_DRIVER * sizeof(int));		// 0 = reg, 1 = d, 2 = x
		char dummy[128];

		for (i = 0; i < MAX_DRIVER; i++) {
			titlelist2[i] = (char*)titles + (i * 0x200);
		}

		memcpy (drivertype2, drivertype, MAX_DRIVER * sizeof(int));
		memset (drivertype, 0, MAX_DRIVER * sizeof (int));

		memset (dummy, 0xff, 128);

		int pos = 0;
		i = 0;
		j = 1;

		// This actually turned out to be much more simple & fast than I anticipated

		while (1)
		{
			if (pos == driverloc) break;

			if (strcmp (titlelist2[i], titlelist2[j]) <= 0) {
				j++;
				j %= driverloc;
			} else {
				i++;
				i %= driverloc;
				j = i + 1;
				j %= driverloc;
			}
	
			if (j == i) {
				titlelist[pos] = (char*)titles + i * 0x200;
				driverlist[pos] = (char*)drivers + i * 0x100;

				drivertype[pos] = drivertype2[i];

				commentlist[pos] = (char*)comments + i * 0x100;

				titlelist2[i] = dummy;
	
				pos++;
			}
		}

		free (drivertype2);
	}

/////////////
// dump the driverlist

	fo = fopen("src\\generated\\driverlist.h", "wt");

	time_t rawtime;
	struct tm * timeinfo;

	time ( &rawtime );
	timeinfo = localtime ( &rawtime );

	fprintf (fo, "// This file was generated %s\n\n", asctime (timeinfo));
	fprintf (fo, "// Declaration of all drivers\n");
	fprintf (fo, "#define DRV extern struct BurnDriver\n");

	int prevtype = 0;

	for (i = 0; i < driverloc; i++)
	{
		int type = drivertype[i];

#if (HIDE_DEBUG_DRIVERS | SHOW_DEBUG_DRIVERS_LAST)
		if (type == 1) continue;
#endif

		if (type == 1 && (prevtype != 1 || i == 0)) {
			fprintf (fo, "#if defined FBA_DEBUG\n");
		}

		if (prevtype == 1 && type != 1) {
			fprintf (fo, "#endif\n");
		}

		prevtype = type;

		if (type != 2) {
			fprintf (fo, "DRV	%s;",  driverlist[i]);
			if (strlen(commentlist[i]) > 1) {
				fprintf (fo, "   	%s%s// %s", (strlen(driverlist[i]) < 12) ? "\t" : "", (strlen(driverlist[i]) < 20) ? "\t" : "", commentlist[i]);
			}
			fprintf (fo, "\n");
		}
	}

	if (prevtype == 1) fprintf (fo, "#endif\n");

#if (!HIDE_DEBUG_DRIVERS && SHOW_DEBUG_DRIVERS_LAST)

	fprintf (fo, "#if defined FBA_DEBUG\n");

	for (i = 0; i < driverloc; i++)
	{
		if (drivertype[i] != 1) continue;

		fprintf (fo, "DRV	%s;",  driverlist[i]);
		if (strlen(commentlist[i]) > 1) {
			fprintf (fo, "   	%s%s// %s", (strlen(driverlist[i]) < 12) ? "\t" : "", (strlen(driverlist[i]) < 20) ? "\t" : "", commentlist[i]);
		}
		fprintf (fo, "\n");
	}

	fprintf (fo, "#endif\n");
#endif

	fprintf (fo, "#undef DRV\n\n");

	fprintf (fo, "// Structure containing addresses of all drivers\n");
	fprintf (fo, "// Needs to be kept sorted (using the full game name as the key) to prevent problems with the gamelist in Kaillera\n");
	fprintf (fo, "static struct BurnDriver* pDriver[] = {\n");

	prevtype = 0;
	for (i = 0; i < driverloc; i++)
	{
		int type = drivertype[i];

#if (HIDE_DEBUG_DRIVERS | SHOW_DEBUG_DRIVERS_LAST)
		if (type == 1) continue;
#endif

		if ((type == 1 && prevtype != 1) || (type == 1 && i == 0)) {
			fprintf (fo, "#if defined FBA_DEBUG\n");
		}

		if (prevtype == 1 && type != 1) {
			fprintf (fo, "#endif\n");
		}

		prevtype = type;

		if (type != 2) fprintf (fo, "	&%s,	%s%s// %s\n", driverlist[i], (strlen(driverlist[i]) < 14) ? "\t" : "", (strlen(driverlist[i]) < 23) ? "\t" : "", titlelist[i]);
	}

	if (prevtype == 1) fprintf (fo, "#endif\n");

#if (!HIDE_DEBUG_DRIVERS && SHOW_DEBUG_DRIVERS_LAST)
	fprintf (fo, "#if defined FBA_DEBUG\n");

	for (i = 0; i < driverloc; i++)
	{
		if (drivertype[i] != 1) continue;

		fprintf (fo, "	&%s,	%s%s// %s\n", driverlist[i], (strlen(driverlist[i]) < 14) ? "\t" : "", (strlen(driverlist[i]) < 23) ? "\t" : "", titlelist[i]);
	}

	fprintf (fo, "#endif\n");
#endif

	fprintf (fo, "};\n\n");


	fprintf (fo, "static struct BurnDriver* pAllDrivers[] = {\n");

	int totallisted = 0;

	prevtype = 0;
	for (i = 0; i < driverloc; i++)
	{
		int type = drivertype[i];

#if (HIDE_DEBUG_DRIVERS | SHOW_DEBUG_DRIVERS_LAST)
		if (type == 1) continue;
#endif

		if ((type == 1 && prevtype != 1) || (type == 1 && i == 0)) {
			fprintf (fo, "#if defined FBA_DEBUG\n");
		}

		if (prevtype == 1 && type != 1) {
			fprintf (fo, "#endif\n");
		}

		prevtype = type;

		if (type != 2) {
			fprintf (fo, "	&%s,	%s%s// %s\n", driverlist[i], (strlen(driverlist[i]) < 14) ? "\t" : "", (strlen(driverlist[i]) < 23) ? "\t" : "", titlelist[i]);
			totallisted++;
		}
	}

	if (prevtype == 1) fprintf (fo, "#endif\n");

#if (!HIDE_DEBUG_DRIVERS && SHOW_DEBUG_DRIVERS_LAST)
	fprintf (fo, "#if defined FBA_DEBUG\n");

	for (i = 0; i < driverloc; i++)
	{
		if (drivertype[i] != 1) continue;

		fprintf (fo, "	&%s,	%s%s// %s\n", driverlist[i], (strlen(driverlist[i]) < 14) ? "\t" : "", (strlen(driverlist[i]) < 23) ? "\t" : "", titlelist[i]);
		totallisted++;
	}

	fprintf (fo, "#endif\n");
#endif

	fprintf (fo, "};\n\n");

	fclose (fo);

	free (drivers);
	free (drivertype);
	free (titles);
	free (comments);
	
	printf ("%d of %d available drivers listed\n", totallisted, driverloc);
}

