Description of the -listinfo output.
------------------------------------

Grammar:
--------
	start -> ENTRY*
	ENTRY -> ENTRYNAME ENTRYVALUE
	ENTRYNAME -> SYMBOL
	ENTRYVALUE -> SYMBOL | STRING | ENTRYLIST
	ENTRYLIST -> '(' ENTRY* ')'
	SYMBOL -> any char except separator, '"', '(', ')'
	STRING -> default C string in double quote ""

Any space (specifically any char which C function ispace() tells is a
space) is considered a separator and must be ignored.


Semantic:
---------
game (...)
Definition of all games emulated.

resource (...)
Definition of a resource. Actually the only two resource are
the NeoGeo BIOS roms (named 'neogeo') and the YM-3812 samples
(named 'ym3812').


Entries common for 'game' and 'resource'
----------------------------------------
name NAME
Name of the game or resource. This is the only required item.
The specified NAME is unique in all file.

description DESCRIPTION
Generic description.

manufacturer MANUFACTURER
Manufacturer of the game or resource.

year YEAR
Year of the game or resource.

romof NAME
The game or resource use rom of another game or resource named NAME.
[Equal to cloneof entry in GameDriver specification.]

sampleof NAME
The game or resource use sample of another game or resource named NAME.
[Is the first element with starting * in the samplenames array.]

rom '(' name NAME size SIZE crc CRC ')'
Rom specification his name, size and crc.

sample NAME
Sample specification.


Entries specific for 'game'
-------------------------
cloneof NAME
Clone-of relathionship. This is intended to express an abstract
clone-of relathionship.	[It's equal to the cloneof entry in gamedriver
specification with the exception of the neogeo game which is not reported
has a parent of any game.]

chip '(' type (cpu | audio) [flags audio] name NAME [clock CLOCK] ')'
List of hardware chips which the game use.
[A MAME cpu used for sound has "flags audio"]

video '(' (screen raster | vector) [x X y Y] colors NCOLORS freq NFREQ ')'
Many information of the game video hardware.

input '(' players NPLAYERS [control TYPE] [buttons NBUTTONS] coins NCOINS ')'
Many information of the game input hardware.

driver '(' status (preliminary | good) color (preliminary | imperfect | good)
  hiscore (preliminary | good) colordeep NCOLORBITS credits CREDITS ')'
Many information of the MAME driver status and requirements.

sound (channels NUMBER)
Number of the sound channels.


How to read the file
--------------------
If you have the necessity to read the output of -listinfo option
you must do attention of these issues:

1) You can't do any assumption of space indentation.
2) You can't do any assumption of max line length. All output can be
   in a very long line.
3) You can't do any assumption of order of the entries.
4) You can't do any assumption of the presence of any entry except the
   game.name and resource.name which are mandatory and unique.
5) You must skip entry unknow.
6) Spaces are required only for separing SYMBOL.
7) Lists of entry empty are valid.

For example:

	game(description"Pac Man (Midway)"unknowentry()name pacman rom(size 4096 name pacman.6e crc c1e6ab10))

is a valid output.

Example
-------
This is a complete module for reading info files.

info.h Library header
info.c Library module
test.c Example of use

-- info.h --------------------------------------------------------------------
#ifndef __INFO_H
#define __INFO_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h>

enum info_t {
	info_error,      /* Generic error in reading */
	info_eof,	 /* End of file got at valid position */
	info_symbol,	 /* Symbol */
	info_open,	 /* ( */
	info_close,	 /* ) */
	info_string      /* C string (automatically converted) */
};

void info_init(void);
void info_done(void);
const char* info_text_get(void);
enum info_t info_token_get(FILE* f);
enum info_t info_skip_value(FILE* f);
unsigned info_row_get(void);
unsigned info_col_get(void);
unsigned info_pos_get(void);

#ifdef __cplusplus
}
#endif

#endif
------------------------------------------------------------------------------

-- info.c --------------------------------------------------------------------
#include "info.h"

#include <ctype.h>
#include <string.h>
#include <malloc.h>
#include <assert.h>

/* Start size of buffer */
#define INFO_BUF_MIN 64

/* Buffer used for storing last token */
static unsigned info_buf_mac = 0;
static unsigned info_buf_max = 0;
static char* info_buf_map = 0;

/* Position in the stream */
static unsigned info_pos = 0; /* Char */
static unsigned info_row = 0; /* Row */
static unsigned info_col = 0; /* Column */

