/*
 * Copyright (c) Tony Bybell 2009-2018.
 *
 * 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.
 */

#include <config.h>
#include "globals.h"
#include <stdio.h>
#include "fstapi.h"
#include "lx2.h"

#include <unistd.h>

#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include "symbol.h"
#include "vcd.h"
#include "lxt.h"
#include "lxt2_read.h"
#include "vzt_read.h"
#include "fstapi.h"
#include "debug.h"
#include "busy.h"
#include "hierpack.h"
#include "fst.h"


#define FST_RDLOAD "FSTLOAD | "

/******************************************************************/

/*
 * doubles going into histent structs are NEVER freed so this is OK..
 * (we are allocating as many entries that fit in 4k minus the size of the two
 * bookkeeping void* pointers found in the malloc_2/free_2 routines in
 * debug.c)
 */
#ifdef _WAVE_HAVE_JUDY
#define FST_DOUBLE_GRANULARITY ( (4*1024) / sizeof(double) )
#else
#define FST_DOUBLE_GRANULARITY ( ( (4*1024)-(2*sizeof(void *)) ) / sizeof(double) )
#endif

#ifndef WAVE_HAS_H_DOUBLE
static void *double_slab_calloc(void)
{
if(GLOBALS->double_curr_fst==GLOBALS->double_fini_fst)
        {
        GLOBALS->double_curr_fst=(double *)calloc_2(FST_DOUBLE_GRANULARITY, sizeof(double));
        GLOBALS->double_fini_fst=GLOBALS->double_curr_fst+FST_DOUBLE_GRANULARITY;
        }

return((void *)(GLOBALS->double_curr_fst++));
}
#endif

/*
 * reverse equality mem compare
 */
static int memrevcmp(int i, const char *s1, const char *s2)
{
i--;
for(;i>=0;i--)
        {
        if(s1[i] != s2[i]) break;
        }

return(i+1);
}



/*
 * fast itoa for decimal numbers
 */
static char* itoa_2(int value, char* result)
{
char* ptr = result, *ptr1 = result, tmp_char;
int tmp_value;

do {
        tmp_value = value;
        value /= 10;
        *ptr++ = "9876543210123456789" [9 + (tmp_value - value * 10)];
} while ( value );

if (tmp_value < 0) *ptr++ = '-';
result = ptr;
*ptr-- = '\0';
while(ptr1 < ptr) {
        tmp_char = *ptr;
        *ptr--= *ptr1;
        *ptr1++ = tmp_char;
}
return(result);
}


/*
 * preformatted sprintf statements which remove parsing latency
 */
static int sprintf_2_sd(char *s, char *c, int d)
{
char *s2 = s;

while(*c)
        {
        *(s2++) = *(c++);
        }
*(s2++) = '[';
s2 = itoa_2(d, s2);
*(s2++) = ']';
*s2 = 0;

return(s2 - s);
}


static int sprintf_2_sdd(char *s, char *c, int d, int d2)
{
char *s2 = s;

while(*c)
        {
        *(s2++) = *(c++);
        }
*(s2++) = '[';
s2 = itoa_2(d, s2);
*(s2++) = ':';
s2 = itoa_2(d2, s2);
*(s2++) = ']';
*s2 = 0;

return(s2 - s);
}

/******************************************************************/


