/* _____________________________________________________________________
**|                                                                     |
**| ParseDumpLine(line) - Parse a line of Empire dump output.           |
**|									|
**| 9/21/92 by Bill Frolik						|
**|                                                                     |
**| This routine uses Empire 'dump' output to construct corresponding	|
**| ship, sector, spy, army, and plane data structures.  Fed one line	|
**| at a time, it examines the dump header and field label lines to 	|
**| decide what kind of dump information it is about to receive, then   |
**| fills in and passes back appropriate data structures as subsequent  |
**| dump data lines come in.  Line data should come from the output     |
**| of 'dump', 'vdump', 'sdump', 'pdump', and 'adump' commands.		|
**|									|
**| The parsing algorithm does NOT depend on certain things being in    |
**| certain columns, nor is it require the pieces of data in any given  |
**| line to appear in a certain order (other than the order defined	|
**| by the field labels).						|
**|									|
**| Data structures are defined in 'dumpstr.h'; all of these structures |
**| are maintained as a union in this module (since only one type of	|
**| structure can be worked on at a time), and a pointer to this local  |
**| copy is what is passed back to the user.  The user should examine   |
**| and/or copy the data before calling this routine again, as each     |
**| call overwrites the local structure space.				|
**|								        |
**| The first byte of the structure defines the structure type, and	|
**| can be used to determine the type cast necessary to	access all of	|
**| the other elements in the structure.				|
**|									|
**| A null pointer is returned if a line does not contain dump data.    |
**|									|
**| EXAMPLE:								|
**|									|
**|	#include "dumpstr.h"						|
**|	char buf[1024];							|
**|	...								|
**|	while (fgets(buf,256,fin)) {					|
**|	    char *ptr = (char *)ParseDumpLine(buf);			|
**|	    if (!ptr) continue;						|
**|	    switch (*ptr) {						|
**|	    case SHIP: {						|
**|	        _shpstr *s = (_shpstr *)ptr;    			|
**|		printf("%s #%d\n",s->type,s->id);			|
**|		break;							|
**|	    }								|
**|	    case PLANE: {						|
**|	        _plnstr *s = (_plnstr *)ptr;    			|
**|		printf("%s #%d %d%%\n",s->type,s->id,s->effic);		|
**|		break;							|
**|	    }								|
**|	    case LAND: {						|
**|	        _sctstr *s = (_sctstr *)ptr;    			|
**|		printf("%c @ %d,%d own=%d\n",s->des,s->x,s->y,s->owner);|
**|		break;							|
**|	    }								|
**|	    case SPY: {							|
**|	        _spystr *s = (_spystr *)ptr;    			|
**|		printf("%s #%d @ %d,%d\n",s->type,s->id,s->x,s->y);	|
**|		break;							|
**|	    }								|
**|	    case ARMY: {						|
**|	        _armstr *s = (_armstr *)ptr;    			|
**|		printf("%s #%d mob=%d\n",s->type,s->id,s->mobil);	|
**|		break;							|
**|	    }								|
**|	    default:							|
**|		break;							|			
**|	    }								|
**|	}								|
**|_____________________________________________________________________|
*/

#include "dumpstr.h"

#define CHAR    1
#define INT     2
#define LONG    3
#define STR16   4
#define STR32   5
#define DOUBLE  6

static union {
    _shpstr	shp;
    _spystr	spy;
    _sctstr	sct;
    _plnstr	pln;
    _armstr	arm;
} rec;

#define	vp(fld) (&(rec.shp.fld))
#define	pp(fld) (&(rec.pln.fld))
#define	ap(fld) (&(rec.arm.fld))
#define	sp(fld) (&(rec.spy.fld))
#define	lp(fld) (&(rec.sct.fld))

