/*	main.cpp
 *
 *		the main entry point for my resource compiler
 */

/*  RCompile - UNIX Resource compiler for YAAF Library
 *  Copyright (C) 1998 William Edward Woody
 *  
 *  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 the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 *  02111-1307, USA.
 *  
 *  To contact the author, either e-mail me at woody@pandawave.com, 
 *	or write to
 *  
 *          William Edward Woody
 *          In Phase Consulting
 *          1545 Ard Eevin Avenue
 *          Glendale, CA 91202
 *
 *	Or visit us on the web at http://www.pandawave.com
 */

#include <stdio.h>
#include <stdlib.h>
#include "RCompile.h"
#include "lex.h"
#include "swap.h"

#if defined(macintosh)
#include <console.h>
#endif


/************************************************************************/
/*																		*/
/*	Sructures															*/
/*																		*/
/************************************************************************/

/*	TOCEntry
 *
 *		A single table of contents entry
 */

struct TOCEntry {
	struct TOCEntry *next;
	
	long			resType;
	long			resID;
	long			resLen;
};

/************************************************************************/
/*																		*/
/*	Globals																*/
/*																		*/
/************************************************************************/

static FILE *GOutData;
static TOCEntry *GList;

static unsigned char GBuffer[8192];				// 8K resource max...

/************************************************************************/
/*																		*/
/*	File Write															*/
/*																		*/
/************************************************************************/

/*	WriteHeader
 *
 *		Writes the header of the resource file
 */

static void WriteHeader(char *name)
{
	fprintf(GOutData,"/*  %s\n",name);
	fprintf(GOutData," *\n");
	fprintf(GOutData," *  Generated by RCompile\n");
	fprintf(GOutData," */\n\n");
	fprintf(GOutData,"#include <XDataUtil.h>\n\n");
	fprintf(GOutData,"#include <stdlib.h>\n\n");
	
	fprintf(GOutData,"/*  Internal Structures  */\n\n");
	fprintf(GOutData,"typedef struct {\n");
	fprintf(GOutData,"    long resType;\n");
	fprintf(GOutData,"    short resID;\n");
	fprintf(GOutData,"    long resLen;\n");
	fprintf(GOutData,"    unsigned char *resData;\n");
	fprintf(GOutData,"} TOCEntry;\n\n");
	
	fprintf(GOutData,"/*  Raw Resource Data */\n\n");
};

/*	WriteTOC
 *
 *		Write the table of contents
 */

static void WriteTOC(void)
{
	TOCEntry *foo;
	char buffer[6];
	
	fprintf(GOutData,"/*  TOC Data */\n\n");
	fprintf(GOutData,"static struct TOCEntry _GData[] = {\n");
	
	for (foo = GList; foo != NULL; foo = foo->next) {
		fprintf(GOutData,"    { ");
		
		*((long *)buffer) = foo->resType;
		buffer[4] = 0;
		
		fprintf(GOutData,"\'%s\', ",buffer);
		fprintf(GOutData,"%ld, %ld, ",foo->resID,foo->resLen);
		fprintf(GOutData,"_R%08lX_%04lX },\n",foo->resType,foo->resID);
	}
	
	fprintf(GOutData,"    { 0, 0, 0, NULL }\n");
	fprintf(GOutData,"};\n\n");
}

/*	WriteTail
 *
 *		Write the tail. This is a short subroutine which is used to
 *	look up resources
 */