static struct fstHier *extractNextVar(void *xc, int *msb, int *lsb, char **nam, int *namlen, unsigned int *nnam_max)
{
struct fstHier *h;
const char *pnts;
char *pnt, *pntd, *lb_last = NULL, *col_last = NULL, *rb_last = NULL;
int acc;
char *s;
unsigned char ttype;
int sdt = FST_SDT_NONE;
int svt = FST_SVT_NONE;
long sxt = 0;

while((h = fstReaderIterateHier(xc)))
        {
        switch(h->htyp)
                {
                case FST_HT_SCOPE:
                        GLOBALS->fst_scope_name = fstReaderPushScope(xc, h->u.scope.name, GLOBALS->mod_tree_parent);
			GLOBALS->fst_scope_name_len = fstReaderGetCurrentScopeLen(xc);

			switch(h->u.scope.typ)
				{
				case FST_ST_VCD_MODULE:		ttype = TREE_VCD_ST_MODULE; break;
				case FST_ST_VCD_TASK:		ttype = TREE_VCD_ST_TASK; break;
				case FST_ST_VCD_FUNCTION:	ttype = TREE_VCD_ST_FUNCTION; break;
				case FST_ST_VCD_BEGIN:		ttype = TREE_VCD_ST_BEGIN; break;
				case FST_ST_VCD_FORK:		ttype = TREE_VCD_ST_FORK; break;
				case FST_ST_VCD_GENERATE:	ttype = TREE_VCD_ST_GENERATE; break;
				case FST_ST_VCD_STRUCT:		ttype = TREE_VCD_ST_STRUCT; break;
				case FST_ST_VCD_UNION:		ttype = TREE_VCD_ST_UNION; break;
				case FST_ST_VCD_CLASS:		ttype = TREE_VCD_ST_CLASS; break;
				case FST_ST_VCD_INTERFACE:	ttype = TREE_VCD_ST_INTERFACE; break;
				case FST_ST_VCD_PACKAGE:	ttype = TREE_VCD_ST_PACKAGE; break;
				case FST_ST_VCD_PROGRAM:	ttype = TREE_VCD_ST_PROGRAM; break;

				case FST_ST_VHDL_ARCHITECTURE:	ttype = TREE_VHDL_ST_ARCHITECTURE; break;
				case FST_ST_VHDL_PROCEDURE:	ttype = TREE_VHDL_ST_PROCEDURE;    break;
				case FST_ST_VHDL_FUNCTION:	ttype = TREE_VHDL_ST_FUNCTION;     break;
				case FST_ST_VHDL_RECORD:	ttype = TREE_VHDL_ST_RECORD;       break;
				case FST_ST_VHDL_PROCESS:	ttype = TREE_VHDL_ST_PROCESS;      break;
				case FST_ST_VHDL_BLOCK:		ttype = TREE_VHDL_ST_BLOCK;        break;
				case FST_ST_VHDL_FOR_GENERATE:	ttype = TREE_VHDL_ST_GENFOR;       break;
				case FST_ST_VHDL_IF_GENERATE:	ttype = TREE_VHDL_ST_GENIF;        break;
				case FST_ST_VHDL_GENERATE:	ttype = TREE_VHDL_ST_GENERATE;     break;
				case FST_ST_VHDL_PACKAGE:	ttype = TREE_VHDL_ST_PACKAGE;      break;

				default:			ttype = TREE_UNKNOWN; break;
				}

			allocate_and_decorate_module_tree_node(ttype, h->u.scope.name, h->u.scope.component, h->u.scope.name_length, h->u.scope.component_length,
				GLOBALS->stem_valid ? GLOBALS->stem_struct_base_siz : 0,
				GLOBALS->istem_valid ? GLOBALS->istem_struct_base_siz : 0);
			GLOBALS->stem_valid = GLOBALS->istem_valid = 0;
                        break;
                case FST_HT_UPSCOPE:
			GLOBALS->mod_tree_parent = fstReaderGetCurrentScopeUserInfo(xc);
                        GLOBALS->fst_scope_name = fstReaderPopScope(xc);
			GLOBALS->fst_scope_name_len = fstReaderGetCurrentScopeLen(xc);
                        break;

                case FST_HT_VAR:
                        /* GLOBALS->fst_scope_name = fstReaderGetCurrentFlatScope(xc); */
			/* GLOBALS->fst_scope_name_len = fstReaderGetCurrentScopeLen(xc); */

			if(h->u.var.name_length > (*nnam_max))
				{
				free_2(*nam); *nam = malloc_2(((*nnam_max) = h->u.var.name_length) + 1);
				}
			s = *nam;
			pnts = h->u.var.name;
			pntd = s;

			int esc_sm = 0;
			while(*pnts)
				{
				if(*pnts == '\\') esc_sm = 1;
				if(*pnts != ' ')
					{
					if(*pnts == '[' && !esc_sm) { lb_last = pntd; col_last = NULL; rb_last = NULL; }
					else if(*pnts == ':' && lb_last != NULL && col_last == NULL && rb_last == NULL && !esc_sm) { col_last = pntd; }
					else if(*pnts == ']' && lb_last != NULL && rb_last == NULL && !esc_sm) { rb_last = pntd; }
					else if(lb_last != NULL && rb_last == NULL && (isdigit((int)(unsigned char)*pnts) || (*pnts == '-')) && !esc_sm) { }
					else { lb_last = NULL; col_last = NULL; rb_last = NULL; }

					*(pntd++) = *pnts;
					}
				else
					{
					esc_sm = 0;
					}
				pnts++;
				}
			*pntd = 0; *namlen = pntd - s;

			if(!rb_last)
				{
				col_last = NULL;
				lb_last = NULL;
				}

			if(!lb_last)
				{
				*msb = *lsb = -1;
				}
			else if	((h->u.var.length > 1) && !col_last && lb_last) /* add for NC arrays that don't add final explicit bitrange like VCS does */
				{
				lb_last = NULL;
				*msb = h->u.var.length -1;
				*lsb = 0;
				}
			else
				{
				int sgna = 1, sgnb = 1;
				pnt = lb_last + 1;
				acc = 0;
				while(isdigit((int)(unsigned char)*pnt) || (*pnt == '-'))
					{
					if(*pnt != '-')
						{
						acc *= 10;
						acc += (*pnt - '0');
						}
						else
						{
						sgna = -1;
						}
					pnt++;
					}

				*msb = acc * sgna;
				if(!col_last)
					{
					*lsb = acc;
					}
				else
					{
					pnt = col_last + 1;
					acc = 0;
					while(isdigit((int)(unsigned char)*pnt) || (*pnt == '-'))
						{
						if(*pnt != '-')
							{
							acc *= 10;
							acc += (*pnt - '0');
							}
							else
							{
							sgnb = -1;
							}
						pnt++;
						}
					*lsb = acc * sgnb;
					}
				}

			if(lb_last)
				{
				*lb_last = 0;
				if((lb_last - s) < (*namlen))
					{
					*namlen = lb_last - s;
					}
				}
			*nam = s;

			h->u.var.svt_workspace = svt;
			h->u.var.sdt_workspace = sdt;
			h->u.var.sxt_workspace = sxt;

			return(h);
                        break;

		case FST_HT_ATTRBEGIN:	/* currently ignored for most cases except VHDL variable vartype/datatype creation */
			if(h->u.attr.typ == FST_AT_MISC)
				{
				if(h->u.attr.subtype == FST_MT_SUPVAR)
					{
					if(h->u.attr.name)
						{
						JRB subvar_jrb_node;
						char *attr_pnt;

						if(GLOBALS->fst_filetype == FST_FT_VHDL)
							{
							char *lc_p = attr_pnt = strdup_2(h->u.attr.name);

							while(*lc_p)
								{
								*lc_p = tolower(*lc_p); /* convert attrib name to lowercase for VHDL */
								lc_p++;
								}
							}
							else
							{
							attr_pnt = NULL;
							}

						/* sxt points to actual type name specified in FST file */
						subvar_jrb_node = jrb_find_str(GLOBALS->subvar_jrb, attr_pnt ? attr_pnt : h->u.attr.name);
						if(subvar_jrb_node)
							{
							sxt = subvar_jrb_node->val.ui;
							}
							else
							{
							Jval jv;

							if(GLOBALS->subvar_jrb_count != WAVE_VARXT_MAX_ID)
								{
								sxt = jv.ui = ++GLOBALS->subvar_jrb_count;
								/* subvar_jrb_node = */ jrb_insert_str(GLOBALS->subvar_jrb, strdup_2(attr_pnt ? attr_pnt : h->u.attr.name), jv);
								}
								else
								{
								sxt = 0;
								if(!GLOBALS->subvar_jrb_count_locked)
									{
									fprintf(stderr, FST_RDLOAD"Max number (%d) of type attributes reached, please increase WAVE_VARXT_MAX_ID.\n", WAVE_VARXT_MAX_ID);
									GLOBALS->subvar_jrb_count_locked = 1;
									}
								}
							}

						if(attr_pnt)
							{
							free_2(attr_pnt);
							}
						}

					svt = h->u.attr.arg >> FST_SDT_SVT_SHIFT_COUNT;
					sdt = h->u.attr.arg & (FST_SDT_ABS_MAX-1);
					GLOBALS->supplemental_datatypes_encountered = 1;
					GLOBALS->supplemental_vartypes_encountered |= ((svt != FST_SVT_NONE) && (svt != FST_SVT_VHDL_SIGNAL));
					}
				else
				if(h->u.attr.subtype == FST_MT_SOURCEISTEM)
					{
					uint32_t istem_path_number = (uint32_t)h->u.attr.arg_from_name;
					uint32_t istem_line_number = (uint32_t)h->u.attr.arg;

					if(istem_path_number <= GLOBALS->stem_path_string_table_siz) /* prevent overflows from malformed writers */
						{
						GLOBALS->istem_valid = 1;
						if(!GLOBALS->istem_struct_base)
							{
							GLOBALS->istem_struct_base_siz_alloc = 1;
							GLOBALS->istem_struct_base_siz = 0;
							GLOBALS->istem_struct_base = malloc_2(GLOBALS->istem_struct_base_siz_alloc * sizeof(struct stem_struct_t));
							}

						if(GLOBALS->istem_struct_base_siz == GLOBALS->istem_struct_base_siz_alloc)
							{
							GLOBALS->istem_struct_base_siz_alloc *= 2;
							GLOBALS->istem_struct_base = realloc_2(GLOBALS->istem_struct_base, GLOBALS->istem_struct_base_siz_alloc * sizeof(struct stem_struct_t));
							}

						GLOBALS->istem_struct_base[GLOBALS->istem_struct_base_siz].stem_idx = istem_path_number - 1;
						GLOBALS->istem_struct_base[GLOBALS->istem_struct_base_siz].stem_line_number = istem_line_number;
						GLOBALS->istem_struct_base_siz++;
						}
					}
				else
				if(h->u.attr.subtype == FST_MT_SOURCESTEM)
					{
					uint32_t stem_path_number = (uint32_t)h->u.attr.arg_from_name;
					uint32_t stem_line_number = (uint32_t)h->u.attr.arg;

					if(stem_path_number <= GLOBALS->stem_path_string_table_siz) /* prevent overflows from malformed writers */
						{
						GLOBALS->stem_valid = 1;
						if(!GLOBALS->stem_struct_base)
							{
							GLOBALS->stem_struct_base_siz_alloc = 1;
							GLOBALS->stem_struct_base_siz = 0;
							GLOBALS->stem_struct_base = malloc_2(GLOBALS->stem_struct_base_siz_alloc * sizeof(struct stem_struct_t));
							}

						if(GLOBALS->stem_struct_base_siz == GLOBALS->stem_struct_base_siz_alloc)
							{
							GLOBALS->stem_struct_base_siz_alloc *= 2;
							GLOBALS->stem_struct_base = realloc_2(GLOBALS->stem_struct_base, GLOBALS->stem_struct_base_siz_alloc * sizeof(struct stem_struct_t));
							}

						GLOBALS->stem_struct_base[GLOBALS->stem_struct_base_siz].stem_idx = stem_path_number - 1;
						GLOBALS->stem_struct_base[GLOBALS->stem_struct_base_siz].stem_line_number = stem_line_number;
						GLOBALS->stem_struct_base_siz++;
						}
					}
				else
				if(h->u.attr.subtype == FST_MT_PATHNAME)
					{
					if(h->u.attr.name && ((GLOBALS->stem_path_string_table_siz+1) == h->u.attr.arg))
						{
						/* == check against h->u.attr.arg is a sanity check against the writer */
						if(!GLOBALS->stem_path_string_table)
							{
							GLOBALS->stem_path_string_table_alloc = 1;
							GLOBALS->stem_path_string_table_siz = 0;
							GLOBALS->stem_path_string_table = malloc_2(GLOBALS->stem_path_string_table_alloc * sizeof(char *));
							}

						if(GLOBALS->stem_path_string_table_siz == GLOBALS->stem_path_string_table_alloc)
							{
							GLOBALS->stem_path_string_table_alloc *= 2;
							GLOBALS->stem_path_string_table = realloc_2(GLOBALS->stem_path_string_table, GLOBALS->stem_path_string_table_alloc * sizeof(char *));
							}

						GLOBALS->stem_path_string_table[GLOBALS->stem_path_string_table_siz] = strdup_2(h->u.attr.name);
						GLOBALS->stem_path_string_table_siz++;
						}
					}
				else
				if(h->u.attr.subtype == FST_MT_VALUELIST)
					{
					if(h->u.attr.name)
						{
						/* format is concatenations of [m b xs xe valstring] */
						if(GLOBALS->fst_synclock_str) 
							{
							free_2(GLOBALS->fst_synclock_str);			
							}
						GLOBALS->fst_synclock_str = strdup_2(h->u.attr.name);
						}
					}
				else
				if(h->u.attr.subtype == FST_MT_ENUMTABLE)
					{
					if(h->u.attr.name)
						{
						GLOBALS->queued_xl_enum_filter = h->u.attr.arg; /* consumed by next enum variable definition */

						if(h->u.attr.name_length)
							{
							struct fstETab *fe = fstUtilityExtractEnumTableFromString(h->u.attr.name);
							/* currently fe->name is unused */
							if(fe)
								{
								uint32_t ie;
#ifdef _WAVE_HAVE_JUDY
								Pvoid_t e = (Pvoid_t) NULL;
								for(ie=0; ie<fe->elem_count; ie++)
									{
									PPvoid_t pv = JudyHSIns(&e, fe->val_arr[ie], strlen(fe->val_arr[ie]), NULL);
									if(*pv)
										{
										free_2(*pv);
										}
									*pv = (void *)strdup_2(fe->literal_arr[ie]);
									}
#else
								struct xl_tree_node *e = NULL;

								for(ie=0; ie<fe->elem_count; ie++)
									{
									e = xl_insert(fe->val_arr[ie], e, fe->literal_arr[ie]);
									}
#endif

								if(GLOBALS->xl_enum_filter)
									{
									GLOBALS->num_xl_enum_filter++;
									GLOBALS->xl_enum_filter = realloc_2(GLOBALS->xl_enum_filter, GLOBALS->num_xl_enum_filter * sizeof(struct xl_tree_node *));
									}
									else
									{
									GLOBALS->num_xl_enum_filter++;
									GLOBALS->xl_enum_filter = malloc_2(sizeof(struct xl_tree_node *));
									}

								GLOBALS->xl_enum_filter[GLOBALS->num_xl_enum_filter-1] = e;

								if((unsigned int)GLOBALS->num_xl_enum_filter != h->u.attr.arg)
									{
									fprintf(stderr, FST_RDLOAD"Internal error, nonsequential enum tables definition encountered, exiting.\n");
									exit(255);
									}

								fstUtilityFreeEnumTable(fe);
								}
							}
						}
					}
				}
			break;

		case FST_HT_ATTREND:
			break;

		default:
			break;
                }
        }

*namlen = 0;
*nam = NULL;
return(NULL);
}