/* Inizialize the info reading system */
void info_init(void) {
	info_buf_max = 0;
	info_buf_map = 0;
	info_pos = 0;
	info_row = 0;
	info_col = 0;
}

/* Deinizialize the info reading system */
void info_done(void) {
	free(info_buf_map);
}

/* Get information of file position */
unsigned info_row_get(void) {
	return info_row;
}

unsigned info_col_get(void) {
	return info_col;
}

unsigned info_pos_get(void) {
	return info_pos;
}

/* Resize the buffer */
static void info_buf_resize(unsigned size) {
	if (!info_buf_max)
		info_buf_max = INFO_BUF_MIN;
	else
		info_buf_max *= 2;
	if (size > info_buf_max)
		info_buf_max = size;
	info_buf_map = realloc(info_buf_map, info_buf_max );
	assert(	info_buf_map );
}

/* Add a char to the buffer end */
static inline void info_buf_add(char c) {
	if (info_buf_mac >= info_buf_max)
		info_buf_resize(info_buf_mac + 1);
	info_buf_map[info_buf_mac++] = c;
}

/* Reset the buffer */
static void info_buf_reset() {
	info_buf_mac = 0;
}

/* Return last token text if is a info_symbol or a info_string token */
const char* info_text_get(void) {
	/* ensure the buffer end with zero */
	if (info_buf_mac==0 || info_buf_map[info_buf_mac-1]!=0)
		info_buf_add(0);
	return info_buf_map;
}

/* Read a char from file */
static int info_getc(FILE* f) {
	int c =	fgetc(f);
	switch (c) {
		case EOF:
			break;
		case '\n':
			info_col = 0;
			++info_row;
			++info_pos;
			break;
		default:
			++info_col;
			++info_pos;
			break;
	}
	return c;
}

/* Unget a char from file */
static void info_ungetc(int c, FILE* f) {
	--info_pos;
	--info_col;
	ungetc(c,f);
}

static enum info_t get_symbol(FILE* f,int c) {
	while (c!=EOF && !isspace(c) && c!='(' && c!=')' && c!='\"') {
		info_buf_add(c);
		c = info_getc(f);
	}
	/* no reason to unget space or EOF */
	if (c!=EOF && !isspace(c))
		info_ungetc(c,f);
	return info_symbol;
}

static unsigned hexdigit(char c) {
	if (isdigit(c))
		return c - '0';
	return toupper(c) - 'A' + 10;
}

static enum info_t get_string(FILE* f) {
	int c = info_getc(f);
	while (c!=EOF && c!='\"') {
		if (c=='\\') {
			c = info_getc(f);
			switch (c) {
				case 'a' : info_buf_add('\a'); break;
				case 'b' : info_buf_add('\b'); break;
				case 'f' : info_buf_add('\f'); break;
				case 'n' : info_buf_add('\n'); break;
				case 'r' : info_buf_add('\r'); break;
				case 't' : info_buf_add('\t'); break;
				case 'v' : info_buf_add('\v'); break;
				case '\\' : info_buf_add('\\'); break;
				case '?' : info_buf_add('\?'); break;
				case '\'' : info_buf_add('\''); break;
				case '\"' : info_buf_add('\"'); break;
				case 'x' : {
					int d0,d1;
					unsigned char cc;
					d0 = info_getc(f);
					if (!isxdigit(d0))
						return info_error;
					d1 = info_getc(f);
					if (!isxdigit(d1))
						return info_error;
					cc = hexdigit(d0) * 16 + hexdigit(d1);
					info_buf_add(cc);
				}
				break;
				default:
					info_buf_add('\\');
					info_buf_add(c);
				break;
			}
		} else {
			info_buf_add(c);
		}
		c = info_getc(f);
	}
	if (c!='\"')
		return info_error;
	return info_string;
}

/* Extract a token */
enum info_t info_token_get(FILE* f) {
	int c = info_getc(f);
	/* reset the buffer */
	info_buf_reset();
	/* skip space */
	while (c!=EOF && isspace(c)) {
		c = info_getc(f);
	}
	/* get token */
	switch (c) {
		case EOF:
			return info_eof;
		case '(':
			return info_open;
		case ')':
			return info_close;
		case '\"':
			return get_string(f);
		default:
			return get_symbol(f,c);
	}
}

/* Skip a value token
 * note:
 *   Skip recusively any info_open and info_close
 * return:
 *   info_error error
 *   otherwise last token skipped
 */