static void WriteTail(void)
{
	fprintf(GOutData,"/*  Resource Search Routine  */\n\n");
	fprintf(GOutData,"/*  GetResourceData\n");
	fprintf(GOutData," *\n");
	fprintf(GOutData," *      Search _GData for the resource requested, and\n");
	fprintf(GOutData," *  return the pointer to the data. Return NULL if not found\n");
	fprintf(GOutData," */\n\n");
	fprintf(GOutData,"void *GetResourceData(long resType, short resID)\n");
	fprintf(GOutData,"{\n");
	fprintf(GOutData,"    int i;\n");
	fprintf(GOutData,"\n");
	fprintf(GOutData,"    for (i = 0; _GData[i].resData != NULL; i++) {\n");
	fprintf(GOutData,"        if ((_GData[i].resType == resType) && (_GData[i].resID == resID)) {\n");
	fprintf(GOutData,"            return (void *)(_GData[i].resData);\n");
	fprintf(GOutData,"        }\n");
	fprintf(GOutData,"    }\n");
	fprintf(GOutData,"    return NULL;  /* Not found */\n");
	fprintf(GOutData,"}\n\n");
}

/*	WriteResHeader
 *
 *		Write the header of the resource data file
 */

static void WriteResHeader(long resType, long resID)
{
	fprintf(GOutData,"static unsigned char _R%08lX_%04lX[] = {\n",resType,resID);
}

/*	WriteResTail
 *
 *		Write the tail of the resource data file
 */

static void WriteResTail(long resType, long resID)
{
	fprintf(GOutData,"}; /* _R%08lX_%04lX */\n\n",resType,resID);
}

/*	WriteResBuffer
 *
 *		Write a single line of data to the output buffer
 */

static void WriteResBuffer(long loc, unsigned char *data, short len, bool c)
{
	short i;
	
	fprintf(GOutData,"    ");
	for (i = 0; i < len; i++) {
		fprintf(GOutData,"0x%02X",data[i]);
		if (!c || (i < len-1)) fprintf(GOutData,", ");
		else fprintf(GOutData,"  ");
	}
	while (i < 8) {
		fprintf(GOutData,"      ");
		i++;
	}
	
	fprintf(GOutData,"/* %08lX: ",loc);
	for (i = 0; i < len; i++) {
		if ((data[i] > 32) && (data[i] <= 127) && (data[i] != '/')) {
			fprintf(GOutData,"%c",data[i]);
		} else {
			fprintf(GOutData,".");
		}
	}
	while (i < 8) {
		fprintf(GOutData," ");
		i++;
	}
	fprintf(GOutData," */\n");
}

/*	WriteResData
 *
 *		Given the input file and the number of bytes to write, this
 *	writes the data out
 */

static void WriteResData(long resType, long resID, long len, FILE *data)
{
	long off;
	long rlen;
	bool end;
	unsigned char buffer[8];
	TOCEntry *entry;
	
	WriteResHeader(resType,resID);
	
	off = 0;
	while (off < len) {
		rlen = len - off;
		if (rlen > 8) {
			rlen = 8;
		} else {
			end = true;
		}
		fread(buffer,1,rlen,data);
		WriteResBuffer(off,buffer,(short)rlen,end);
		off += rlen;
	}
	
	WriteResTail(resType,resID);
	
	/*
	 *	Add to TOC
	 */
	
	entry = new TOCEntry;
	entry->next = GList;
	GList = entry;
	
	entry->resType = resType;
	entry->resID = resID;
	entry->resLen = len;
}

/*	WriteResData
 *
 *		Given a data buffer and the number of bytes to write, this
 *	writes the data out
 */

static void WriteResData(long resType, long resID, long len, unsigned char *data)
{
	long off;
	long rlen;
	bool end;
	TOCEntry *entry;
	
	WriteResHeader(resType,resID);
	
	off = 0;
	while (off < len) {
		rlen = len - off;
		if (rlen > 8) {
			rlen = 8;
		} else {
			end = true;
		}
		WriteResBuffer(off,data + off,(short)rlen,end);
		off += rlen;
	}
	
	WriteResTail(resType,resID);
	
	/*
	 *	Add to TOC
	 */
	
	entry = new TOCEntry;
	entry->next = GList;
	GList = entry;
	
	entry->resType = resType;
	entry->resID = resID;
	entry->resLen = len;
}