static void fst_append_graft_chain(int len, char *nam, int which, struct tree *par)
{
struct tree *t = talloc_2(sizeof(struct tree) + len + 1);

memcpy(t->name, nam, len+1);
t->t_which = which;

t->child = par;
t->next = GLOBALS->terminals_tchain_tree_c_1;
GLOBALS->terminals_tchain_tree_c_1 = t;
}


/*
 * mainline
 */
TimeType fst_main(char *fname, char *skip_start, char *skip_end)
{
int i;
struct Node *n;
struct symbol *s, *prevsymroot=NULL, *prevsym=NULL;
signed char scale;
int numalias = 0;
int numvars = 0;
struct symbol *sym_block = NULL;
struct Node *node_block = NULL;
struct fstHier *h = NULL;
int msb, lsb;
char *nnam = NULL;
uint32_t activity_idx, num_activity_changes;
struct tree *npar = NULL;
char **f_name = NULL;
int   *f_name_len = NULL, *f_name_max_len = NULL;
int allowed_to_autocoalesce;
unsigned int nnam_max = 0;

int f_name_build_buf_len = 128;
char *f_name_build_buf = malloc_2(f_name_build_buf_len + 1);

GLOBALS->fst_fst_c_1 = fstReaderOpen(fname);
if(!GLOBALS->fst_fst_c_1)
        {
	return(LLDescriptor(0));	/* look at GLOBALS->fst_fst_c_1 in caller for success status... */
        }
/* SPLASH */                            splash_create();

allowed_to_autocoalesce = (strstr(fstReaderGetVersionString(GLOBALS->fst_fst_c_1), "Icarus") == NULL);
if(!allowed_to_autocoalesce)
	{
	GLOBALS->autocoalesce = 0;
	}

scale=(signed char)fstReaderGetTimescale(GLOBALS->fst_fst_c_1);
exponent_to_time_scale(scale);

f_name = calloc_2(F_NAME_MODULUS+1,sizeof(char *));
f_name_len = calloc_2(F_NAME_MODULUS+1,sizeof(int));
f_name_max_len = calloc_2(F_NAME_MODULUS+1,sizeof(int));

nnam_max = 16;
nnam = malloc_2(nnam_max + 1);

GLOBALS->fst_filetype = fstReaderGetFileType(GLOBALS->fst_fst_c_1);
if(GLOBALS->fst_filetype == FST_FT_VHDL)
	{
	GLOBALS->is_vhdl_component_format = 1;
	}

GLOBALS->subvar_jrb = make_jrb(); /* only used for attributes such as generated in VHDL, etc. */
GLOBALS->synclock_jrb = make_jrb(); /* only used for synthetic clocks */

GLOBALS->numfacs=fstReaderGetVarCount(GLOBALS->fst_fst_c_1);
GLOBALS->mvlfacs_fst_c_3=(struct fac *)calloc_2(GLOBALS->numfacs,sizeof(struct fac));
GLOBALS->fst_table_fst_c_1=(struct lx2_entry *)calloc_2(GLOBALS->numfacs, sizeof(struct lx2_entry));
sym_block = (struct symbol *)calloc_2(GLOBALS->numfacs, sizeof(struct symbol));
node_block=(struct Node *)calloc_2(GLOBALS->numfacs,sizeof(struct Node));
GLOBALS->facs=(struct symbol **)malloc_2(GLOBALS->numfacs*sizeof(struct symbol *));
GLOBALS->mvlfacs_fst_alias = calloc_2(GLOBALS->numfacs,sizeof(fstHandle));
GLOBALS->mvlfacs_fst_rvs_alias = calloc_2(GLOBALS->numfacs,sizeof(fstHandle));

if(!GLOBALS->fast_tree_sort)
	{
	GLOBALS->do_hier_compress = 0;
	}
	else
	{
	hier_auto_enable(); /* enable if greater than threshold */
	}

init_facility_pack();

fprintf(stderr, FST_RDLOAD"Processing %d facs.\n", GLOBALS->numfacs);
/* SPLASH */                            splash_sync(1, 5);

GLOBALS->first_cycle_fst_c_3 = (TimeType) fstReaderGetStartTime(GLOBALS->fst_fst_c_1) * GLOBALS->time_scale;
GLOBALS->last_cycle_fst_c_3 = (TimeType) fstReaderGetEndTime(GLOBALS->fst_fst_c_1) * GLOBALS->time_scale;
GLOBALS->total_cycles_fst_c_3 = GLOBALS->last_cycle_fst_c_3 - GLOBALS->first_cycle_fst_c_3 + 1;
GLOBALS->global_time_offset = fstReaderGetTimezero(GLOBALS->fst_fst_c_1) * GLOBALS->time_scale;

/* blackout region processing */
num_activity_changes = fstReaderGetNumberDumpActivityChanges(GLOBALS->fst_fst_c_1);
for(activity_idx = 0; activity_idx < num_activity_changes; activity_idx++)
	{
	uint32_t activity_idx2;
	uint64_t ct = fstReaderGetDumpActivityChangeTime(GLOBALS->fst_fst_c_1, activity_idx);
	unsigned char ac = fstReaderGetDumpActivityChangeValue(GLOBALS->fst_fst_c_1, activity_idx);

	if(ac == 1) continue;
	if((activity_idx+1) == num_activity_changes)
		{
		struct blackout_region_t *bt = calloc_2(1, sizeof(struct blackout_region_t));
		bt->bstart = (TimeType)(ct * GLOBALS->time_scale);
		bt->bend = (TimeType)(GLOBALS->last_cycle_fst_c_3 * GLOBALS->time_scale);
                bt->next = GLOBALS->blackout_regions;

                GLOBALS->blackout_regions = bt;

		/* activity_idx = activity_idx2; */ /* scan-build says is dead + assigned garbage value , which is true : code does not need to mirror for() loop below */
		break;
		}

	for(activity_idx2 = activity_idx+1; activity_idx2 < num_activity_changes; activity_idx2++)
		{
		uint64_t ct2 = fstReaderGetDumpActivityChangeTime(GLOBALS->fst_fst_c_1, activity_idx2);
		ac = fstReaderGetDumpActivityChangeValue(GLOBALS->fst_fst_c_1, activity_idx2);
		if((ac == 0) && (activity_idx2 == (num_activity_changes-1)))
			{
			ac = 1;
			ct2 = GLOBALS->last_cycle_fst_c_3;
			}

		if(ac == 1)
			{
			struct blackout_region_t *bt = calloc_2(1, sizeof(struct blackout_region_t));
			bt->bstart = (TimeType)(ct * GLOBALS->time_scale);
			bt->bend = (TimeType)(ct2 * GLOBALS->time_scale);
	                bt->next = GLOBALS->blackout_regions;

	                GLOBALS->blackout_regions = bt;

			activity_idx = activity_idx2;
			break;
			}
		}

	}


/* do your stuff here..all useful info has been initialized by now */

if(!GLOBALS->hier_was_explicitly_set)    /* set default hierarchy split char */
        {
        GLOBALS->hier_delimeter='.';
        }

for(i=0;i<GLOBALS->numfacs;i++)
        {
	char buf[65537];
	char *str;
	struct fac *f;
	int hier_len, name_len, tlen;
	unsigned char nvt, nvd, ndt;
	int longest_nam_candidate = 0;
	char *fnam;
	int len_subst = 0;

	h = extractNextVar(GLOBALS->fst_fst_c_1, &msb, &lsb, &nnam, &name_len, &nnam_max);
	if(!h)
		{
		/* this should never happen */
		fstReaderIterateHierRewind(GLOBALS->fst_fst_c_1);
		h = extractNextVar(GLOBALS->fst_fst_c_1, &msb, &lsb, &nnam, &name_len, &nnam_max);
		if(!h)
			{
		        fprintf(stderr, FST_RDLOAD"Exiting, missing or malformed names table encountered.\n");
        		exit(255);
			}
		}

	npar = GLOBALS->mod_tree_parent;
	hier_len = GLOBALS->fst_scope_name ? GLOBALS->fst_scope_name_len : 0;
	if(hier_len)
		{
		tlen = hier_len + 1 + name_len;
		if(tlen > f_name_max_len[i&F_NAME_MODULUS])
			{
			if(f_name[i&F_NAME_MODULUS]) free_2(f_name[i&F_NAME_MODULUS]);
			f_name_max_len[i&F_NAME_MODULUS] = tlen;
			fnam = malloc_2(tlen + 1);
			}
			else
			{
			fnam = f_name[i&F_NAME_MODULUS];
			}

		memcpy(fnam, GLOBALS->fst_scope_name, hier_len);
		fnam[hier_len] = GLOBALS->hier_delimeter;
		memcpy(fnam + hier_len + 1, nnam, name_len + 1);
		}
		else
		{
		tlen = name_len;
		if(tlen > f_name_max_len[i&F_NAME_MODULUS])
			{
			if(f_name[i&F_NAME_MODULUS]) free_2(f_name[i&F_NAME_MODULUS]);
			f_name_max_len[i&F_NAME_MODULUS] = tlen;
			fnam = malloc_2(tlen + 1);
			}
			else
			{
			fnam = f_name[i&F_NAME_MODULUS];
			}

		memcpy(fnam, nnam, name_len + 1);
		}

	f_name[i&F_NAME_MODULUS] = fnam;
	f_name_len[i&F_NAME_MODULUS] = tlen;

        if((h->u.var.length > 1) && (msb == -1) && (lsb == -1))
		{
		node_block[i].msi = h->u.var.length - 1;
		node_block[i].lsi = 0;
		}
		else
		{
		unsigned int abslen = (msb >= lsb) ? (msb - lsb + 1) : (lsb - msb + 1);

		if((h->u.var.length > abslen) && !(h->u.var.length % abslen)) /* check if 2d array */
			{
			/* printf("h->u.var.length: %d, abslen: %d '%s'\n", h->u.var.length, abslen, fnam); */
			len_subst = 1;
			}

		node_block[i].msi = msb;
		node_block[i].lsi = lsb;
		}
	GLOBALS->mvlfacs_fst_c_3[i].len = h->u.var.length;

	if(h->u.var.length)
		{
		switch(h->u.var.direction)
			{
			case FST_VD_INPUT:		nvd = ND_DIR_IN; GLOBALS->nonimplicit_direction_encountered = 1; break;
			case FST_VD_OUTPUT:		nvd = ND_DIR_OUT; GLOBALS->nonimplicit_direction_encountered = 1; break;
			case FST_VD_INOUT:		nvd = ND_DIR_INOUT; GLOBALS->nonimplicit_direction_encountered = 1; break;
			case FST_VD_BUFFER:		nvd = ND_DIR_BUFFER; GLOBALS->nonimplicit_direction_encountered = 1; break;
			case FST_VD_LINKAGE:		nvd = ND_DIR_LINKAGE; GLOBALS->nonimplicit_direction_encountered = 1; break;

			case FST_VD_IMPLICIT:
			default:			nvd = ND_DIR_IMPLICIT; break;
			}

		switch(h->u.var.typ)
			{
	                case FST_VT_VCD_EVENT: 		nvt = ND_VCD_EVENT; break;
	                case FST_VT_VCD_INTEGER: 	nvt = ND_VCD_INTEGER; break;
	                case FST_VT_VCD_PARAMETER: 	nvt = ND_VCD_PARAMETER; break;
	                case FST_VT_VCD_REAL: 		nvt = ND_VCD_REAL; break;
	                case FST_VT_VCD_REAL_PARAMETER: nvt = ND_VCD_REAL_PARAMETER; break;
	                case FST_VT_VCD_REALTIME:       nvt = ND_VCD_REALTIME; break;
	                case FST_VT_VCD_REG: 		nvt = ND_VCD_REG; break;
	                case FST_VT_VCD_SUPPLY0: 	nvt = ND_VCD_SUPPLY0; break;
	                case FST_VT_VCD_SUPPLY1: 	nvt = ND_VCD_SUPPLY1; break;
	                case FST_VT_VCD_TIME: 		nvt = ND_VCD_TIME; break;
	                case FST_VT_VCD_TRI: 		nvt = ND_VCD_TRI; break;
	                case FST_VT_VCD_TRIAND: 	nvt = ND_VCD_TRIAND; break;
	                case FST_VT_VCD_TRIOR: 		nvt = ND_VCD_TRIOR; break;
	                case FST_VT_VCD_TRIREG: 	nvt = ND_VCD_TRIREG; break;
	                case FST_VT_VCD_TRI0: 		nvt = ND_VCD_TRI0; break;
	                case FST_VT_VCD_TRI1: 		nvt = ND_VCD_TRI1; break;
	                case FST_VT_VCD_WAND: 		nvt = ND_VCD_WAND; break;
	                case FST_VT_VCD_WIRE: 		nvt = ND_VCD_WIRE; break;
	                case FST_VT_VCD_WOR: 		nvt = ND_VCD_WOR; break;
	                case FST_VT_VCD_PORT: 		nvt = ND_VCD_PORT; break;

	                case FST_VT_GEN_STRING:		nvt = ND_GEN_STRING; break;

			case FST_VT_SV_BIT:             nvt = ND_SV_BIT; break;
			case FST_VT_SV_LOGIC:           nvt = ND_SV_LOGIC; break;
			case FST_VT_SV_INT:             nvt = ND_SV_INT; break;
			case FST_VT_SV_SHORTINT:        nvt = ND_SV_SHORTINT; break;
			case FST_VT_SV_LONGINT:         nvt = ND_SV_LONGINT; break;
			case FST_VT_SV_BYTE:            nvt = ND_SV_BYTE; break;
			case FST_VT_SV_ENUM:            nvt = ND_SV_ENUM; break;
			case FST_VT_SV_SHORTREAL:       nvt = ND_SV_SHORTREAL; break;

			default: 			nvt = ND_UNSPECIFIED_DEFAULT; break;
			}

		switch(h->u.var.typ)
			{
			case FST_VT_VCD_PARAMETER:
			case FST_VT_VCD_INTEGER:
			case FST_VT_SV_INT:
			case FST_VT_SV_SHORTINT:
			case FST_VT_SV_LONGINT:
				GLOBALS->mvlfacs_fst_c_3[i].flags = VZT_RD_SYM_F_INTEGER;
				break;

			case FST_VT_VCD_REAL:
			case FST_VT_VCD_REAL_PARAMETER:
			case FST_VT_VCD_REALTIME:
			case FST_VT_SV_SHORTREAL:
				GLOBALS->mvlfacs_fst_c_3[i].flags = VZT_RD_SYM_F_DOUBLE;
				break;

			case FST_VT_GEN_STRING:
				GLOBALS->mvlfacs_fst_c_3[i].flags = VZT_RD_SYM_F_STRING;
				GLOBALS->mvlfacs_fst_c_3[i].len = 2;
				break;

			default:
				GLOBALS->mvlfacs_fst_c_3[i].flags = VZT_RD_SYM_F_BITS;
				break;
			}
		}
		else /* convert any variable length records into strings */
		{
		nvt = ND_GEN_STRING;
		nvd = ND_DIR_IMPLICIT;
		GLOBALS->mvlfacs_fst_c_3[i].flags = VZT_RD_SYM_F_STRING;
		GLOBALS->mvlfacs_fst_c_3[i].len = 2;
		}

	if(GLOBALS->fst_synclock_str)
		{
		if(GLOBALS->mvlfacs_fst_c_3[i].len == 1) /* currently only for single bit signals */
			{
			Jval syn_jv;

			GLOBALS->mvlfacs_fst_c_3[i].flags |= VZT_RD_SYM_F_SYNVEC; /* special meaning for this in FST loader--means synthetic signal! */
			syn_jv.s = GLOBALS->fst_synclock_str;
			jrb_insert_int(GLOBALS->synclock_jrb, i, syn_jv);
			}
			else
			{
			free_2(GLOBALS->fst_synclock_str);
			}

		GLOBALS->fst_synclock_str = NULL; /* under malloc_2() control for true if() branch, so not lost */
		}

	if(h->u.var.is_alias)
		{
		GLOBALS->mvlfacs_fst_c_3[i].node_alias = h->u.var.handle - 1; /* subtract 1 to scale it with gtkwave-style numbering */
		GLOBALS->mvlfacs_fst_c_3[i].flags |= VZT_RD_SYM_F_ALIAS;
		numalias++;
		}
	else
		{
		GLOBALS->mvlfacs_fst_rvs_alias[numvars] = i;
		GLOBALS->mvlfacs_fst_c_3[i].node_alias = numvars;
		numvars++;
		}

	f=GLOBALS->mvlfacs_fst_c_3+i;

	if((f->len>1)&& (!(f->flags&(VZT_RD_SYM_F_INTEGER|VZT_RD_SYM_F_DOUBLE|VZT_RD_SYM_F_STRING))) )
		{
		int len=sprintf_2_sdd(buf, f_name[(i)&F_NAME_MODULUS],node_block[i].msi, node_block[i].lsi);

		if(len_subst) /* preserve 2d in name, but make 1d internally */
			{
			node_block[i].msi = h->u.var.length-1;
			node_block[i].lsi = 0;
			}

		longest_nam_candidate = len;

		if(!GLOBALS->do_hier_compress)
			{
			str=malloc_2(len+1);
			}
			else
			{
			if(len > f_name_build_buf_len)
				{
				free_2(f_name_build_buf); f_name_build_buf = malloc_2((f_name_build_buf_len=len)+1);
				}
			str = f_name_build_buf;
			}

		if(!GLOBALS->alt_hier_delimeter)
			{
			memcpy(str, buf, len+1);
			}
			else
			{
			strcpy_vcdalt(str, buf, GLOBALS->alt_hier_delimeter);
			}
		s=&sym_block[i];
	        symadd_name_exists_sym_exists(s,str,0);
		prevsymroot = prevsym = NULL;

		if(GLOBALS->fast_tree_sort)
			{
			len = sprintf_2_sdd(buf, nnam,node_block[i].msi, node_block[i].lsi);
			fst_append_graft_chain(len, buf, i, npar);
			}
		}
	else
		{
		int gatecmp = (f->len==1) && (!(f->flags&(VZT_RD_SYM_F_INTEGER|VZT_RD_SYM_F_DOUBLE|VZT_RD_SYM_F_STRING))) && (node_block[i].msi!=-1) && (node_block[i].lsi!=-1);
		int revcmp = gatecmp && (i) && (f_name_len[(i)&F_NAME_MODULUS] == f_name_len[(i-1)&F_NAME_MODULUS]) && (!memrevcmp(f_name_len[(i)&F_NAME_MODULUS], f_name[(i)&F_NAME_MODULUS], f_name[(i-1)&F_NAME_MODULUS]));

		if(gatecmp)
			{
			int len = sprintf_2_sd(buf, f_name[(i)&F_NAME_MODULUS],node_block[i].msi);

			longest_nam_candidate = len;
			if(!GLOBALS->do_hier_compress)
				{
				str=malloc_2(len+1);
				}
				else
				{
				if(len > f_name_build_buf_len)
					{
					free_2(f_name_build_buf); f_name_build_buf = malloc_2((f_name_build_buf_len=len)+1);
					}
				str = f_name_build_buf;
				}

			if(!GLOBALS->alt_hier_delimeter)
				{
				memcpy(str, buf, len+1);
				}
				else
				{
				strcpy_vcdalt(str, buf, GLOBALS->alt_hier_delimeter);
				}
			s=&sym_block[i];
		        symadd_name_exists_sym_exists(s,str,0);
			if((allowed_to_autocoalesce)&&(prevsym)&&(revcmp)) /* allow chaining for search functions.. NOTE: removed &&(!strchr(f_name[(i)&F_NAME_MODULUS], '\\')) because of esc_sm fix */
				{
				prevsym->vec_root = prevsymroot;
				prevsym->vec_chain = s;
				s->vec_root = prevsymroot;
				prevsym = s;
				}
				else
				{
				prevsymroot = prevsym = s;
				}

			if(GLOBALS->fast_tree_sort)
				{
				len = sprintf_2_sd(buf, nnam,node_block[i].msi);
				fst_append_graft_chain(len, buf, i, npar);
				}
			}
			else
			{
			int len = f_name_len[(i)&F_NAME_MODULUS];

			longest_nam_candidate = len;
			if(!GLOBALS->do_hier_compress)
				{
				str=malloc_2(len+1);
				}
				else
				{
				if(len > f_name_build_buf_len)
					{
					free_2(f_name_build_buf); f_name_build_buf = malloc_2((f_name_build_buf_len=len)+1);
					}
				str = f_name_build_buf;
				}

			if(!GLOBALS->alt_hier_delimeter)
				{
				memcpy(str, f_name[(i)&F_NAME_MODULUS], len+1);
				}
				else
				{
				strcpy_vcdalt(str, f_name[(i)&F_NAME_MODULUS], GLOBALS->alt_hier_delimeter);
				}
			s=&sym_block[i];
		        symadd_name_exists_sym_exists(s,str,0);
			prevsymroot = prevsym = NULL;

			if(f->flags&VZT_RD_SYM_F_INTEGER)
				{
				if(f->len != 0)
					{
					node_block[i].msi = f->len - 1;
					node_block[i].lsi = 0;
					GLOBALS->mvlfacs_fst_c_3[i].len = f->len;
					}
					else
					{
					node_block[i].msi = 31;
					node_block[i].lsi = 0;
					GLOBALS->mvlfacs_fst_c_3[i].len = 32;
					}
			}

			if(GLOBALS->fast_tree_sort)
				{
				fst_append_graft_chain(strlen(nnam), nnam, i, npar);
				}
			}
		}

        if(longest_nam_candidate > GLOBALS->longestname) GLOBALS->longestname = longest_nam_candidate;

        GLOBALS->facs[i]=&sym_block[i];
        n=&node_block[i];

	if(GLOBALS->queued_xl_enum_filter)
		{
		Jval jv;
		jv.ui = GLOBALS->queued_xl_enum_filter;
		GLOBALS->queued_xl_enum_filter = 0;

		if(!GLOBALS->enum_nptrs_jrb) GLOBALS->enum_nptrs_jrb = make_jrb();
		jrb_insert_vptr(GLOBALS->enum_nptrs_jrb, n, jv);
		}

	if(GLOBALS->do_hier_compress)
		{
		n->nname = compress_facility((unsigned char *)s->name, longest_nam_candidate);
		/* free_2(s->name); ...removed as f_name_build_buf is now used */
		s->name = n->nname;
		}
		else
		{
		n->nname=s->name;
		}

        n->mv.mvlfac = GLOBALS->mvlfacs_fst_c_3+i;
	GLOBALS->mvlfacs_fst_c_3[i].working_node = n;
	n->vardir = nvd;
	n->varxt  = h->u.var.sxt_workspace;
	if((h->u.var.svt_workspace == FST_SVT_NONE) && (h->u.var.sdt_workspace == FST_SDT_NONE))
		{
		n->vartype = nvt;
		}
		else
		{
		switch(h->u.var.svt_workspace)
			{
			case FST_SVT_VHDL_SIGNAL:	nvt = ND_VHDL_SIGNAL; break;
			case FST_SVT_VHDL_VARIABLE:	nvt = ND_VHDL_VARIABLE; break;
			case FST_SVT_VHDL_CONSTANT:	nvt = ND_VHDL_CONSTANT; break;
			case FST_SVT_VHDL_FILE:		nvt = ND_VHDL_FILE; break;
			case FST_SVT_VHDL_MEMORY:	nvt = ND_VHDL_MEMORY; break;
			default:			break; /* keep what exists */
			}
		n->vartype = nvt;

		switch(h->u.var.sdt_workspace)
			{
			case FST_SDT_VHDL_BOOLEAN:		ndt = ND_VDT_VHDL_BOOLEAN; break;
			case FST_SDT_VHDL_BIT:			ndt = ND_VDT_VHDL_BIT; break;
			case FST_SDT_VHDL_BIT_VECTOR:   	ndt = ND_VDT_VHDL_BIT_VECTOR; break;
			case FST_SDT_VHDL_STD_ULOGIC:   	ndt = ND_VDT_VHDL_STD_ULOGIC; break;
			case FST_SDT_VHDL_STD_ULOGIC_VECTOR: 	ndt = ND_VDT_VHDL_STD_ULOGIC_VECTOR; break;
			case FST_SDT_VHDL_STD_LOGIC:       	ndt = ND_VDT_VHDL_STD_LOGIC; break;
			case FST_SDT_VHDL_STD_LOGIC_VECTOR:  	ndt = ND_VDT_VHDL_STD_LOGIC_VECTOR; break;
			case FST_SDT_VHDL_UNSIGNED:          	ndt = ND_VDT_VHDL_UNSIGNED; break;
			case FST_SDT_VHDL_SIGNED:            	ndt = ND_VDT_VHDL_SIGNED; break;
			case FST_SDT_VHDL_INTEGER:           	ndt = ND_VDT_VHDL_INTEGER; break;
			case FST_SDT_VHDL_REAL:              	ndt = ND_VDT_VHDL_REAL; break;
			case FST_SDT_VHDL_NATURAL:           	ndt = ND_VDT_VHDL_NATURAL; break;
			case FST_SDT_VHDL_POSITIVE:          	ndt = ND_VDT_VHDL_POSITIVE; break;
			case FST_SDT_VHDL_TIME:              	ndt = ND_VDT_VHDL_TIME; break;
			case FST_SDT_VHDL_CHARACTER:         	ndt = ND_VDT_VHDL_CHARACTER; break;
			case FST_SDT_VHDL_STRING:            	ndt = ND_VDT_VHDL_STRING; break;
			default:				ndt = ND_VDT_NONE; break;
			}

		n->vardt = ndt;
		}

	if((f->len>1)||(f->flags&(VZT_RD_SYM_F_DOUBLE|VZT_RD_SYM_F_STRING)))
		{
		n->extvals = 1;
		}

        n->head.time=-1;        /* mark 1st node as negative time */
        n->head.v.h_val=AN_X;
        s->n=n;
        }			/* for(i) of facs parsing */


if(f_name_max_len) { free_2(f_name_max_len); f_name_max_len = NULL; }
if(nnam) { free_2(nnam); nnam = NULL; }
if(f_name_build_buf) { free_2(f_name_build_buf); f_name_build_buf = NULL; }

for(i=0;i<=F_NAME_MODULUS;i++)
	{
	if(f_name[i])
		{
		free_2(f_name[i]);
		f_name[i] = NULL;
		}
	}
free_2(f_name); f_name = NULL;
free_2(f_name_len); f_name_len = NULL;

if(numvars != GLOBALS->numfacs) { GLOBALS->mvlfacs_fst_rvs_alias = realloc_2(GLOBALS->mvlfacs_fst_rvs_alias, numvars * sizeof(fstHandle)); }

if(GLOBALS->subvar_jrb_count) /* generate lookup table for typenames explicitly given as attributes */
	{
	JRB subvar_jrb_node = NULL;
	GLOBALS->subvar_pnt = calloc_2(GLOBALS->subvar_jrb_count + 1, sizeof(char *));

	jrb_traverse(subvar_jrb_node, GLOBALS->subvar_jrb)
		{
		GLOBALS->subvar_pnt[subvar_jrb_node->val.ui] = subvar_jrb_node->key.s;
		}
	}

if(GLOBALS->istem_struct_base)
	{
	if(GLOBALS->istem_struct_base_siz != GLOBALS->istem_struct_base_siz_alloc)
		{
		GLOBALS->istem_struct_base_siz_alloc = GLOBALS->istem_struct_base_siz;
		GLOBALS->istem_struct_base = realloc_2(GLOBALS->istem_struct_base, GLOBALS->istem_struct_base_siz_alloc * sizeof(struct stem_struct_t));
		}
	}

if(GLOBALS->stem_struct_base)
	{
	if(GLOBALS->stem_struct_base_siz != GLOBALS->stem_struct_base_siz_alloc)
		{
		GLOBALS->stem_struct_base_siz_alloc = GLOBALS->stem_struct_base_siz;
		GLOBALS->stem_struct_base = realloc_2(GLOBALS->stem_struct_base, GLOBALS->stem_struct_base_siz_alloc * sizeof(struct stem_struct_t));
		}
	}

if(GLOBALS->stem_path_string_table)
	{
	if(GLOBALS->stem_path_string_table_siz != GLOBALS->stem_path_string_table_alloc)
		{
		GLOBALS->stem_path_string_table_alloc = GLOBALS->stem_path_string_table_siz;
		GLOBALS->stem_path_string_table = realloc_2(GLOBALS->stem_path_string_table, GLOBALS->stem_path_string_table_alloc * sizeof(char *));
		}
	}

decorated_module_cleanup(); /* ...also now in gtk2_treesearch.c */
freeze_facility_pack();
iter_through_comp_name_table();

fprintf(stderr, FST_RDLOAD"Built %d signal%s and %d alias%s.\n",
	numvars, (numvars == 1) ? "" : "s",
	numalias, (numalias == 1) ? "" : "es");

GLOBALS->fst_maxhandle = numvars;

if(GLOBALS->fast_tree_sort)
        {
/* SPLASH */                            splash_sync(2, 5);
        fprintf(stderr, FST_RDLOAD"Building facility hierarchy tree.\n");

        init_tree();
        treegraft(&GLOBALS->treeroot);

/* SPLASH */                            splash_sync(3, 5);

        fprintf(stderr, FST_RDLOAD"Sorting facility hierarchy tree.\n");
        treesort(GLOBALS->treeroot, NULL);
/* SPLASH */                            splash_sync(4, 5);
        order_facs_from_treesort(GLOBALS->treeroot, &GLOBALS->facs);

/* SPLASH */                            splash_sync(5, 5);
        GLOBALS->facs_are_sorted=1;
        }
        else
	{
/* SPLASH */                            splash_sync(2, 5);
	for(i=0;i<GLOBALS->numfacs;i++)
		{
		char *subst, ch;
		int esc = 0;

	        subst=GLOBALS->facs[i]->name;
		while((ch=(*subst)))
			{
#ifdef WAVE_HIERFIX
	                if(ch==GLOBALS->hier_delimeter) { *subst=(!esc) ? VCDNAM_HIERSORT : VCDNAM_ESCAPE; }    /* forces sort at hier boundaries */
#else
	                if((ch==GLOBALS->hier_delimeter)&&(esc)) { *subst = VCDNAM_ESCAPE; }    /* forces sort at hier boundaries */
#endif
	                else if(ch=='\\') { esc = 1; GLOBALS->escaped_names_found_vcd_c_1 = 1; }
	                subst++;
			}
		}

/* SPLASH */                            splash_sync(3, 5);
	fprintf(stderr, FST_RDLOAD"Sorting facilities at hierarchy boundaries.\n");
	wave_heapsort(GLOBALS->facs,GLOBALS->numfacs);

#ifdef WAVE_HIERFIX
	for(i=0;i<GLOBALS->numfacs;i++)
		{
		char *subst, ch;

		subst=GLOBALS->facs[i]->name;
		while((ch=(*subst)))
			{
			if(ch==VCDNAM_HIERSORT) { *subst=GLOBALS->hier_delimeter; }	/* restore back to normal */
			subst++;
			}
		}
#endif

	GLOBALS->facs_are_sorted=1;

/* SPLASH */                            splash_sync(4, 5);
	fprintf(stderr, FST_RDLOAD"Building facility hierarchy tree.\n");

	init_tree();
	for(i=0;i<GLOBALS->numfacs;i++)
		{
		char *nf = GLOBALS->facs[i]->name;
	        build_tree_from_name(nf, i);
		}
/* SPLASH */                            splash_sync(5, 5);
	if(GLOBALS->escaped_names_found_vcd_c_1)
	        {
		for(i=0;i<GLOBALS->numfacs;i++)
			{
		        char *subst, ch;
		        subst=GLOBALS->facs[i]->name;
		        while((ch=(*subst)))
		                {
		                if(ch==VCDNAM_ESCAPE) { *subst=GLOBALS->hier_delimeter; } /* restore back to normal */
		                subst++;
		                }
			}
	        }
	treegraft(&GLOBALS->treeroot);
	treesort(GLOBALS->treeroot, NULL);
	if(GLOBALS->escaped_names_found_vcd_c_1)
	        {
	        treenamefix(GLOBALS->treeroot);
	        }
	}

#if 0
{
int num_dups = 0;
for(i=0;i<GLOBALS->numfacs-1;i++)
	{
	if(!strcmp(GLOBALS->facs[i]->name, GLOBALS->facs[i+1]->name))
		{
		fprintf(stderr, FST_RDLOAD"DUPLICATE FAC: '%s'\n", GLOBALS->facs[i]->name);
		num_dups++;
		}
	}

if(num_dups)
	{
	fprintf(stderr, FST_RDLOAD"Exiting, %d duplicate signals are present.\n", num_dups);
	exit(255);
	}
}
#endif

GLOBALS->min_time = GLOBALS->first_cycle_fst_c_3; GLOBALS->max_time=GLOBALS->last_cycle_fst_c_3;
GLOBALS->is_lx2 = LXT2_IS_FST;

if(skip_start || skip_end)
	{
	TimeType b_start, b_end;

	if(!skip_start) b_start = GLOBALS->min_time; else b_start = unformat_time(skip_start, GLOBALS->time_dimension);
	if(!skip_end) b_end = GLOBALS->max_time; else b_end = unformat_time(skip_end, GLOBALS->time_dimension);

	if(b_start<GLOBALS->min_time) b_start = GLOBALS->min_time;
	else if(b_start>GLOBALS->max_time) b_start = GLOBALS->max_time;

	if(b_end<GLOBALS->min_time) b_end = GLOBALS->min_time;
	else if(b_end>GLOBALS->max_time) b_end = GLOBALS->max_time;

        if(b_start > b_end)
                {
		TimeType tmp_time = b_start;
                b_start = b_end;
                b_end = tmp_time;
                }

	fstReaderSetLimitTimeRange(GLOBALS->fst_fst_c_1, b_start, b_end);
	GLOBALS->min_time = b_start;
	GLOBALS->max_time = b_end;
	}

fstReaderIterBlocksSetNativeDoublesOnCallback(GLOBALS->fst_fst_c_1, 1); /* to avoid bin -> ascii -> bin double swap */

/* SPLASH */                            splash_finalize();
return(GLOBALS->max_time);
}


