// XMIInspect header reading

#include "stdafx.h"

/* read a global XMI header 
 *
 * structure:
 *  sint32 magic + version + globalwhitespace flag
 *  slist  path expressions
 */
int XMIInspect::readGlobalHeader()
{
	int stat = 0;
	char isneg;

	if (level >= XMIINS_REPORT_INFO) {
		printf("checking XMI global header\n");
	}
	bytesleft = len - (long)curptr + (long)buffer;

	/* check the magic & version */
	magic = Load::SInt32(curptr, &isneg);
	version = (magic - MAGIC_KEY_BASE) & 0xffff;
	if (level >= XMIINS_REPORT_MININFO) {
		printf("read magic 0x%08x, version = %ld.%ld\n", magic, version >> 8, version & 0xff);
	}
	if (magic>>16 != MAGIC_KEY_HIGH) {
		if (level >= XMIINS_REPORT_ERRORS) {
			printf("magic is incorrect!\n");
		}
		stat = XMIINS_ERR_MAGIC;
		goto cleanup;
	}
   if (version > XMILL_CURRENT_VERSION) {
		if (level >= XMIINS_REPORT_ERRORS) {
			printf("version %04x is unknown!\n", version);
		}
		stat = XMIINS_ERR_VERSION;
		goto cleanup;
	}

	/* report global whitespace flag */
	if (level >= XMIINS_REPORT_INFO) {
		printf("global whitespace ignore flag: %s\n", isneg ? "on" : "off");
	}

   if (version >= XMILL_VERSION_1_0) {
   	int bla = Load::UInt32(curptr);
   }

   /* print all path expressions and build container info. The last two will be empty 
		since they are the default expressions //# and /, which always use the text 't'
	   semantic (un)compressor. */
	if (level >= XMIINS_REPORT_MOREINFO) {
		printf("path expressions:");
	}
	printslist(curptr, false, &exprs, &numexprs);
	if ((stat = buildExprInfo(exprs, numexprs)) != 0) {
		goto cleanup;
	}

	/* do some administration */
	globalheadersize = bytesleft;
	bytesleft = len - (long)curptr + (long)buffer;
	globalheadersize -= bytesleft;
	if (level >= XMIINS_REPORT_INFO) {
		printf("global header size: %ld Bytes\n", globalheadersize);
	}

cleanup:
	return stat;
}

/* read a run header
 *
 * structure:
 *  uint32    datasize (calculated by the compressor in an implementation dependent way!)
 *  list      container blocks
 *   uint32   index
 *   list     containers in this block
 *    uint32  size
 * slist      labels
 * list		  global blocks
 *  uint32	  index
 *  uint32    state size
 * for all globals with state size < SMALLCOMPRESS_THRESHOLD (1024):
 *  'index' times an lstring
 * for all containers in all container blocks with size < SMALLCONT_THRESHOLD (2000):
 *  'size' Bytes of data. This data is compressor dependent and will not be reported or checked.
 *
 * the datasize is an addition of 
 *  1. all container block sizes added together, and 
 *  2. the addition of the result of evaluating an implementation dependent function for each global block, namely:
 *		sizeof(EnumDictItem)*index[j] +          // The size of the dictionary directoy
 *		WordAlignUInt(statesize[j]  				  // The size of the string space (word-aligned)
 *    sizeof(EnumDictItem) is 16, the alignment is on a 4-Byte boundary
 */
int XMIInspect::readRunHeader()
{
	int stat = 0;

	bytesleft = len - (long)curptr + (long)buffer;
	if (level >= XMIINS_REPORT_MOREINFO) {
		printf("run header # %ld contains %ld Bytes\n", numheaders+1, bytesleft);
	}

	/* read total datasize as stored in the XMI */
	if ((unsigned long)(datasize = Load::UInt32(curptr)) > buflen) {
		/* allocate larger buffer */
		buflen = datasize;
		realloc = true;
	}
	totaldatasize += datasize;

	/* read container info */
	if ((stat = readContainerInfo()) != 0) {
		goto cleanup;
	}

	/* print & store all labels */
	if (level >= XMIINS_REPORT_MOREINFO) {
		printf("labels:");
	}
	printslist(curptr, true, &labels, &numlabels, true);

	/* get # global data blocks */
	numglobals = Load::UInt32(curptr);
	if (level >= XMIINS_REPORT_SOMEINFO) {
		printf("# global data blocks: %ld\n", numglobals);
	}

#if 0
	/* allocate mem for dictionary sizes */
	dictionarysizes = new long[numglobals];
	for (int i=0; i<numglobals; i++) {
		dictionarysizes[i] = 0;
	}
#endif

	/* set enum indexes */
	if ((stat = matchEnumIndexes()) != 0) {
		goto cleanup;
	}

	/* header data done, do some size administration */
	headersize = preheadersize + (long)curptr;
	totalheadersize += headersize;
	numheaders++;
	bytesleft = len - (long)curptr + (long)buffer;
	if (level >= XMIINS_REPORT_INFO) {
		printf("run header #%ld size: %ld Bytes, %ld Bytes left in small data (global + containers)\n", 
			numheaders, headersize - (numbuffers == 0 ? globalheadersize : 0), bytesleft);
	}

	/* read small global blocks */
	if ((stat = readSmallGlobalBlock()) != 0) {
		goto cleanup;
	}

	/* read small containers */
	if ((stat = readSmallContainerBlock()) != 0) {
		goto cleanup;
	}

	/* check if all data was consumed */
	bytesleft = len - (long)curptr + (long)buffer;
	if (bytesleft != 0) {
		if (level >= XMIINS_REPORT_WARNINGS) {
			printf("run header now contains %ld Bytes, should be 0!\n", bytesleft);
		}
	} else if (level >= XMIINS_REPORT_MOREINFO) {
		printf ("run header is OK\n");
	}

	/* go to next state */
	if (numlargeglobals > 0) {
		/* there are global data blocks */
		curglobal = 0;
		state = XMIINS_READ_GLOBAL_BLOCK;
	} else if (expectedcontainers > 0) {
		/* there are only large container blocks, so skip the XMIINS_READ_GLOBAL_BLOCK state */
		state = XMIINS_READ_CONTAINER_BLOCK;
		curcontainer = 0;
		curblock = 0;
	} else {
		/* no large data at all, this run only contains a header */
		if ((stat = endThisRun()) != 0) {
			goto cleanup;
		}
	}

cleanup:
	return stat;
}