static struct thingstr {
    char	*name;
    int		type;
    void	*shpptr,*plnptr,*armptr,*spyptr,*sctptr;
} thing[] = {

/*-------------------------------------------------------------------------------------------------*/
/*  THIS TABLE drives the parser.  It specifies all possible field names, how to interpret the	   */
/*  data in that field, and pointers to the various structure elements into which the data should  */
/*  be tranferred.  Feel free to modify this list as necessary to pick up new pieces of dump       */
/*  mation as they become available in future releases of the game.  Fields which appear in the    */
/*  dump output but not in this list will be appropriately ignored during the parsing process.     */
/*												   */
/*  DUMP        RECORD  SHIP/VEHICLE    PLANE           ARMY            SPY             SECTOR     */
/*  LABEL       SIZE    FIELD PTR       FIELD PTR       FIELD PTR       FIELD PTR       FIELD PTR  */
/*-------------------------------------------------------------------------------------------------*/

    "own",	INT,	vp(owner),	pp(owner),	ap(owner),	sp(owner),	lp(owner),
    "id",	INT,	vp(id),		pp(id),		ap(id),		sp(id),		0,
    "type",	STR32,	vp(type[0]),	pp(type[0]),	ap(type[0]),	sp(type[0]),	0,
    "x",	INT,   	vp(x),		pp(x),		ap(x),		sp(x),		lp(x),
    "y",	INT,	vp(y),		pp(y),		ap(y),		sp(y),		lp(y),
    "eff",	INT,   	vp(effic),	pp(effic),	ap(effic),	sp(effic),	lp(effic),
    "mob",	INT,	vp(mobil),	pp(mobil),	ap(mobil),	sp(mobil),	lp(mobil),
    "wing",	CHAR,   vp(fleet),	pp(wing),	ap(group),	sp(ring),	0,
    "tech",	INT,	vp(tech),	pp(tech),	ap(tech),	sp(tech),	0,
    "range",	INT,   	0,		pp(range),	0,		0,		0,
    "att",	INT,	0,		pp(att),	0,		0,		0,
    "def",	INT,   	0,		pp(def),	0,		0,		0,
    "harden",	INT,	0,		pp(harden),	0,		0,		0,
    "nuketype",	STR32,  0,		pp(nuketype[0]),0,		0,		0,
    "nukeamt",	INT,	0,		pp(nukeamt),	0,		0,		0,
    "gdr",	INT,   	0,		pp(gdr),	0,		0,		0,
    "armed",	INT,	0,		pp(armed),	0,		0,		0,
    "launched",	INT,   	0,		pp(launched),	0,		0,		0,
    "boxed",	INT,	0,		pp(boxed),	0,		0,		0,
    "longrange",INT,   	0,		pp(longrange),	0,		0,		0,
    "alt",	INT,	vp(alt_depth),	pp(alt),	0,		0,		0,
    "orbit",	STR16,  vp(orbit[0]),	pp(orbit[0]),	0,		0,		0,
    "ship",	INT,	vp(aboard),	pp(aboard),	ap(aboard),	sp(aboard),	0,
    "class",	STR16,  vp(class[0]),	0,		0,		0,		0,
    "name",	STR16,	vp(name[0]),	0,		ap(name[0]),	sp(name[0]),	0,
    "fleet",	CHAR,   vp(fleet),	0,		0,		0,		0,
    "convoy",	CHAR,	vp(convoy),	0,		0,		0,		0,
    "planes",	INT,   	vp(planes),	0,		0,		0,		0,
    "tanks",	INT,   	vp(tanks),	0,		0,		0,		0,
    "alt_depth",STR16,  vp(depth[0]),	0,		0,		0,		0,
    "air",	INT,   	vp(air),	0,		0,		0,		0,
    "destx",	INT,	vp(dest_x),	0,		0,		0,		0,
    "desty",	INT,   	vp(dest_y),	0,		0,		0,		0,
    "autonav",	INT,	vp(autonav),	0,		0,		0,		0,
    "standby",	INT,   	vp(standby),	0,		0,		0,		0,
    "aboard",	INT,	vp(aboard),	pp(aboard),	ap(aboard),	sp(aboard),	0,
    "ring",	CHAR,   0,		0,		0,		sp(ring),	0,
    "edu",	INT,   	0,		0,		0,		sp(edu),	0,
    "hap",	INT,    0,		0,		0,		sp(hap),	0,
    "age",	INT,   	0,		0,		0,		sp(age),	0,
    "pilot",	INT,	0,		0,		0,		sp(pilot_tech),	0,
    "acc",	INT,   	0,		0,		ap(acc),	sp(acc),	0,
    "morale",	INT,	0,		0,		ap(morale),	0,		0,
    "des",	CHAR,   0,		0,		0,		0,		lp(des),
    "sdes",	CHAR,   0,		0,		0,		0,		lp(newdes),
    "*",	CHAR,   0,		0,		0,		0,		lp(occupied),
    "min",	INT,	0,		0,		0,		0,		lp(m_res),
    "gold",	INT,   	0,		0,		0,		0,		lp(g_res),
    "fert",	INT,	0,		0,		0,		0,		lp(f_res),
    "ocontent",	INT,   	0,		0,		0,		0,		lp(o_res),
    "uran",	INT,	0,		0,		0,		0,		lp(u_res),
    "work",	INT,   	0,		0,		0,		0,		lp(work),
    "avail",	INT,	0,		0,		0,		0,		lp(avail),
    "terr",	INT,   	0,		0,		0,		0,		lp(terr),
    "bunker",	INT,	0,		0,		0,		0,		lp(bunker_size),
    "road",	INT,	0,		0,		0,		0,		lp(road_imp),
    "rail",	INT,	0,		0,		0,		0,		lp(rail_imp),
    "dist_path",STR32,	0,		0,		0,		0,		lp(dist_path[0]),
    "dist_x",	INT,	0,		0,		0,		0,		lp(dist_x),
    "dist_y",	INT,	0,		0,		0,		0,		lp(dist_y),
    "civ",	INT,	vp(amt.c),	0,		ap(amt.c),	sp(amt.c),	lp(amt.c),
    "mil",	INT,	vp(amt.m),	0,		ap(amt.m),	sp(amt.m),	lp(amt.m),
    "uw",	INT,	vp(amt.u),	0,		ap(amt.u),	sp(amt.u),	lp(amt.u),
    "shell",	INT,	vp(amt.s),	0,		ap(amt.s),	sp(amt.s),	lp(amt.s),
    "gun",	INT,	vp(amt.g),	0,		ap(amt.g),	sp(amt.g),	lp(amt.g),
    "pet",	INT,	vp(amt.p),	0,		ap(amt.p),	sp(amt.p),	lp(amt.p),
    "iron",	INT,	vp(amt.i),	0,		ap(amt.i),	sp(amt.i),	lp(amt.i),
    "dust",	INT,	vp(amt.d),	0,		ap(amt.d),	sp(amt.d),	lp(amt.d),
    "bar",	INT,	vp(amt.b),	0,		ap(amt.b),	sp(amt.b),	lp(amt.b),
    "food",	INT,	vp(amt.f),	0,		ap(amt.f),	sp(amt.f),	lp(amt.f),
    "oil",	INT,	vp(amt.o),	0,		ap(amt.o),	sp(amt.o),	lp(amt.o),
    "lcm",	INT,	vp(amt.l),	0,		ap(amt.l),	sp(amt.l),	lp(amt.l),
    "hcm",	INT,	vp(amt.h),	0,		ap(amt.h),	sp(amt.h),	lp(amt.h),
    "rad",	INT,	vp(amt.r),	0,		ap(amt.r),	sp(amt.r),	lp(amt.r),
    "vacc",	INT,	vp(amt.v),	0,		ap(amt.v),	sp(amt.v),	lp(amt.v),
    "c_amt",	INT,	vp(amt.c),	0,		ap(amt.c),	sp(amt.c),	lp(amt.c),
    "m_amt",	INT,	vp(amt.m),	0,		ap(amt.m),	sp(amt.m),	lp(amt.m),
    "u_amt",	INT,	vp(amt.u),	0,		ap(amt.u),	sp(amt.u),	lp(amt.u),
    "s_amt",	INT,	vp(amt.s),	0,		ap(amt.s),	sp(amt.s),	lp(amt.s),
    "g_amt",	INT,	vp(amt.g),	0,		ap(amt.g),	sp(amt.g),	lp(amt.g),
    "p_amt",	INT,	vp(amt.p),	0,		ap(amt.p),	sp(amt.p),	lp(amt.p),
    "i_amt",	INT,	vp(amt.i),	0,		ap(amt.i),	sp(amt.i),	lp(amt.i),
    "d_amt",	INT,	vp(amt.d),	0,		ap(amt.d),	sp(amt.d),	lp(amt.d),
    "b_amt",	INT,	vp(amt.b),	0,		ap(amt.b),	sp(amt.b),	lp(amt.b),
    "f_amt",	INT,	vp(amt.f),	0,		ap(amt.f),	sp(amt.f),	lp(amt.f),
    "o_amt",	INT,	vp(amt.o),	0,		ap(amt.o),	sp(amt.o),	lp(amt.o),
    "l_amt",	INT,	vp(amt.l),	0,		ap(amt.l),	sp(amt.l),	lp(amt.l),
    "h_amt",	INT,	vp(amt.h),	0,		ap(amt.h),	sp(amt.h),	lp(amt.h),
    "r_amt",	INT,	vp(amt.r),	0,		ap(amt.r),	sp(amt.r),	lp(amt.r),
    "v_amt",	INT,	vp(amt.v),	0,		ap(amt.v),	sp(amt.v),	lp(amt.v),
    "c_del",	INT,	0,		0,		0,		0,		lp(del.c),
    "m_del",	INT,	0,		0,		0,		0,		lp(del.m),
    "u_del",	INT,	0,		0,		0,		0,		lp(del.u),
    "f_del",	INT,   	0,		0,		0,		0,		lp(del.f),
    "s_del",	INT,	0,		0,		0,		0,		lp(del.s),
    "g_del",	INT,   	0,		0,		0,		0,		lp(del.g),
    "p_del",	INT,	0,		0,		0,		0,		lp(del.p),
    "i_del",	INT,   	0,		0,		0,		0,		lp(del.i),
    "d_del",	INT,	0,		0,		0,		0,		lp(del.d),
    "b_del",	INT,   	0,		0,		0,		0,		lp(del.b),
    "o_del",	INT,	0,		0,		0,		0,		lp(del.o),
    "l_del",	INT,   	0,		0,		0,		0,		lp(del.l),
    "h_del",	INT,	0,		0,		0,		0,		lp(del.h),
    "r_del",	INT,   	0,		0,		0,		0,		lp(del.r),
    "v_del",	INT,	0,		0,		0,		0,		lp(del.v),
    "c_cut",	INT,   	0,		0,		0,		0,		lp(cut.c),
    "m_cut",	INT,   	0,		0,		0,		0,		lp(cut.m),
    "u_cut",	INT,   	0,		0,		0,		0,		lp(cut.u),
    "f_cut",	INT,	0,		0,		0,		0,		lp(cut.f),
    "s_cut",	INT,   	0,		0,		0,		0,		lp(cut.s),
    "g_cut",	INT,	0,		0,		0,		0,		lp(cut.g),
    "p_cut",	INT,   	0,		0,		0,		0,		lp(cut.p),
    "i_cut",	INT,	0,		0,		0,		0,		lp(cut.i),
    "d_cut",	INT,   	0,		0,		0,		0,		lp(cut.d),
    "b_cut",	INT,	0,		0,		0,		0,		lp(cut.b),
    "o_cut",	INT,   	0,		0,		0,		0,		lp(cut.o),
    "l_cut",	INT,	0,		0,		0,		0,		lp(cut.l),
    "h_cut",	INT,   	0,		0,		0,		0,		lp(cut.h),
    "r_cut",	INT,	0,		0,		0,		0,		lp(cut.r),
    "v_cut",	INT,   	0,		0,		0,		0,		lp(cut.v),
    "c_dist",	INT,   	0,		0,		0,		0,		lp(dist.c),
    "m_dist",	INT,   	0,		0,		0,		0,		lp(dist.m),
    "u_dist",	INT,   	0,		0,		0,		0,		lp(dist.u),
    "f_dist",	INT,	0,		0,		0,		0,		lp(dist.f),
    "s_dist",	INT,   	0,		0,		0,		0,		lp(dist.s),
    "g_dist",	INT,	0,		0,		0,		0,		lp(dist.g),
    "p_dist",	INT,   	0,		0,		0,		0,		lp(dist.p),
    "i_dist",	INT,	0,		0,		0,		0,		lp(dist.i),
    "d_dist",	INT,   	0,		0,		0,		0,		lp(dist.d),
    "b_dist",	INT,	0,		0,		0,		0,		lp(dist.b),
    "o_dist",	INT,   	0,		0,		0,		0,		lp(dist.o),
    "l_dist",	INT,	0,		0,		0,		0,		lp(dist.l),
    "h_dist",	INT,   	0,		0,		0,		0,		lp(dist.h),
    "r_dist",	INT,	0,		0,		0,		0,		lp(dist.r),
    "v_dist",	INT,   	0,		0,		0,		0,		lp(dist.v),

    0,0,0,0,0,0,0,0
    };