/*
 * conversion from evcd -> vcd format
 */
static void evcd_memcpy(char *dst, const char *src, int len)
{
static const char *evcd="DUNZduLHXTlh01?FAaBbCcf";
static const char  *vcd="01xz0101xz0101xzxxxxxxz";

char ch;
int i, j;

for(j=0;j<len;j++)
        {
	ch=*src;
        for(i=0;i<23;i++)
                {
                if(evcd[i]==ch)
                        {
                        *dst=vcd[i];
                        break;
                        }
                }
        if(i==23) *dst='x';

        src++;
        dst++;
        }
}


/*
 * fst callback (only does bits for now)
 */
static void fst_callback2(void *user_callback_data_pointer, uint64_t tim, fstHandle txidx, const unsigned char *value, uint32_t plen)
{
(void)user_callback_data_pointer;

fstHandle facidx = GLOBALS->mvlfacs_fst_rvs_alias[--txidx];
struct HistEnt *htemp;
struct lx2_entry *l2e = GLOBALS->fst_table_fst_c_1+facidx;
struct fac *f = GLOBALS->mvlfacs_fst_c_3+facidx;

GLOBALS->busycnt_fst_c_2++;
if(GLOBALS->busycnt_fst_c_2==WAVE_BUSY_ITER)
	{
	busy_window_refresh();
	GLOBALS->busycnt_fst_c_2 = 0;
	}

/* fprintf(stderr, "%lld %d '%s'\n", tim, facidx, value); */

if(!(f->flags&(VZT_RD_SYM_F_DOUBLE|VZT_RD_SYM_F_STRING)))
	{
	unsigned char vt = ND_UNSPECIFIED_DEFAULT;
	if(f->working_node)
		{
		vt = f->working_node->vartype;
		}

	if(f->len>1)
	        {
		char *h_vector = (char *)malloc_2(f->len);
		if(vt != ND_VCD_PORT)
			{
			memcpy(h_vector, value, f->len);
			}
			else
			{
			evcd_memcpy(h_vector, (const char *)value, f->len);
			}

		if((l2e->histent_curr)&&(l2e->histent_curr->v.h_vector)) /* remove duplicate values */
			{
			if((!memcmp(l2e->histent_curr->v.h_vector, h_vector, f->len))&&(!GLOBALS->vcd_preserve_glitches))
				{
				free_2(h_vector);
				return;
				}
			}

		htemp = histent_calloc();
		htemp->v.h_vector = h_vector;
	        }
	        else
	        {
		unsigned char h_val;

		if(vt != ND_VCD_PORT)
			{
			switch(*value)
				{
                                case '0':	h_val = AN_0; break;
                                case '1':	h_val = AN_1; break;
                                case 'X':
                                case 'x':	h_val = AN_X; break;
                                case 'Z':
                                case 'z':	h_val = AN_Z; break;
                                case 'H':
                                case 'h':	h_val = AN_H; break;
                                case 'U':
                                case 'u':	h_val = AN_U; break;
                                case 'W':
                                case 'w':	h_val = AN_W; break;
                                case 'L':
                                case 'l':	h_val = AN_L; break;
				case '-':	h_val = AN_DASH; break;

                                default:        h_val = AN_X; break;
				}
			}
			else
			{
			char membuf[1];
			evcd_memcpy(membuf, (const char *)value, 1);
			switch(*membuf)
				{
				case '0':	h_val = AN_0; break;
				case '1':	h_val = AN_1; break;
				case 'Z':
				case 'z':	h_val = AN_Z; break;
				default:	h_val = AN_X; break;
				}
			}

		if((vt != ND_VCD_EVENT) && (l2e->histent_curr)) /* remove duplicate values */
			{
			if((l2e->histent_curr->v.h_val == h_val) && (!GLOBALS->vcd_preserve_glitches))
				{
				return;
				}
			}

		htemp = histent_calloc();
		htemp->v.h_val = h_val;
	        }
	}
else if(f->flags&VZT_RD_SYM_F_DOUBLE)
	{
	if((l2e->histent_curr)&&(l2e->histent_curr->v.h_vector)) /* remove duplicate values */
		{
#ifdef WAVE_HAS_H_DOUBLE
		if(!memcmp(&l2e->histent_curr->v.h_double, value, sizeof(double)))
#else
		if(!memcmp(l2e->histent_curr->v.h_vector, value, sizeof(double)))
#endif
			{
			if((!GLOBALS->vcd_preserve_glitches)&&(!GLOBALS->vcd_preserve_glitches_real))
				{
				return;
				}
			}
		}

	/* if(fstReaderIterBlocksSetNativeDoublesOnCallback is disabled...)

	double *d = double_slab_calloc();
	sscanf(value, "%lg", d);
	htemp = histent_calloc();
	htemp->v.h_vector = (char *)d;

	otherwise...
	*/

	htemp = histent_calloc();
#ifdef WAVE_HAS_H_DOUBLE
	memcpy(&htemp->v.h_double, value, sizeof(double));
#else
	htemp->v.h_vector = double_slab_calloc();
	memcpy(htemp->v.h_vector, value, sizeof(double));
#endif
	htemp->flags = HIST_REAL;
	}
else	/* string */
	{
	unsigned char *s = malloc_2(plen + 1);
	uint32_t pidx;

	for(pidx=0;pidx<plen;pidx++)
		{
		unsigned char ch = value[pidx];

#if 0
		/* for now do not convert to printable unless done in vcd + lxt loaders also */
		if((ch < ' ') || (ch > '~'))
			{
			ch = '.';
			}
#endif

		s[pidx] = ch;
		}
	s[pidx] = 0;

	if((l2e->histent_curr)&&(l2e->histent_curr->v.h_vector)) /* remove duplicate values */
		{
		if((!strcmp(l2e->histent_curr->v.h_vector, (const char *)value)) && (!GLOBALS->vcd_preserve_glitches))
			{
			free_2(s);
			return;
			}
		}

	htemp = histent_calloc();
	htemp->v.h_vector = (char *)s;
	htemp->flags = HIST_REAL|HIST_STRING;
	}


htemp->time = (tim) * (GLOBALS->time_scale);

if(l2e->histent_curr) /* scan-build : was l2e->histent_head */
	{
	l2e->histent_curr->next = htemp; /* scan-build : this is ok given how it's used */
	l2e->histent_curr = htemp;
	}
	else
	{
	l2e->histent_head = l2e->histent_curr = htemp;
	}

l2e->numtrans++;
}


