/* wdb2text - derived from domap.c
	By Dave Pape, dave.pape@acm.org, 6 December 2000
    Program to convert compressed CIA World Data Bank II files into
    simpler, plain text files.
    The original files store positions as integer arc seconds; this
    will write them out as floating point degrees, so there may be
    some loss of accuracy.
*/
#include <stdio.h>
#include <math.h>
#include <strings.h>

/*
 * Much of this program was written by Brian Reid at the Dec
 * Western Research Lab. The rest was written by Joe Dellinger
 * at Stanford University. Neither of us much cares what becomes
 * of it. Sat Apr  1 01:26:51 PST 1989
 */

/* ----------------------------------------------------------------------
 * Read and process one binary map file.
 */

#include <sys/file.h>

#define SHORTFLAG 0x4000
#define CBD_MAGIC 0x20770002
#define BIT32 int
#define BIT16 short


struct cbdhead
{
    BIT32           magic;	/* Magic number */
    BIT32           dictaddr;	/* Offset of segment dictionary in file */
    BIT32           segcount;	/* Number of segments in file */
    BIT32           segsize;	/* Size of segment dictionary */
    BIT32           segmax;	/* Size of largest segment's strokes, /2 */
    BIT32           fill[(40 / sizeof (BIT32)) - 5];	/* Filler */
};

main (argc, argv)
    int             argc;
    char          **argv;
{
int             Ifile, segcount, idx, idy, segbufsize, olt, oln, j, k, jstroke, iseg;
BIT32           i32;
BIT16          *segbuf;
double          x, y, z, x1, x2, xc, y1, y2, yc, z1, z2, zc;

struct segdict
{
    BIT32           segid;
    BIT32           maxlat, minlat, maxlong, minlong;
    BIT32           absaddr;
    BIT16           nbytes;
    BIT16           rank;
}              *sd, *sdbuf;

struct segment
{
    BIT32           orgx, orgy;
    BIT32           id;
    BIT16           nstrokes;
    BIT16           dummy;
}               sb;

struct cbdhead  header;

	Ifile = open (argv[1], O_RDONLY, 0);

	if (Ifile == -1)
	{
		perror(argv[1]);
		exit(1);
	}

/*
 * Read in the file header, check it for the correct magic number,
 * and learn the address of the segment dictionary
 */
	read (Ifile, &header, sizeof header);

	twiddle32 (&(header.magic));
	twiddle32 (&(header.dictaddr));
	twiddle32 (&(header.segcount));
	twiddle32 (&(header.segsize));
	twiddle32 (&(header.segmax));

	if (header.magic != CBD_MAGIC)
	{
	    fprintf (stderr, "File has bad magic number %d != %d\n",
		     header.magic, CBD_MAGIC);
	    exit (2);
	}

/* allocate space for the segment buffer */
	segbufsize = 2 * header.segmax;
	segbuf = (BIT16 *) malloc (50 + segbufsize);

/* allocate space for the segment dictionary */
	sdbuf = (struct segdict *) malloc (100 + header.segsize);
	sd = sdbuf;
	sd++;

/* Go read in the segment dictionary (it's at the end of the file) */
	lseek (Ifile, header.dictaddr, L_SET);
	j = read (Ifile, sd, header.segsize);

	if (j < header.segsize)
	{
	    fprintf (stderr, "File segment dictionary expecting %d got %d\n",
		     header.segsize, j);
	    close (Ifile);
	    free (sdbuf);
	    free (segbuf);
	    return;
	}

	segcount = header.segcount;

	sd = sdbuf;
	for (iseg = 1; iseg <= segcount; iseg++)
	{
	    sd++;		/* does this really work? wow! */

	    twiddle32 (&(sd->segid));
	    twiddle32 (&(sd->maxlat));
	    twiddle32 (&(sd->minlat));
	    twiddle32 (&(sd->maxlong));
	    twiddle32 (&(sd->minlong));
	    twiddle32 (&(sd->absaddr));
	    twiddle16 (&(sd->nbytes));
	    twiddle16 (&(sd->rank));

	    lseek (Ifile, sd->absaddr, L_SET);
	    read (Ifile, &sb, sizeof sb);
	    twiddle32 (&(sb.orgx));
	    twiddle32 (&(sb.orgy));
	    twiddle32 (&(sb.id));
	    twiddle16 (&(sb.nstrokes));

	    if (sd->nbytes > segbufsize)
	    {
		fprintf (stderr, "Segment %d needs %d bytes; buffer limit is %d.\n", iseg, sd->nbytes, segbufsize);
		close (Ifile);
		free (sdbuf);
		free (segbuf);
		return;
	    }

	    printf("segment %d  rank %d  points %d\n", iseg, sd->rank, sb.nstrokes+1);

	    read (Ifile, segbuf, sd->nbytes);

	    k = 0;

	    oln = sb.orgx;
	    olt = sb.orgy;

	    printf("\t%f %f\n",olt/3600.0,oln/3600.0);

	    for (jstroke = 1; jstroke <= sb.nstrokes; jstroke++)
	    {
		twiddle16 (&segbuf[k]);
		if (segbuf[k] & SHORTFLAG)
		{
/* Flag bit on: unpack a 16-bit field into dx and dy */
		    i32 = segbuf[k++];
		    if (i32 > 0)
			i32 &= ~SHORTFLAG;
		    idy = i32 & 0xFF;
		    if (idy & 0x80)
			idy |= ~0xFF;	/* extend sign */
		    idx = i32 >> 8;
		    if (idx & 0x80)
			idx |= ~0xBF;	/* extend sign */
		}
		else
		{
/* Flag bit off: take dx and dy from 32-bit fields. */
		    idx = segbuf[k++];
		    if (idx < 0)
			idx |= SHORTFLAG;
		    twiddle16 (&segbuf[k]);
		    idx = (idx << 16) | segbuf[k];
		    k++;

		    twiddle16 (&segbuf[k]);
		    idy = segbuf[k];
		    k++;
		    if (idy < 0)
			idy |= SHORTFLAG;
		    twiddle16 (&segbuf[k]);
		    idy = (idy << 16) | segbuf[k];
		    k++;
		}
		olt = olt + idy;
		oln = oln + idx;

		printf("\t%f %f\n",olt/3600.0,oln/3600.0);
	    }
	}
	close (Ifile);
	free (sdbuf);
	free (segbuf);
}

/*
 * Change the two routines below to accomodate the byte ordering on your
 * machine! - Joe Dellinger
 */

twiddle32 (i32)
    BIT32          *i32;
{
#if 0
BIT32           temp;

    temp = 0;
    temp |= 0xFF000000 & ((0x000000FF & *i32) << 24);
    temp |= 0x00FF0000 & ((0x0000FF00 & *i32) << 8);
    temp |= 0x0000FF00 & ((0x00FF0000 & *i32) >> 8);
    temp |= 0x000000FF & ((0xFF000000 & *i32) >> 24);
    *i32 = temp;
#endif
}

twiddle16 (i16)
    BIT16          *i16;
{
#if 0
BIT16           temp;

    temp = 0;
    temp |= 0xFF00 & ((0x00FF & *i16) << 8);
    temp |= 0x00FF & ((0xFF00 & *i16) >> 8);
    *i16 = temp;
#endif
}