/************************************************************************/
/*																		*/
/*	Handle inputs														*/
/*																		*/
/************************************************************************/

/*	ReadRData
 *
 *		Read an RData file
 */

static void ReadRData(FILE *fin)
{
	long type;
	long id;
	long len;
	
	for (;;) {
		if (0 >= fread(&type,1,4,fin)) break;		// end of RData file
		if (0 >= fread(&id,1,4,fin)) break;
		if (0 >= fread(&len,1,4,fin)) break;
		
		type = NSwapLong(type);
		id = NSwapLong(type);
		len = NSwapLong(len);
		
		WriteResData(type,id,len,fin);
	}
}

/*	ReadRawData
 *
 *		Read the data raw.
 */

static void ReadRawData(long type, long id, FILE *fin)
{
	long len;
	
	fseek(fin,0,SEEK_END);
	len = ftell(fin);
	fseek(fin,0,SEEK_SET);
	
	WriteResData(type,id,len,fin);
}


/*	ParseResSpec
 *
 *		This parses the resource specification file. There are two
 *	types of resources I can handle--raw and RData resources.
 */

static void ParseResSpec(char *in)
{
	int i;
	long resType;
	long resID;
	long resLen;
	FILE *f;
	
	InitLexEngine();
	ReadLexFile(in);
	WriteHeader(in);
	
	for (;;) {
		i = GetNextToken();
		if (i == -1) break;
		if (i == KTokenRaw) {
			/*
			 *	Next three params: 'XXXX' ### "data file"
			 */
			
			i = GetNextToken();
			if (i != KLexChar) {
				PrintError("Expected resource specification");
				exit(1);
			}
			resType = *((long *)GToken);
			
			i = GetNextToken();
			if (i != KLexInteger) {
				PrintError("Expected resource index");
				exit(1);
			}
			resID = GInteger;
			
			i = GetNextToken();
			if (i != KLexString) {
				PrintError("Expected file name");
				exit(1);
			}
			f = fopen(GToken,"rb");
			if (f == NULL) {
				PrintError("File %s cannot be read\n",GToken);
				exit(1);
			} else {
				ReadRawData(resType,resID,f);
				fclose(f);
			}
		} else if (i == KTokenRData) {
			/*
			 *	Next param: "data file"
			 */
			
			i = GetNextToken();
			if (i != KLexString) {
				PrintError("Expected file name");
				exit(1);
			}
			f = fopen(GToken,"rb");
			if (f == NULL) {
				PrintError("File %s cannot be read\n",GToken);
				exit(1);
			} else {
				ReadRData(f);
				fclose(f);
			}
		} else if (i == KTokenMenu) {
			i = GetNextToken();
			if (i != KLexInteger) {
				PrintError("Expected resource index");
				exit(1);
			}
			resID = GInteger;

			resLen = CompileMenuResource(GBuffer);
			if (resLen >= 0) {
				WriteResData('Menu',resID,resLen,GBuffer);
			}
		} else {
			PrintError("Unexpected token: %s\n",GToken);
			exit(1);
		}
	}
	EndLexEngine();
	
	/*
	 *	Dump TOC
	 */
	
	WriteTOC();
	WriteTail();
}


/************************************************************************/
/*																		*/
/*	Main entry point													*/
/*																		*/
/************************************************************************/

/*	main
 *
 *		Get arguments and figure out what to do.
 */

int main(int argc, char **argv)
{
#if defined(macintosh)
	argc = ccommand(&argv);
#endif

	if (argc != 3) {
		printf("Usage: %s <in> <out>\n",argv[0]);
		exit(1);
	}
	
	/*
	 *	Compile the resource specification list into a single .c
	 *	file with the resource data in it
	 */
	
	GOutData = fopen(argv[2],"w");
	if (GOutData == NULL) {
		printf("Unable to open %s for output\n",argv[2]);
		exit(1);
	}
	
	ParseResSpec(argv[1]);
	return 1;
}