static void fst_callback(void *user_callback_data_pointer, uint64_t tim, fstHandle txidx, const unsigned char *value)
{
fst_callback2(user_callback_data_pointer, tim, txidx, value, 0);
}


/*
 * this is the black magic that handles aliased signals...
 */
static void fst_resolver(nptr np, nptr resolve)
{
np->extvals = resolve->extvals;
np->msi = resolve->msi;
np->lsi = resolve->lsi;
memcpy(&np->head, &resolve->head, sizeof(struct HistEnt));
np->curr = resolve->curr;
np->harray = resolve->harray;
np->numhist = resolve->numhist;
np->mv.mvlfac=NULL;
}



/*
 * actually import a fst trace but don't do it if it's already been imported
 */
void import_fst_trace(nptr np)
{
hptr htemp, htempx=NULL, histent_tail;
int len, i;
struct fac *f;
int txidx;
nptr nold = np;

if(!(f=np->mv.mvlfac)) return;	/* already imported */

txidx = f - GLOBALS->mvlfacs_fst_c_3;
if(np->mv.mvlfac->flags&VZT_RD_SYM_F_ALIAS)
	{
	txidx = GLOBALS->mvlfacs_fst_c_3[txidx].node_alias; /* this is to map to fstHandles, so even non-aliased are remapped */
	txidx = GLOBALS->mvlfacs_fst_rvs_alias[txidx];
	np = GLOBALS->mvlfacs_fst_c_3[txidx].working_node;

	if(!(f=np->mv.mvlfac))
		{
		fst_resolver(nold, np);
		return;	/* already imported */
		}
	}


if(!(f->flags&VZT_RD_SYM_F_SYNVEC)) /* block debug message for synclk */
	{
	int flagged = HIER_DEPACK_STATIC;
	char *str = hier_decompress_flagged(np->nname, &flagged);
	fprintf(stderr, "Import: %s\n", str); /* normally this never happens */
	}


/* new stuff */
len = np->mv.mvlfac->len;

/* check here for array height in future */

if(!(f->flags&VZT_RD_SYM_F_SYNVEC))
	{
	fstReaderSetFacProcessMask(GLOBALS->fst_fst_c_1, GLOBALS->mvlfacs_fst_c_3[txidx].node_alias+1);
	fstReaderIterBlocks2(GLOBALS->fst_fst_c_1, fst_callback, fst_callback2, NULL, NULL);
	fstReaderClrFacProcessMask(GLOBALS->fst_fst_c_1, GLOBALS->mvlfacs_fst_c_3[txidx].node_alias+1);
	}

histent_tail = htemp = histent_calloc();
if(len>1)
	{
	htemp->v.h_vector = (char *)malloc_2(len);
	for(i=0;i<len;i++) htemp->v.h_vector[i] = AN_Z;
	}
	else
	{
	htemp->v.h_val = AN_Z;		/* z */
	}
htemp->time = MAX_HISTENT_TIME;

htemp = histent_calloc();
if(len>1)
	{
	if(!(f->flags&VZT_RD_SYM_F_DOUBLE))
		{
		if(!(f->flags&VZT_RD_SYM_F_STRING))
			{
			htemp->v.h_vector = (char *)malloc_2(len);
			for(i=0;i<len;i++) htemp->v.h_vector[i] = AN_X;
			}
			else
			{
			htemp->v.h_vector = strdup_2("UNDEF");
			htemp->flags = HIST_REAL|HIST_STRING;
			}
		}
		else
		{
#ifdef WAVE_HAS_H_DOUBLE
		htemp->v.h_double = strtod("NaN", NULL);
#else
                double *d = malloc_2(sizeof(double));
                *d = strtod("NaN", NULL);
                htemp->v.h_vector = (char *)d;
#endif
                htemp->flags = HIST_REAL;
		}
        htempx = htemp;
	}
	else
	{
	htemp->v.h_val = AN_X;		/* x */
        htempx = htemp;
	}
htemp->time = MAX_HISTENT_TIME-1;
htemp->next = histent_tail;

if(GLOBALS->fst_table_fst_c_1[txidx].histent_curr)
	{
	GLOBALS->fst_table_fst_c_1[txidx].histent_curr->next = htemp;
	htemp = GLOBALS->fst_table_fst_c_1[txidx].histent_head;
	}

if(!(f->flags&(VZT_RD_SYM_F_DOUBLE|VZT_RD_SYM_F_STRING)))
        {
	if(len>1)
		{
		np->head.v.h_vector = (char *)malloc_2(len);
		for(i=0;i<len;i++) np->head.v.h_vector[i] = AN_X;
		}
		else
		{
		np->head.v.h_val = AN_X;	/* x */
		}
	}
        else
        {
        np->head.flags = HIST_REAL;
        if(f->flags&VZT_RD_SYM_F_STRING)
		{
		np->head.flags |= HIST_STRING;
		}
        }

	{
        struct HistEnt *htemp2 = histent_calloc();
        htemp2->time = -1;
        if(len>1)
        	{
                htemp2->v.h_vector = htempx->v.h_vector;
		htemp2->flags = htempx->flags;
                }
                else
                {
                htemp2->v.h_val = htempx->v.h_val;
		}
	htemp2->next = htemp;
        htemp = htemp2;
        GLOBALS->fst_table_fst_c_1[txidx].numtrans++;
        }

np->head.time  = -2;
np->head.next = htemp;
np->numhist=GLOBALS->fst_table_fst_c_1[txidx].numtrans +2 /*endcap*/ +1 /*frontcap*/;

memset(GLOBALS->fst_table_fst_c_1+txidx, 0, sizeof(struct lx2_entry));	/* zero it out */

np->curr = histent_tail;
np->mv.mvlfac = NULL;	/* it's imported and cached so we can forget it's an mvlfac now */

if(nold!=np)
	{
	fst_resolver(nold, np);
	}
}