enum info_t info_skip_value(FILE* f) {
	/* read value token */
	enum info_t t = info_token_get(f);
	switch (t) {
		case info_open:
			t = info_token_get(f);
			if (t==info_error)
				return info_error;
			while (t!=info_close) {
				/* first read type as a symbol */
				if (t!=info_symbol)
					return info_error;
				/* second skip the value */
				t = info_skip_value(f);
				/* two value required */
				if (t==info_error)
					return info_error;
				/* read next token, a type or a info_close */
				t = info_token_get(f);
				if (t==info_error)
					return info_error;
			}
		break;
		case info_symbol:
		case info_string:
		break;
		default:
			return info_error;
	}
	return t;
}
------------------------------------------------------------------------------

-- test.c --------------------------------------------------------------------
#include "info.h"

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

#define true 1
#define false 0

int info_load(FILE* f) {
	enum info_t token = info_token_get(f);
	while (token!=info_eof) {
		if (token != info_symbol) return false;
		if (strcmp(info_text_get(),"game")==0) {
			if (info_token_get(f) != info_open) return false;
			token =	info_token_get(f);
			while (token != info_close) {
				if (token != info_symbol)
					return false;
				if (strcmp(info_text_get(),"name")==0) {
					if (info_token_get(f) != info_symbol) return false;
					printf("name %s\n", info_text_get() );
				} else if (strcmp(info_text_get(),"description")==0) {
					if (info_token_get(f) != info_string) return false;
					printf("description %s\n", info_text_get() );
				} else if (strcmp(info_text_get(),"manufacturer")==0) {
					if (info_token_get(f) != info_string) return false;
					printf("manufacturer %s\n", info_text_get() );
				} else if (strcmp(info_text_get(),"year")==0) {
					if (info_token_get(f) != info_symbol) return false;
					printf("year %s\n", info_text_get() );
				} else if (strcmp(info_text_get(),"cloneof")==0) {
					if (info_token_get(f) != info_symbol) return false;
					printf("cloneof %s\n", info_text_get() );
				} else if (strcmp(info_text_get(),"romof")==0) {
					if (info_token_get(f) != info_symbol) return false;
					printf("romof %s\n", info_text_get() );
				} else if (strcmp(info_text_get(),"sampleof")==0) {
					if (info_token_get(f) != info_symbol) return false;
					printf("sampleof %s\n", info_text_get() );
				} else if (strcmp(info_text_get(),"rom")==0) {
					if (info_token_get(f) != info_open)  return false;
					token =	info_token_get(f);
					while (token != info_close) {
						if (token != info_symbol) return false;
						if (strcmp(info_text_get(),"name")==0) {
							if (info_token_get(f) != info_symbol) return false;
							printf("romname %s\n", info_text_get() );
						} else if (strcmp(info_text_get(),"size")==0) {
							if (info_token_get(f) != info_symbol) return false;
							printf("romsize %s\n", info_text_get() );
						} else if (strcmp(info_text_get(),"crc")==0) {
							if (info_token_get(f) != info_symbol) return false;
							printf("romcrc %s\n", info_text_get() );
						} else {
							if (info_skip_value(f) == info_error) return false;
						}
						token = info_token_get(f);
					}
				} else if (strcmp(info_text_get(),"driver")==0) {
					if (info_token_get(f) != info_open)  return false;
					token =	info_token_get(f);
					while (token != info_close) {
						if (token != info_symbol) return false;
						if (strcmp(info_text_get(),"status")==0) {
							if (info_token_get(f) != info_symbol) return false;
							printf("driverstatus %s\n", info_text_get() );
						} else {
							if (info_skip_value(f) == info_error) return false;
						}
						token = info_token_get(f);
					}
				} else if (strcmp(info_text_get(),"sample")==0) {
					if (info_token_get(f) != info_symbol) return false;
					printf("samplenames %s\n", info_text_get() );
				} else {
					if (info_skip_value(f) == info_error) return false;
				}
				token = info_token_get(f);
			}
		} else {
			if (info_skip_value(f) == info_error) return false;
		}
		token = info_token_get(f);
	}

	return true;
}

int main() {
	info_init();

	if (!info_load(stdin)) {
		info_done();
		fprintf(stderr,"Error reading at row %d column %d\n",info_row_get()+1,info_col_get()+1);
		exit(EXIT_FAILURE);
	}

	info_done();

	return EXIT_SUCCESS;
}
------------------------------------------------------------------------------