#define MaxItems ((sizeof(thing)/sizeof(struct thingstr))-1)

static int		NumItems;
static char		DumpType;
static char		State = 0;
static struct thingstr  *Item[MaxItems];

void *ParseDumpLine(line)
char *line;
{
    double f;
    int i,j,k,slen;
    char *strstr();
    char *s,*w;
    char c,strbuf[64];
    struct thingstr *u,*t;

    if (!line) {
	State = 0;
	return 0;
    }

    switch (State) {

    case 0: /* STATE 0 - line should be empty, timestamp, header, or garbage */

	if (s=strstr(line,":")) {
	    if (s=strstr(s+1,":")) {
		while (*s) s++;
		while (*s <= ' ') s--;
		s[1] = '\0';
		strcpy(rec.shp.TIME,line);
		return 0;
	    }
	}
	if (strstr(line,"DUMP")) {
	    if (strstr(line,"SE"))      DumpType = LAND;
	    else if (strstr(line,"PL")) DumpType = PLANE;
	    else if (strstr(line,"SP")) DumpType = SPY;
	    else if (strstr(line,"SH")) DumpType = SHIP;
	    else if (strstr(line,"VE")) DumpType = SHIP;
	    else if (strstr(line,"AR")) DumpType = ARMY;
	    else			DumpType = UNKNOWN;
	    State = 1;
	}
	return 0;

    case 1: /* STATE 1 - line should contain data descriptors */

	for (w=line; *w && *w<=' ';) w++;
	for (i=j=NumItems=0; *w && NumItems<MaxItems; NumItems++) {
	    for (t=thing; t->name; t++) { /* Is this token in the list? */
		i = strlen(t->name);
		if (!strncmp(w,t->name,i) && w[i]<=' ') break;
	    }
	    if (t->name) { /* If yes, point at it's list entry */
		Item[NumItems] = t;
		j = 1;
		w += i;
	    } else { /* If not in list, give it a null pointer */
		Item[NumItems] = 0;
		while (*w && *w>' ') w++;
	    }
	    while (*w && *w<=' ') w++;
	}
	State = j>0 ? 2 : 0; /* Reject line if no valid tokens */
	return 0;

    case 2: /* STATE 2 - line should contain data */

	rec.shp.DUMPTYPE = DumpType;
	for (s=line, i=0; i<NumItems; i++) {
	    while (*s && *s<=' ') s++;
	    if (!*s) { /* Insufficient data; reject line */
		State=0; 
		return 0;
	    }
	    slen = 16;
	    switch ((t=Item[i])->type) {
	    case INT:
		k = atoi(s);
		switch (DumpType) {
		case SHIP: if (t->shpptr) *((int *)(t->shpptr)) = k; break;
		case PLANE:if (t->plnptr) *((int *)(t->plnptr)) = k; break;
		case ARMY: if (t->armptr) *((int *)(t->armptr)) = k; break;
		case SPY:  if (t->spyptr) *((int *)(t->spyptr)) = k; break;
		case LAND: if (t->sctptr) *((int *)(t->sctptr)) = k; break;
		}
		break;
	    case DOUBLE:
		f = atof(s);
		switch (DumpType) {
		case SHIP: if (t->shpptr) *((double *)(t->shpptr)) = f; break;
		case PLANE:if (t->plnptr) *((double *)(t->plnptr)) = f; break;
		case ARMY: if (t->armptr) *((double *)(t->armptr)) = f; break;
		case SPY:  if (t->spyptr) *((double *)(t->spyptr)) = f; break;
		case LAND: if (t->sctptr) *((double *)(t->sctptr)) = f; break;
		}
		break;
	    case CHAR:
		if ((c = *s) == '_') c = ' ';
		switch (DumpType) {
		case SHIP: if (t->shpptr) *((char *)(t->shpptr)) = c; break;
		case PLANE:if (t->plnptr) *((char *)(t->plnptr)) = c; break;
		case ARMY: if (t->armptr) *((char *)(t->armptr)) = c; break;
		case SPY:  if (t->spyptr) *((char *)(t->spyptr)) = c; break;
		case LAND: if (t->sctptr) *((char *)(t->sctptr)) = c; break;
		}
		break;
	    case STR32:  slen = 32; /* fall into STR16 */
	    case STR16:
		if (j = (*s=='\"')) s++;
		for (w=strbuf, k=slen; *s; s++) {
		    if ((j && *s=='\"') || (!j && *s<=' ')) break;
		    if (k-- > 1) *w++ = *s;
		}
		*w = '\0';
		if (j && *s!='\"') { /* No closing quote; reject line */
		    State = 0;
		    return 0;
		}
		switch (DumpType) {
		case SHIP: if (t->shpptr) strcpy(t->shpptr,strbuf); break;
		case PLANE:if (t->plnptr) strcpy(t->plnptr,strbuf); break;
		case ARMY: if (t->armptr) strcpy(t->armptr,strbuf); break;
		case SPY:  if (t->spyptr) strcpy(t->spyptr,strbuf); break;
		case LAND: if (t->sctptr) strcpy(t->sctptr,strbuf); break;
		}
	    default:
		break;
	    }
	    while (*s && *s>' ') s++; /* skip to blank char */
	}
	return ((void *)&(rec.shp));
    }
}