/*
 * decompress [m b xs xe valstring]... format string into trace
 */
static void expand_synvec(int txidx, const char *s)
{
char *scopy = NULL;
char *pnt, *pnt2;
double m, b;
uint64_t xs, xe, xi;
char *vs;
uint64_t tim;
uint64_t tim_max;
int vslen;
int vspnt;
unsigned char value[2] = {0, 0};
unsigned char pval = 0;

scopy = strdup_2(s);
vs = calloc_2(1, strlen(s) + 1); /* will never be as big as original string */
pnt = scopy;

while(*pnt)
	{
	if(*pnt != '[') { pnt++; continue; }
	pnt++;

	pnt2 = strchr(pnt, ']');
	if(!pnt2) break;
	*pnt2 = 0;

	/* printf("PNT: %s\n", pnt); */
	int rc = sscanf(pnt, "%lg %lg %"SCNu64" %"SCNu64" %s", &m, &b, &xs, &xe, vs);
	if(rc == 5)
		{
		vslen = strlen(vs);
		vspnt = 0;

		tim_max = 0;
		for(xi = xs; xi <= xe; xi++)
			{
			tim = (xi * m) + b;
			/* fprintf(stderr, "#%"PRIu64" '%c'\n", tim, vs[vspnt]); */
			value[0] = vs[vspnt];
			if(value[0] != pval) /* collapse new == old value transitions so new is ignored */
				{
				if((tim >= tim_max) || (xi == xs))
					{
					fst_callback2(NULL, tim, txidx, value, 0);
					tim_max = tim;
					}
				pval = value[0];
				}
			vspnt++; vspnt = (vspnt == vslen) ? 0 : vspnt; /* modulus on repeating clock */
			}
		}
		else
		{
		break;
		}

	pnt = pnt2 + 1;
	}

free_2(vs);
free_2(scopy);
}


/*
 * pre-import many traces at once so function above doesn't have to iterate...
 */
void fst_set_fac_process_mask(nptr np)
{
struct fac *f;
int txidx;

if(!(f=np->mv.mvlfac)) return;	/* already imported */

txidx = f-GLOBALS->mvlfacs_fst_c_3;

if(np->mv.mvlfac->flags&VZT_RD_SYM_F_ALIAS)
	{
	txidx = GLOBALS->mvlfacs_fst_c_3[txidx].node_alias;
	txidx = GLOBALS->mvlfacs_fst_rvs_alias[txidx];
	np = GLOBALS->mvlfacs_fst_c_3[txidx].working_node;

	if(!(np->mv.mvlfac)) return;	/* already imported */
	}

if(np->mv.mvlfac->flags&VZT_RD_SYM_F_SYNVEC)
	{
	JRB fi = jrb_find_int(GLOBALS->synclock_jrb, txidx);
	if(fi)
		{
		expand_synvec(GLOBALS->mvlfacs_fst_c_3[txidx].node_alias+1, fi->val.s);
		import_fst_trace(np);
		return; /* import_fst_trace() will construct the trailer */
		}
	}

/* check here for array height in future */
	{
	fstReaderSetFacProcessMask(GLOBALS->fst_fst_c_1, GLOBALS->mvlfacs_fst_c_3[txidx].node_alias+1);
	GLOBALS->fst_table_fst_c_1[txidx].np = np;
	}
}


void fst_import_masked(void)
{
unsigned int txidxi;
int i, cnt;
hptr htempx = NULL;

cnt = 0;
for(txidxi=0;txidxi<GLOBALS->fst_maxhandle;txidxi++)
	{
	if(fstReaderGetFacProcessMask(GLOBALS->fst_fst_c_1, txidxi+1))
		{
		cnt++;
		}
	}

if(!cnt)
	{
	return;
	}

if(cnt>100)
	{
	fprintf(stderr, FST_RDLOAD"Extracting %d traces\n", cnt);
	}

set_window_busy(NULL);
fstReaderIterBlocks2(GLOBALS->fst_fst_c_1, fst_callback, fst_callback2, NULL, NULL);
set_window_idle(NULL);

for(txidxi=0;txidxi<GLOBALS->fst_maxhandle;txidxi++)
	{
	if(fstReaderGetFacProcessMask(GLOBALS->fst_fst_c_1, txidxi+1))
		{
		int txidx = GLOBALS->mvlfacs_fst_rvs_alias[txidxi];
		struct HistEnt *htemp, *histent_tail;
		struct fac *f = GLOBALS->mvlfacs_fst_c_3+txidx;
		int len = f->len;
		nptr np = GLOBALS->fst_table_fst_c_1[txidx].np;

		histent_tail = htemp = histent_calloc();
		if(len>1)
			{
			htemp->v.h_vector = (char *)malloc_2(len);
			for(i=0;i<len;i++) htemp->v.h_vector[i] = AN_Z;
			}
			else
			{
			htemp->v.h_val = AN_Z;		/* z */
			}
		htemp->time = MAX_HISTENT_TIME;

		htemp = histent_calloc();
		if(len>1)
			{
			if(!(f->flags&VZT_RD_SYM_F_DOUBLE))
				{
				if(!(f->flags&VZT_RD_SYM_F_STRING))
					{
					htemp->v.h_vector = (char *)malloc_2(len);
					for(i=0;i<len;i++) htemp->v.h_vector[i] = AN_X;
					}
					else
					{
					htemp->v.h_vector = strdup_2("UNDEF");
					htemp->flags = HIST_REAL|HIST_STRING;
					}
				htempx = htemp;
				}
				else
				{
#ifdef WAVE_HAS_H_DOUBLE
				htemp->v.h_double = strtod("NaN", NULL);
#else
				double *d = malloc_2(sizeof(double));

				*d = strtod("NaN", NULL);
				htemp->v.h_vector = (char *)d;
#endif
				htemp->flags = HIST_REAL;
				htempx = htemp;
				}
			}
			else
			{
			htemp->v.h_val = AN_X;		/* x */
			htempx = htemp;
			}
		htemp->time = MAX_HISTENT_TIME-1;
		htemp->next = histent_tail;

		if(GLOBALS->fst_table_fst_c_1[txidx].histent_curr)
			{
			GLOBALS->fst_table_fst_c_1[txidx].histent_curr->next = htemp;
			htemp = GLOBALS->fst_table_fst_c_1[txidx].histent_head;
			}

		if(!(f->flags&(VZT_RD_SYM_F_DOUBLE|VZT_RD_SYM_F_STRING)))
		        {
			if(len>1)
				{
				np->head.v.h_vector = (char *)malloc_2(len);
				for(i=0;i<len;i++) np->head.v.h_vector[i] = AN_X;
				}
				else
				{
				np->head.v.h_val = AN_X;	/* x */
				}
			}
		        else
		        {
		        np->head.flags = HIST_REAL;
		        if(f->flags&VZT_RD_SYM_F_STRING)
				{
				np->head.flags |= HIST_STRING;
				}
		        }

                        {
                        struct HistEnt *htemp2 = histent_calloc();
                        htemp2->time = -1;
                        if(len>1)
                                {
                                htemp2->v.h_vector = htempx->v.h_vector;
                                htemp2->flags = htempx->flags;
                                }
                                else
                                {
                                htemp2->v.h_val = htempx->v.h_val;
				}
                        htemp2->next = htemp;
                        htemp = htemp2;
                        GLOBALS->fst_table_fst_c_1[txidx].numtrans++;
                        }

		np->head.time  = -2;
		np->head.next = htemp;
		np->numhist=GLOBALS->fst_table_fst_c_1[txidx].numtrans +2 /*endcap*/ +1 /*frontcap*/;

		memset(GLOBALS->fst_table_fst_c_1+txidx, 0, sizeof(struct lx2_entry));	/* zero it out */

		np->curr = histent_tail;
		np->mv.mvlfac = NULL;	/* it's imported and cached so we can forget it's an mvlfac now */
		fstReaderClrFacProcessMask(GLOBALS->fst_fst_c_1, txidxi+1);
		}
	}
}

