/*
 *                            COPYRIGHT
 *
 *  pcb-rnd, interactive printed circuit board design
 *  Copyright (C) 2017 Tibor 'Igor2' Palinkas
 *  Copyright (C) 2017 Erich S. Heinzle
 *
 *  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.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *  Contact:
 *    Project page: http://repo.hu/projects/pcb-rnd
 *    lead developer: http://repo.hu/projects/pcb-rnd/contact.html
 *    mailing list: pcb-rnd (at) list.repo.hu (send "subscribe")
 *
 */

/* Eagle binary tree parser */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>

#include "eagle_bin.h"
#include "egb_tree.h"
#include <librnd/core/error.h>
#include <librnd/core/math_helper.h>
#include <librnd/core/misc_util.h>
#include <librnd/core/compat_misc.h>

/* Describe a bitfield: width of the field that hosts the bitfield, first
and last bit offsets, inclusive. Bit offsets are starting from 0 at LSB. */
#define BITFIELD(field_width_bytes, first_bit, last_bit) \
	(((unsigned long)(field_width_bytes)) << 16 | ((unsigned long)(first_bit)) << 8 | ((unsigned long)(last_bit)))

typedef enum {
	T_BMB, /* bit-mask-bool: apply the bitmask in 'len' to the the byte at offs and use the result as a boolean */
	T_UBF, /* unsigned bitfield; offset is the first byte in block[], len is a BITFIELD() */
	T_INT,
	T_DBL,
	T_STR
} pcb_eagle_type_t;

typedef enum {
	SS_DIRECT,	    	/* it specifies the number of direct children */
	SS_RECURSIVE,	 	/* it specifies the number of all children, recursively */
	SS_RECURSIVE_MINUS_1	/* same as SS_RECURSIVE, but decrease the children count first */
} pcb_eagle_ss_type_t;


typedef struct {
	int offs; /* 0 is the terminator (no more offsets) */
	unsigned long len;
	int val;
} fmatch_t;

typedef struct {
	int offs; /* 0 is the terminator (no more offsets) */
	int len;
	pcb_eagle_ss_type_t ss_type;
	char *tree_name; /* if not NULL, create a subtree using this name and no attributes */
} subsect_t;

typedef struct {
	char *name; /* NULL is the terminator (no more attributes) */
	pcb_eagle_type_t type;
	int offs;
	unsigned len;
} attrs_t;

typedef struct {
	unsigned int cmd, cmd_mask;/* rule matches only if block[0] == cmd */
	char *name;
	fmatch_t fmatch[4];  /* rule matches only if all fmatch integer fields match their val */
	subsect_t subs[8];/* how to extract number of subsections (direct children) */
	attrs_t attrs[32];/* how to extract node attributes */
} pcb_eagle_script_t;

typedef struct
{
	egb_node_t *root, *layers, *drawing, *libraries, *elements, *firstel, *signals, *board, *drc;
	long mdWireWire, wire2pad, wire2via, pad2pad, pad2via, via2via, pad2smd, via2smd,
	smd2smd, copper2dimension, drill2hole, msWidth, minDrill, psTop, psBottom, psFirst;
	/* DRC values for spacing and width */ 
	double rvPadTop, rvPadInner, rvPadBottom; /* DRC values for via sizes */
	double rvViaOuter, rvViaInner, rvMicroViaOuter, rvMicroViaInner;
	/* the following design rule values don't map obviously to XML parameters */
	long restring_limit1_mil, restring_limit2_mil, restring_limit3_mil,
	restring_limit4_mil, restring_limit5_mil, restring_limit6_mil,
	restring_limit7_mil, restring_limit8_mil, restring_limit9_mil,
	restring_limit10_mil, restring_limit11_mil, restring_limit12_mil,
	restring_limit13_mil, restring_limit14_mil, mask_limit1_mil,
	mask_limit2_mil, mask_limit3_mil, mask_limit4_mil;
	double mask_percentages1_ratio, mask_percentages2_ratio;
	char *free_text;
	char *free_text_cursor;
	size_t free_text_len;
} egb_ctx_t;

#define TERM {0}

static const pcb_eagle_script_t pcb_eagle_script[] = {
	{ PCB_EGKW_SECT_START, 0xFF7F, "drawing",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
/*			{2, 2, SS_DIRECT, NULL},*/
			{4, 4, SS_RECURSIVE_MINUS_1, NULL},
			TERM
		},
		{ /* attributes */
			{"subsecs", T_INT, 2, 2},
			{"numsecs", T_INT, 4, 4},
			{"subsecsMSB", T_INT, 3, 1},
			{"subsecsLSB", T_INT, 2, 1},
			{"numsecsMSB2", T_INT, 7, 1},
			{"numsecsMSB1", T_INT, 6, 1},
			{"numsecsMSB0", T_INT, 5, 1},
			{"numsecsLSB", T_INT, 4, 1},
			{"v1", T_INT, 8, 1},
			{"v2", T_INT, 9, 1},
			TERM
		},
	},
	{ PCB_EGKW_SECT_UNKNOWN11, 0xFFFF, "unknown11" },
	{ PCB_EGKW_SECT_GRID, 0xFFFF, "grid",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"display", T_BMB, 2, 0x01},
			{"visible", T_BMB, 2, 0x02},
			{"unit", T_UBF, 3, BITFIELD(1, 0, 3)},
			{"altunit", T_UBF, 3, BITFIELD(1, 4, 7)},
			{"multiple",T_INT, 4, 3},
			{"size", T_DBL, 8, 8},
			{"altsize", T_DBL, 16, 8},
			TERM
		},
	},
	{ PCB_EGKW_SECT_LAYER, 0xFF7F, "layer",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"side", T_BMB, 2, 0x10},
			{"visible", T_UBF, 2, BITFIELD(1, 2, 3)},
			{"active", T_BMB, 2, 0x02},
			{"number",T_UBF, 3, BITFIELD(1, 0, 7)},
			{"other",T_INT, 4, 1},
			{"fill", T_UBF, 5, BITFIELD(1, 0, 3)},
			{"color",T_UBF, 6, BITFIELD(1, 0, 5)},
			{"name", T_STR, 15, 9},
			TERM
		},
	},
	{ PCB_EGKW_SECT_SCHEMA, 0xFFFF, "schema",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			{4, 4, SS_DIRECT, NULL},
			TERM
		},
		{ /* attributes */
			{"shtsubsecs", T_INT, 8, 4},
			{"atrsubsecs", T_INT, 12, 4}, /* 0 if v4 */
			{"xref_format",  T_STR, 19, 5},
			TERM
		},
	},
	{ PCB_EGKW_SECT_LIBRARY, 0xFF7F, "library",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			{4, 4, SS_RECURSIVE, NULL},
			{8, 4, SS_RECURSIVE, NULL},
			{12, 4, SS_RECURSIVE, NULL},
			/*{4, 4, SS_DIRECT, NULL},*/
			TERM
		},
		{ /* attributes */
			{"devsubsecs", T_INT, 4, 4},
			{"symsubsecs", T_INT, 8, 4},
			{"pacsubsecs", T_INT, 12, 4},
			{"children", T_INT, 8, 4},
			{"name",  T_STR, 16, 8},
			TERM
		},
	},
	{ PCB_EGKW_SECT_DEVICES, 0xFF7F, "devices",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			{4, 4, SS_DIRECT, NULL},
			TERM
		},
		{ /* attributes */
			{"children", T_INT, 8, 4},
			{"library",  T_STR, 16, 8},
			TERM
		},
	},
	{ PCB_EGKW_SECT_SYMBOLS, 0xFF7F, "symbols",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			{4, 4, SS_RECURSIVE, NULL},
			TERM
		},
		{ /* attributes */
			{"children", T_INT, 8, 4},
			{"library",  T_STR, 16, 8},
			TERM
		},
	},
	{ PCB_EGKW_SECT_PACKAGES, 0xFF5F, "packages",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			{4, 4, SS_RECURSIVE, NULL},
			TERM
		},
		{ /* attributes */
			{"subsects", T_INT, 4, 4},
			{"children", T_INT, 8, 2},
			{"desc",  T_STR, 10, 6},
			{"library",  T_STR, 16, 8},
			TERM
		},
	},
	{ PCB_EGKW_SECT_SCHEMASHEET, 0xFFFF, "schemasheet",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			{2, 2, SS_DIRECT, NULL},
			TERM
		},
		{ /* attributes */
			{"minx",  T_INT, 4, 2},
			{"miny",  T_INT, 6, 2},
			{"maxx",  T_INT, 8, 2},
			{"maxy",  T_INT, 10, 2},
			{"partsubsecs",  T_INT, 12, 4},
			{"bussubsecs",  T_INT, 16, 4},
			{"netsubsecs",  T_INT, 20, 4},
			TERM
		},
	},
	{ PCB_EGKW_SECT_BOARD, 0xFF37, "board",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			{12, 4, SS_RECURSIVE, "libraries"}, /* lib */
			{2,  2, SS_DIRECT, "plain"}, /* globals */
			{16, 4, SS_RECURSIVE, "elements"}, /* package refs */
			{20, 4, SS_RECURSIVE, "signals"}, /* nets */
			TERM
		},
		{ /* attributes */
			{"minx",  T_INT, 4, 2},
			{"miny",  T_INT, 6, 2},
			{"maxx",  T_INT, 8, 2},
			{"maxy",  T_INT, 10, 2},
			{"defsubsecs",  T_INT, 12, 4},
			{"pacsubsecs",  T_INT, 16, 4},
			{"netsubsecs",  T_INT, 20, 4},
			TERM
		},
	},
	{ PCB_EGKW_SECT_SIGNAL, 0xFFB3, "signal",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			{2, 2, SS_DIRECT, NULL},
			TERM
		},
		{ /* attributes */
			{"minx",  T_INT, 4, 2},
			{"miny",  T_INT, 6, 2},
			{"maxx",  T_INT, 8, 2},
			{"maxy",  T_INT, 10, 2},
			{"airwires",  T_BMB, 12, 0x02},
			{"netclass",  T_UBF, 13, BITFIELD(1, 0, 3)},
			{"name",  T_STR, 16, 8},
			TERM
		},
	},
	{ PCB_EGKW_SECT_SYMBOL, 0xFFFF, "symbol",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			{2, 2, SS_DIRECT, NULL},
			TERM
		},
		{ /* attributes */
			{"minx",  T_INT, 4, 2},
			{"miny",  T_INT, 6, 2},
			{"maxx",  T_INT, 8, 2},
			{"maxy",  T_INT, 10, 2},
			{"name",  T_STR, 16, 8},
			TERM
		},
	},
	{ PCB_EGKW_SECT_PACKAGE, 0xFFDF, "package",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			{2, 2, SS_RECURSIVE, NULL},
			TERM
		},
		{ /* attributes */
			{"minx",  T_INT, 4, 2},
			{"miny",  T_INT, 6, 2},
			{"maxx",  T_INT, 8, 2},
			{"maxy",  T_INT, 10, 2},
			{"desc",  T_STR, 13, 5},
			{"name",  T_STR, 18, 6},
			TERM
		},
	},
	{ PCB_EGKW_SECT_SCHEMANET, 0xFFFF, "schemanet",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			{2, 2, SS_DIRECT, NULL},
			TERM
		},
		{ /* attributes */
			{"minx",  T_INT, 4, 2},
			{"miny",  T_INT, 6, 2},
			{"maxx",  T_INT, 8, 2},
			{"maxy",  T_INT, 10, 2},
			{"netclass",  T_UBF, 13, BITFIELD(1, 0, 3)},
			{"name",  T_STR, 18, 6},
			TERM
		},
	},
	{ PCB_EGKW_SECT_PATH, 0xFFFF, "path",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			{2, 2, SS_DIRECT, NULL},
			TERM
		},
		{ /* attributes */
			{"minx",  T_INT, 4, 2},
			{"miny",  T_INT, 6, 2},
			{"maxx",  T_INT, 8, 2},
			{"maxy",  T_INT, 10, 2},
			TERM
		},
	},
	{ PCB_EGKW_SECT_POLYGON, 0xFFF7, "polygon",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			{2, 2, SS_DIRECT, NULL},
			TERM
		},
		{ /* attributes */
			{"minx",  T_INT, 4, 2},
			{"miny",  T_INT, 6, 2},
			{"maxx",  T_INT, 8, 2},
			{"maxy",  T_INT, 10, 2},
			{"width",  T_INT, 12, 2},
			{"spacing",  T_INT, 14, 2},
			{"isolate",  T_INT, 16, 2},
			{"layer",  T_UBF, 18, BITFIELD(1,0,7)},
			{"pour",  T_BMB, 19, 0x01},
			{"rank",  T_BMB, 19, BITFIELD(1, 1, 3)},
			{"thermals",  T_BMB, 19, 0x80},
			{"orphans",  T_BMB, 19, 0x40},
			TERM
		},
	},
	{ PCB_EGKW_SECT_LINE, 0xFF43, "wire",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"layer",  T_UBF, 3, BITFIELD(1,0,7)},
			{"half_width",  T_INT, 20, 2},
			{"stflags",  T_BMB, 22, 0x33},
			/*{"bin_style",  T_BMB, 22, 0x03},*/
			/*{"bin_cap",  T_BMB, 22, 0x10},*/
			{"clockwise",  T_BMB, 22, 0x20},
			{"linetype", T_UBF, 23, BITFIELD(1, 0, 7)},
			{"linetype_0_x1",  T_INT, 4, 4},
			/*{"x",  T_INT, 4, 4},  only needed if wire used for polygon vertex coord */
			{"linetype_0_y1",  T_INT, 8, 4},
			/*{"y",  T_INT, 8, 4},  only needed if wire used for polygon vertex coord */
			{"linetype_0_x2",  T_INT, 12, 4},
			{"linetype_0_y2",  T_INT, 16, 4},
			{"arc_negflags", T_UBF, 19, BITFIELD(1, 0, 4)},
			/*{"c_negflag", T_BMB, 19, 0x01},*/
			{"arc_c1",  T_INT, 7, 1},
			{"arc_c2",  T_INT, 11, 1},
			{"arc_c3",  T_INT, 15, 1},
			{"arc_x1",  T_INT, 4, 3},
			{"arc_y1",  T_INT, 8, 3},
			{"arc_x2",  T_INT, 12, 3},
			{"arc_y2",  T_INT, 16, 3},
			TERM
		},
	},
	{ PCB_EGKW_SECT_ARC, 0xFFFF, "arc",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"layer",  T_UBF, 3, BITFIELD(1,0,7)},
			{"half_width",  T_INT, 20, 2},
			{"clockwise",  T_BMB, 22, 0x20},
			{"arctype",  T_UBF, 23, BITFIELD(1, 0, 7)},
			{"arc_negflags", T_UBF, 19, BITFIELD(1, 0, 7)},
			/*{"c_negflag", T_BMB, 19, 0x01},*/
			{"arc_c1",  T_INT, 7, 1},
			{"arc_c2",  T_INT, 11, 1},
			{"arc_c3",  T_INT, 15, 1},
			{"arc_x1",  T_INT, 4, 3},
			{"arc_y1",  T_INT, 8, 3},
			{"arc_x2",  T_INT, 12, 3},
			{"arc_y2",  T_INT, 16, 3},
			{"arctype_other_x1",  T_INT, 4, 4},
			{"arctype_other_y1",  T_INT, 8, 4},
			{"arctype_other_x2",  T_INT, 12, 4},
			{"arctype_other_y2",  T_INT, 16, 4},
			TERM
		},
	},
	{ PCB_EGKW_SECT_CIRCLE, 0xFF53, "circle",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"layer", T_UBF, 3, BITFIELD(1,0,7)},
			{"x",  T_INT, 4, 4},
			{"y",  T_INT, 8, 4},
			{"radius",  T_INT, 12, 4},
			{"half_width", T_INT, 20, 4},
			TERM
		},
	},
	{ PCB_EGKW_SECT_RECTANGLE, 0xFF5F, "rectangle",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"layer", T_UBF, 3, BITFIELD(1,0,7)},
			{"x1",  T_INT, 4, 4},
			{"y1",  T_INT, 8, 4},
			{"x2",  T_INT, 12, 4},
			{"y2",  T_INT, 16, 4},
			{"bin_rot",  T_INT, 20, 2}, /* ? should it be T_UBF, 16, BITFIELD(2, 0, 11)},*/
			TERM
		},
	},
	{ PCB_EGKW_SECT_JUNCTION, 0xFFFF, "junction",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"layer", T_UBF, 3, BITFIELD(1,0,7)},
			{"x",  T_INT, 4, 4},
			{"y",  T_INT, 8, 4},
			{"width_2",  T_INT, 12, 2},
			TERM
		},
	},
	{ PCB_EGKW_SECT_HOLE, 0xFF53, "hole",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"x", T_INT, 4, 4},
			{"y", T_INT, 8, 4},
			{"half_diameter", T_UBF, 12, BITFIELD(2, 0, 15)},
			{"half_drill", T_UBF, 12, BITFIELD(2, 0, 15)}, /* try duplicating field */
			TERM
		},
	},
	{ PCB_EGKW_SECT_VIA, 0xFF7F, "via",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"shape", T_INT, 2, 1},
			{"x",  T_INT, 4, 4},
			{"y",  T_INT, 8, 4},
			{"half_drill", T_UBF, 12, BITFIELD(2, 0, 15)},
			{"half_diameter", T_UBF, 14, BITFIELD(2, 0, 15)},
			{"layers",  T_UBF, 16, BITFIELD(1,0,7)}, /*not 1:1 mapping */
			{"stop",  T_BMB, 17, 0x01},
			TERM
		},
	},
	{ PCB_EGKW_SECT_PAD, 0xFF5F, "pad",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"shape", T_INT, 2, 1},
			{"x",  T_INT, 4, 4},
			{"y",  T_INT, 8, 4},
			{"half_drill",  T_UBF, 12, BITFIELD(2, 0, 15)},
			{"half_diameter",  T_UBF, 14, BITFIELD(2, 0, 15)},
			{"bin_rot" , T_INT, 16, 2}, /* ? maybe T_UBF, 16, BITFIELD(2, 0, 11)}, */
			{"stop",  T_BMB, 18, 0x01},
			{"thermals",  T_BMB, 18, 0x04},
			{"first",  T_BMB, 18, 0x08},
			{"name",  T_STR, 19, 5},
			TERM
		},
	},
	{ PCB_EGKW_SECT_SMD, 0xFF7F, "smd",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"roundness", T_INT, 2, 1},
			{"layer", T_UBF, 3, BITFIELD(1,0,7)},
			{"x",  T_INT, 4, 4},
			{"y",  T_INT, 8, 4},
			{"half_dx",  T_UBF, 12, BITFIELD(2, 0, 15)},
			{"half_dy",  T_UBF, 14, BITFIELD(2, 0, 15)},
			{"bin_rot",  T_UBF, 16, BITFIELD(2, 0, 11)},
			{"stop",  T_BMB, 18, 0x01},
			{"cream",  T_BMB, 18, 0x02},
			{"thermals",  T_BMB, 18, 0x04},
			{"first",  T_BMB, 18, 0x08},
			{"name",  T_STR, 19, 5},
			TERM
		},
	},
	{ PCB_EGKW_SECT_PIN, 0xFF7F, "pin",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"function", T_UBF, 2, BITFIELD(1, 0, 1)},
			{"visible", T_UBF, 2, BITFIELD(1, 6, 7)},
			{"x",  T_INT, 4, 4},
			{"y",  T_INT, 8, 4},
			{"direction",  T_UBF, 12, BITFIELD(1, 0, 3)},
			{"length",  T_UBF, 12, BITFIELD(1, 4, 5)},
			{"bin_rot",  T_UBF, 12, BITFIELD(1, 6, 7)},
			{"direction",  T_UBF, 12, BITFIELD(1, 0, 3)},
			{"swaplevel",  T_INT, 13, 1},
			{"name",  T_STR, 14, 10},
			TERM
		},
	},
	{ PCB_EGKW_SECT_GATE, 0xFF7F, "gate",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"x",  T_INT, 4, 4},
			{"y",  T_INT, 8, 4},
			{"addlevel",  T_INT, 12, 1},
			{"swap",  T_INT, 13, 1},
			{"symno",  T_INT, 14, 2},
			{"name",  T_STR, 16, 8},
			TERM
		},
	},
	{ PCB_EGKW_SECT_ELEMENT, 0xFF53, "element",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			{2, 2, SS_DIRECT, NULL},
			TERM
		},
		{ /* attributes */
			{"x",  T_INT, 4, 4},
			{"y",  T_INT, 8, 4},
			{"library",  T_INT, 12, 2},
			{"package",  T_INT, 14, 2},
			{"bin_rot",  T_UBF, 16, BITFIELD(2, 0, 11)}, /* result is n*1024 */
			{"mirrored", T_BMB, 17, 0x10},
			{"spin", T_BMB, 17, 0x40},
			TERM
		},
	},
	{ PCB_EGKW_SECT_ELEMENT2, 0xFF5F, "element2",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"name",  T_STR, 2, 8},
			{"value",  T_STR, 10, 14},
			TERM
		},
	},
	{ PCB_EGKW_SECT_INSTANCE, 0xFFFF, "instance",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			{2, 2, SS_DIRECT, NULL},
			TERM
		},
		{ /* attributes */
			{"x",  T_INT, 4, 4},
			{"y",  T_INT, 8, 4},
			{"placed", T_INT, 12, 2}, /* == True for v4 */
			{"gateno", T_INT, 14, 2},
			{"bin_rot",  T_UBF, 16, BITFIELD(2, 10, 11)}, /* was 0, 11}, */
			/* _get_uint16_mask(16, 0x0c00) */
			{"mirrored",  T_UBF, 16, BITFIELD(2, 12, 12)},
			/* _get_uint16_mask(16, 0x1000) */
			{"smashed",  T_BMB, 18, 0x01},
			TERM
		},
	},
	{ PCB_EGKW_SECT_TEXT, 0xFF53, "text",  /* basic text block */
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"layer", T_UBF, 3, BITFIELD(1,0,7)},
			{"x",  T_INT, 4, 4},
			{"y",  T_INT, 8, 4},
			{"half_size",  T_INT, 12, 2},
			{"ratio", T_UBF, 14, BITFIELD(2, 2, 6)},
			/*self._get_uint8_mask(14, 0x7c) >> 2 },*/
			{"bin_rot" , T_UBF, 16, BITFIELD(2, 0, 11)},
			/*self._get_uint16_mask(16, 0x0fff)*/
			{"mirrored" , T_UBF, 16, BITFIELD(2, 12, 12)},
			/*bool(self._get_uint16_mask(16, 0x1000))*/
			{"spin" , T_UBF, 16, BITFIELD(2, 14, 14)},
			/*bool(self._get_uint16_mask(16, 0x4000))*/
			{"textfield",  T_STR, 18, 5},
			TERM
		},
	},
	{ PCB_EGKW_SECT_NETBUSLABEL, 0xFFFF, "netbuslabel",  /* text base section equiv. */
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"layer", T_UBF, 3, BITFIELD(1,0,7)},
			{"x",  T_INT, 4, 4},
			{"y",  T_INT, 8, 4},
			{"size",  T_INT, 12, 2},
			{"ratio", T_UBF, 14, BITFIELD(2, 2, 6)},
			/*self._get_uint8_mask(14, 0x7c) >> 2 },*/
			{"bin_rot" , T_UBF, 16, BITFIELD(2, 0, 11)},
			/*self._get_uint16_mask(16, 0x0fff)*/
			{"mirrored" , T_UBF, 16, BITFIELD(2, 12, 12)},
			/*bool(self._get_uint16_mask(16, 0x1000))*/
			{"spin" , T_UBF, 16, BITFIELD(2, 14, 14)},
			/*bool(self._get_uint16_mask(16, 0x4000))*/
			{"textfield",  T_STR, 18, 5},
			TERM
		},
	},
	{ PCB_EGKW_SECT_SMASHEDNAME, 0xFF73, "name", /* text base section equiv. */
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"layer", T_UBF, 3, BITFIELD(1,0,7)},
			{"x",  T_INT, 4, 4},
			{"y",  T_INT, 8, 4},
			{"size",  T_INT, 12, 2},
			{"ratio", T_UBF, 14, BITFIELD(2, 2, 6)},
			/*self._get_uint8_mask(14, 0x7c) >> 2 },*/
			{"bin_rot" , T_UBF, 16, BITFIELD(2, 0, 11)},
			/*self._get_uint16_mask(16, 0x0fff)*/
			{"mirrored" , T_UBF, 16, BITFIELD(2, 12, 12)},
			/*bool(self._get_uint16_mask(16, 0x1000))*/
			{"spin" , T_UBF, 16, BITFIELD(2, 14, 14)},
			/*bool(self._get_uint16_mask(16, 0x4000))*/
			{"textfield",  T_STR, 18, 5},
			TERM
		},
	},
	{ PCB_EGKW_SECT_SMASHEDVALUE, 0xFF73, "value",  /* text base section equiv. */
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"layer", T_UBF, 3, BITFIELD(1,0,7)},
			{"x",  T_INT, 4, 4},
			{"y",  T_INT, 8, 4},
			{"size",  T_INT, 12, 2},
			{"ratio", T_UBF, 14, BITFIELD(2, 2, 6)},
			/*self._get_uint8_mask(14, 0x7c) >> 2 },*/
			{"bin_rot" , T_UBF, 16, BITFIELD(2, 0, 11)},
			/*self._get_uint16_mask(16, 0x0fff)*/
			{"mirrored" , T_UBF, 16, BITFIELD(2, 12, 12)},
			/*bool(self._get_uint16_mask(16, 0x1000))*/
			{"spin" , T_UBF, 16, BITFIELD(2, 14, 14)},
			/*bool(self._get_uint16_mask(16, 0x4000))*/
			{"textfield",  T_STR, 18, 5},
			TERM
		},
	},
	{ PCB_EGKW_SECT_PACKAGEVARIANT, 0xFF7F, "packagevariant",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			{2, 2, SS_DIRECT, NULL},
			TERM
		},
		{ /* attributes */
			{"package", T_INT, 4, 2},
			{"table",  T_STR, 6, 13},
			{"name",  T_STR, 19, 5},
			TERM
		},
	},
	{ PCB_EGKW_SECT_DEVICE, 0xFF7F, "device",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			{2, 2, SS_RECURSIVE, "gates"}, /* gates */
			{4, 2, SS_RECURSIVE, "variants"}, /* variants */
			/* ? {2, 2, SS_DIRECT, NULL},*/
			TERM			
		},
		{ /* attributes */
			{"gates", T_INT, 2, 2},
			{"variants", T_INT, 4, 2},
			{"prefix",  T_STR, 8, 5},
			{"desc",  T_STR, 13, 5},
			{"name",  T_STR, 18, 5},
			TERM
		},
	},
	{ PCB_EGKW_SECT_PART, 0xFFFF, "part",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			{2, 2, SS_DIRECT, NULL},
			TERM
		},
		{ /* attributes */
			{"lib",  T_INT, 4, 2},
			{"device",  T_INT, 6, 2},
			{"variant",  T_INT, 8, 1},
			{"technology",  T_INT, 9, 2},
			{"name",  T_STR, 11, 5},
			{"value",  T_STR, 16, 8},
			TERM
		},
	},
	{ PCB_EGKW_SECT_SCHEMABUS, 0xFFFF },
	{ PCB_EGKW_SECT_VARIANTCONNECTIONS, 0xFF7F, "variantconnections",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			/* a sequence of 22 bytes or 11 H?, in format (x1,y1), (x2,y2), ... */
			TERM
		},
	},
	{ PCB_EGKW_SECT_SCHEMACONNECTION, 0xFFFF },
	{ PCB_EGKW_SECT_CONTACTREF, 0xFFF57, "contactref",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"partnumber",  T_INT, 4, 2},
			{"pin",  T_INT, 6, 2},
			TERM
		},
	},
	{ PCB_EGKW_SECT_SMASHEDPART, 0xFFFF, "smashedpart", /* same as text basesection */
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"layer", T_UBF, 3, BITFIELD(1,0,7)},
			{"x",  T_INT, 4, 4},
			{"y",  T_INT, 8, 4},
			{"size",  T_INT, 12, 2},
			{"ratio", T_UBF, 14, BITFIELD(2, 2, 6)},
			/*self._get_uint8_mask(14, 0x7c) >> 2 },*/
			{"bin_rot" , T_UBF, 16, BITFIELD(2, 0, 11)},
			/*self._get_uint16_mask(16, 0x0fff)*/
			{"mirrored" , T_UBF, 16, BITFIELD(2, 12, 12)},
			/*bool(self._get_uint16_mask(16, 0x1000))*/
			{"spin" , T_UBF, 16, BITFIELD(2, 14, 14)},
			/*bool(self._get_uint16_mask(16, 0x4000))*/
			{"textfield",  T_STR, 18, 5},
			TERM
		},
	},
	{ PCB_EGKW_SECT_SMASHEDGATE, 0xFFFF, "smashedgate", /* same as text basesection */
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"layer", T_UBF, 3, BITFIELD(1,0,7)},
			{"x",  T_INT, 4, 4},
			{"y",  T_INT, 8, 4},
			{"size",  T_INT, 12, 2},
			{"ratio", T_UBF, 14, BITFIELD(2, 2, 6)},
			/*self._get_uint8_mask(14, 0x7c) >> 2 },*/
			{"bin_rot" , T_UBF, 16, BITFIELD(2, 0, 11)},
			/*self._get_uint16_mask(16, 0x0fff)*/
			{"mirrored" , T_UBF, 16, BITFIELD(2, 12, 12)},
			/*bool(self._get_uint16_mask(16, 0x1000))*/
			{"spin" , T_UBF, 16, BITFIELD(2, 14, 14)},
			/*bool(self._get_uint16_mask(16, 0x4000))*/
			{"textfield",  T_STR, 18, 5},
			TERM
		},
	},
	{ PCB_EGKW_SECT_ATTRIBUTE, 0xFF7F, "attribute", /* same as text basesection */
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"layer", T_UBF, 3, BITFIELD(1,0,7)},
			{"x",  T_INT, 4, 4},
			{"y",  T_INT, 8, 4},
			{"size",  T_INT, 12, 2},
			{"ratio", T_UBF, 14, BITFIELD(2, 2, 6)},
			/*self._get_uint8_mask(14, 0x7c) >> 2 },*/
			{"bin_rot" , T_UBF, 16, BITFIELD(2, 0, 11)},
			/*self._get_uint16_mask(16, 0x0fff)*/
			{"mirrored" , T_UBF, 16, BITFIELD(2, 12, 12)},
			/*bool(self._get_uint16_mask(16, 0x1000))*/
			{"spin" , T_UBF, 16, BITFIELD(2, 14, 14)},
			/*bool(self._get_uint16_mask(16, 0x4000))*/
			{"textfield",  T_STR, 18, 5},
			TERM
		},
	},
	{ PCB_EGKW_SECT_ATTRIBUTEVALUE, 0xFFFF, "attribute-value",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"symbol",  T_STR, 2, 5},
			{"attribute",  T_STR, 7, 17},
			TERM
		},
	},
	{ PCB_EGKW_SECT_FRAME, 0xFFFF, "frame",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			/*{"drawables-subsec", T_INT, 2, 2},*/
			TERM
		},
		{ /* attributes */
			{"layer",  T_UBF, 3, BITFIELD(1,0,7)},
			{"x1",  T_INT, 4, 4},
			{"y1",  T_INT, 8, 4},
			{"x2",  T_INT, 12, 4},
			{"y2",  T_INT, 16, 4},
			{"cols",  T_INT, 20, 1},
			{"rows",  T_INT, 21, 1},
			{"borders",  T_INT, 22, 1},
			TERM
		},
	},
	{ PCB_EGKW_SECT_SMASHEDXREF, 0xFFFF, "smashedxref",
		{ /* field match */
			TERM
		},
		{ /* subsection sizes */
			TERM
		},
		{ /* attributes */
			{"layer", T_UBF, 3, BITFIELD(1,0,7)},
			{"x",  T_INT, 4, 4},
			{"y",  T_INT, 8, 4},
			{"size",  T_INT, 12, 2},
			{"ratio", T_UBF, 14, BITFIELD(2, 2, 6)},
			/*self._get_uint8_mask(14, 0x7c) >> 2 },*/
			{"bin_rot" , T_UBF, 16, BITFIELD(2, 0, 11)},
			/*self._get_uint16_mask(16, 0x0fff)*/
			{"mirrored" , T_UBF, 16, BITFIELD(2, 12, 12)},
			/*bool(self._get_uint16_mask(16, 0x1000))*/
			{"spin" , T_UBF, 16, BITFIELD(2, 14, 14)},
			/*bool(self._get_uint16_mask(16, 0x4000))*/
			{"textfield",  T_STR, 18, 5},
			TERM
		},
	},

	/* unknown leaves */
	{ 0x5300,  0xFFFF },
	{ 0x2d84,  0xFFFF },
	/* end of table */
	{ 0 }
};

static unsigned long load_ulong_se(unsigned char *src, int offs, unsigned long len, int sign_extend)
{
	int n;
	unsigned long l = 0;

	if ((sign_extend) && (src[offs+len-1] & 0x80))
		l = -1;

	for(n = 0; n < len; n++) {
		l <<= 8;
		l |= src[offs+len-n-1];
	}
	return l;
}

static unsigned long load_ulong(unsigned char *src, int offs, unsigned long len)
{
	return load_ulong_se(src, offs, len, 0);
}

static long load_long(unsigned char *src, int offs, unsigned long len)
{
	return (long)load_ulong_se(src, offs, len, 1);
}

static int load_bmb(unsigned char *src, int offs, unsigned long len)
{
	return !!(src[offs] & len);
}

static unsigned long load_ubf(unsigned char *src, int offs, unsigned long field)
{
	unsigned long val;
	int len, first, last, mask;

	len = (field >> 16) & 0xff;
	first = (field >> 8) & 0xff;
	last = field & 0xff;
	mask = (1 << (last - first + 1)) - 1;

	val = load_ulong(src, offs, len);
	val >>= first;
	return val & mask;
}

static char *load_str(unsigned char *src, char *dst, int offs, unsigned long len)
{
	memcpy(dst, src+offs, len);
	dst[len] = '\0';
	return dst;
}

static double load_double(unsigned char *src, int offs, unsigned long len)
{
	double d;
	assert(len == sizeof(double));
	memcpy(&d, src+offs, sizeof(double));
	return d;
}

/* a function to convert binary format rotations into XML compatible SMRXXX format */
int bin_rot2degrees(const char *rot, char *tmp, int mirrored, int spin)
{
	long deg;
	char *end, *cend;
	if (rot == NULL) {
		return -1;
	} else {
		end = tmp;
		if (spin) *end++ = 'S';
		if (mirrored) *end++ = 'M';
		*end++ = 'R';
		end[0] = '0';
		end[1] = '\0'; /* default "R0" for v3,4,5 where bin_rot == 0*/

		deg = strtol(rot, &cend, 10);
		/*printf("Calculated deg == %ld pre bin_rot2degree conversion\n", deg);*/
		if (*cend != '\0') {
TODO(": convert this to proper error reporting")
			printf("unexpected binary field 'rot' value suffix\n");
			return -1;
		}
		if (deg >= 1024) {
			deg = (360*deg)/4096; /* v4, v5 do n*1024 */
			sprintf(end, "%ld", deg);
			/*printf("Did deg == %ld bin_rot2degree conversion\n", deg);*/
			return 0;
		} else if (deg > 0) { /* v3 */
			deg = deg & 0x00f0; /* only need bottom 4 bits for v3, it seems*/
			deg = deg*90;
			/*printf("About to do deg < 1024 bin_rot2degree conversion\n");*/
			sprintf(end, "%ld", deg);
			return 0;
		} else {
			/*printf("Default deg == 0 bin_rot2degree conversion\n");*/
			return 0;
		}
	}
	return -1; /* should not get here */
}

static const char *egb_next_long_text(egb_ctx_t *egb_ctx)
{
	const char *res = egb_ctx->free_text_cursor;

	if (res == NULL) {
		rnd_message(RND_MSG_ERROR, "Invalid free-text reference (out of strings)\n");
		return "<invalid>";
	}

	/* seek the next string */
	while(*egb_ctx->free_text_cursor != '\0') egb_ctx->free_text_cursor++;
	egb_ctx->free_text_cursor++;

	/* detect end of list by the terminating empty string */
	if (*egb_ctx->free_text_cursor == '\0')
		egb_ctx->free_text_cursor = NULL;

	return res;
}

int read_notes(void *ctx, FILE *f, const char *fn, egb_ctx_t *egb_ctx)
{
	unsigned char block[8];
	int text_remaining = 0;
	egb_ctx->free_text_len = 0;
	egb_ctx->free_text = NULL;
	egb_ctx->free_text_cursor = NULL;

	if (fread(block, 1, 8, f) != 8) {
		rnd_message(RND_MSG_ERROR, "Short read in free text section preamble. Text section not found.\n");
		return -1;
	}

	if (load_long(block, 0, 1) == 0x13 && load_long(block, 1, 1) == 0x12) {
	}
	else {
		rnd_message(RND_MSG_ERROR, "Failed to find 0x1312 start of pre-DRC free text section.\n");
		return -1;
	}

	text_remaining = egb_ctx->free_text_len = (int)load_long(block, 4, 2);
	text_remaining += 4; /* there seems to be a 4 byte checksum or something at the end */

TODO("TODO instead of skipping the text, we need to load it completely with drc_ctx->free_text pointing to it")
	egb_ctx->free_text = malloc(text_remaining);
	if (fread(egb_ctx->free_text, 1, text_remaining, f) != text_remaining) {
		rnd_message(RND_MSG_ERROR, "Short read: free text block content. Truncated file?\n");
		return -1;
	}
	egb_ctx->free_text_cursor = egb_ctx->free_text;

	return 0;
}

int read_drc(void *ctx, FILE *f, const char *fn, egb_ctx_t *drc_ctx)
{
	unsigned char block[4];
	int DRC_length_used = 244;
	unsigned char DRC_block[244];
	unsigned char c;
	int DRC_preamble_end_found = 0;

	/* these are sane, default values for DRC in case not present, i.e. in v3 no DRC section */
	drc_ctx->mdWireWire = 12;
	drc_ctx->msWidth = 10;
	drc_ctx->rvPadTop = 0.25;
	drc_ctx->rvPadInner = 0.25;
	drc_ctx->rvPadBottom = 0.25;

	if (fread(block, 1, 4, f) != 4) {
TODO(": convert this to proper error reporting")
		rnd_trace("E: short attempted DRC preamble read; preamble not found. Truncated file?\n");
		return -1;
	}

	/* look for DRC start sentinel */
	if (!(load_long(block, 0, 1) == 0x10
		&& load_long(block, 1, 1) == 0x04
		&& load_long(block, 2, 1) == 0x00
		&& load_long(block, 3, 1) == 0x20)) {
TODO(": convert this to proper error reporting")
		rnd_trace("E: start of DRC preamble not found where it was expected.\n");
		rnd_trace("E: drc byte 0 : %d\n", (int)load_long(block, 0, 1) );
		rnd_trace("E: drc byte 1 : %d\n", (int)load_long(block, 1, 1) );
		rnd_trace("E: drc byte 2 : %d\n", (int)load_long(block, 2, 1) );
		rnd_trace("E: drc byte 3 : %d\n", (int)load_long(block, 3, 1) );
		return -1;
	}

	while (!DRC_preamble_end_found) {
		if (fread(&c, 1, 1, f) != 1) { /* the text preamble is not necessarily n * 4 bytes */
TODO(": convert this to proper error reporting")
			rnd_trace("E: short attempted DRC preamble read. Truncated file?\n");
			return -1;
		} else {
			if (c == '\0') { /* so we step through, looking for each 0x00 */
				if (fread(block, 1, 4, f) != 4) { /* the text preamble seems to n * 24 bytes */
TODO(": convert this to proper error reporting")
					rnd_trace("E: short attempted DRC preamble read. Truncated file?\n");
					return -1;
				}
				if (load_long(block, 0, 1) == 0x78
					&& load_long(block, 1, 1) == 0x56
					&& load_long(block, 2, 1) == 0x34
					&& load_long(block, 3, 1) == 0x12) {
						DRC_preamble_end_found = 1;
				}
			}
		}
	}

	if (fread(DRC_block, 1, DRC_length_used, f) != DRC_length_used) {
TODO(": convert this to proper error reporting")
		rnd_trace("E: short DRC value block read. DRC section incomplete. Truncated file?\n");
		return -1;
	}

	/* first ~134 bytes contain the most useful DRC stuff, such as
	wire2wire wire2pad wire2via pad2pad pad2via via2via pad2smd via2smd smd2smd
	also, restring order: padtop padinner padbottom viaouter viainner
	(microviaouter microviainner) */

	drc_ctx->mdWireWire = (long)(load_long(DRC_block, 0, 4)/2.54/100);
	drc_ctx->wire2pad = (long)(load_long(DRC_block, 4, 4)/2.54/100);
	drc_ctx->wire2via = (long)(load_long(DRC_block, 8, 4)/2.54/100);
	drc_ctx->pad2pad = (long)(load_long(DRC_block, 12, 4)/2.54/100);
	drc_ctx->pad2via = (long)(load_long(DRC_block, 16, 4)/2.54/100);
	drc_ctx->via2via = (long)(load_long(DRC_block, 20, 4)/2.54/100);
	drc_ctx->pad2smd = (long)(load_long(DRC_block, 24, 4)/2.54/100);
	drc_ctx->via2smd = (long)(load_long(DRC_block, 28, 4)/2.54/100);
	drc_ctx->smd2smd = (long)(load_long(DRC_block, 32, 4)/2.54/100);
	drc_ctx->copper2dimension = (long)(load_long(DRC_block, 44, 4)/2.54/100);
	drc_ctx->drill2hole = (long)(load_long(DRC_block, 52, 4)/2.54/100);

	drc_ctx->msWidth = (long)(load_long(DRC_block, 64, 4)/2.54/100);
	drc_ctx->minDrill = (long)(load_long(DRC_block, 68, 4)/2.54/100);

	/*in version 5, this is wedged inbetween drill and pad ratios:
	  min_micro_via, blind_via_ratio, int, float, 12 bytes*/

	drc_ctx->rvPadTop = load_double(DRC_block, 84, 8);
	drc_ctx->rvPadInner = load_double(DRC_block, 92, 8);
	drc_ctx->rvPadBottom = load_double(DRC_block, 100, 8);

	drc_ctx->rvViaOuter = load_double(DRC_block, 108, 8);
	drc_ctx->rvViaInner = load_double(DRC_block, 116, 8);
	drc_ctx->rvMicroViaOuter = load_double(DRC_block, 124, 8);
	drc_ctx->rvMicroViaInner = load_double(DRC_block, 132, 8);

	/* not entirely sure what these do as they don't map obviously to XML */
	drc_ctx->restring_limit1_mil = (long)load_long(DRC_block, 140, 4)/2.54/100;
	drc_ctx->restring_limit2_mil = (long)load_long(DRC_block, 144, 4)/2.54/100;
	drc_ctx->restring_limit3_mil = (long)load_long(DRC_block, 148, 4)/2.54/100;
	drc_ctx->restring_limit4_mil = (long)load_long(DRC_block, 152, 4)/2.54/100;
	drc_ctx->restring_limit5_mil = (long)load_long(DRC_block, 156, 4)/2.54/100;
	drc_ctx->restring_limit6_mil = (long)load_long(DRC_block, 160, 4)/2.54/100;
	drc_ctx->restring_limit7_mil = (long)load_long(DRC_block, 164, 4)/2.54/100;
	drc_ctx->restring_limit8_mil = (long)load_long(DRC_block, 168, 4)/2.54/100;
	drc_ctx->restring_limit9_mil = (long)load_long(DRC_block, 172, 4)/2.54/100;
	drc_ctx->restring_limit10_mil = (long)load_long(DRC_block, 176, 4)/2.54/100;
	drc_ctx->restring_limit11_mil = (long)load_long(DRC_block, 180, 4)/2.54/100;
	drc_ctx->restring_limit12_mil = (long)load_long(DRC_block, 184, 4)/2.54/100;
	drc_ctx->restring_limit13_mil = (long)load_long(DRC_block, 188, 4)/2.54/100;
	drc_ctx->restring_limit14_mil = (long)load_long(DRC_block, 192, 4)/2.54/100;

	/* pad shapes */
	drc_ctx->psTop = load_long(DRC_block, 196, 4);
	drc_ctx->psBottom = load_long(DRC_block, 200, 4);
	drc_ctx->psFirst = load_long(DRC_block, 204, 4);

	/* not sure how these map to XML design rules parameters*/
	drc_ctx->mask_percentages1_ratio = load_double(DRC_block, 208, 8);
	drc_ctx->mask_percentages2_ratio = load_double(DRC_block, 216, 8);
	drc_ctx->mask_limit1_mil = (long)load_long(DRC_block, 224, 4)/2.54/100;
	drc_ctx->mask_limit2_mil = (long)load_long(DRC_block, 228, 4)/2.54/100;
	drc_ctx->mask_limit3_mil = (long)load_long(DRC_block, 232, 4)/2.54/100;
	drc_ctx->mask_limit4_mil = (long)load_long(DRC_block, 236, 4)/2.54/100;

	/* populate the drc_ctx struct to return the result of the DRC block read attempt */

	return 0;
}


int read_block(long *numblocks, int level, void *ctx, FILE *f, const char *fn, egb_node_t *parent)
{
	unsigned char block[24];
	const pcb_eagle_script_t *sc;
	const subsect_t *ss;
	const attrs_t *at;
	const fmatch_t *fm;
	char ind[256];
	int processed = 0;
	egb_node_t *lpar;

	memset(ind, ' ', level);
	ind[level] = '\0';

	/* load the current block */
	if (fread(block, 1, 24, f) != 24) {
TODO(": convert this to proper error reporting")
		rnd_trace("E: short read\n");
		return -1;
	}
	processed++;

	if (*numblocks < 0 && load_long(block, 0, 1) == 0x10) {
		*numblocks = load_long(block, 4, 4);
	}

	for(sc = pcb_eagle_script; sc->cmd != 0; sc++) {
		int match = 1;
		unsigned int cmdh = (sc->cmd >> 8) & 0xFF, cmdl = sc->cmd & 0xFF;
		unsigned int mskh = (sc->cmd_mask >> 8) & 0xFF, mskl = sc->cmd_mask & 0xFF;

		assert((cmdh & mskh) == cmdh);
		assert((cmdl & mskl) == cmdl);

		if ((cmdh != (block[0] & mskh)) || (cmdl != (block[1] & mskl)))
			continue;

		for(fm = sc->fmatch; fm->offs != 0; fm++) {
			if (load_long(block, fm->offs, fm->len) != fm->val) {
				match = 0;
				break;
			}
		}
		if (match)
			goto found;
	}

TODO(": convert this to proper error reporting")
	rnd_trace("E: unknown block ID 0x%02x%02x at offset %ld\n", block[0], block[1], ftell(f));
	return -1;

	found:;

	if (sc->name != NULL)
		parent = egb_node_append(parent, egb_node_alloc(sc->cmd, sc->name));
	else
		parent = egb_node_append(parent, egb_node_alloc(sc->cmd, "UNKNOWN"));

	for(at = sc->attrs; at->name != NULL; at++) {
		char buff[128];
		*buff = '\0';
		switch(at->type) {
			case T_BMB:
				sprintf(buff, "%d", load_bmb(block, at->offs, at->len));
				break;
			case T_UBF:
				sprintf(buff, "%ld", load_ubf(block, at->offs, at->len));
				break;
			case T_INT:
				sprintf(buff, "%ld", load_long(block, at->offs, at->len));
				break;
			case T_DBL:
				sprintf(buff, "%f", load_double(block, at->offs, at->len));
				break;
			case T_STR:
				load_str(block, buff, at->offs, at->len);
				break;
		}
		egb_node_prop_set(parent, at->name, buff);
	}

	*numblocks = *numblocks - 1;

	for(ss = sc->subs; ss->offs != 0; ss++) {
		unsigned long int n, numch = load_ulong(block, ss->offs, ss->len);

		if (ss->ss_type == SS_DIRECT) {
			long lp = 0;
			if (ss->tree_name != NULL)
				lpar = egb_node_append(parent, egb_node_alloc(0, ss->tree_name));
			else
				lpar = parent;
			for(n = 0; n < numch && *numblocks > 0; n++) {
				int res = read_block(numblocks, level+1, ctx, f, fn, lpar);
				if (res < 0)
					return res;
				processed += res;
				lp += res;
			}
		}
		else {
			long rem, lp = 0;
			if (ss->tree_name != NULL)
				lpar = egb_node_append(parent, egb_node_alloc(0, ss->tree_name));
			else
				lpar = parent;
			if (ss->ss_type == SS_RECURSIVE_MINUS_1)
				numch--;
			rem = numch;
			for(n = 0; n < numch && rem > 0; n++) {
				int res = read_block(&rem, level+1, ctx, f, fn, lpar);
				if (res < 0)
					return res;
				*numblocks -= res;
				processed += res;
				lp += res;
			}
		}
	}

	return processed;
}

static egb_node_t *find_node(egb_node_t *first, int id)
{
	egb_node_t *n;

	for(n = first; n != NULL; n = n->next)
		if (n->id == id)
			return n;

	return NULL;
}

static egb_node_t *find_node_name(egb_node_t *first, const char *id_name)
{
	egb_node_t *n;

	for(n = first; n != NULL; n = n->next)
		if (strcmp(n->id_name, id_name) == 0)
			return n;

	return NULL;
}

static long fix_three_byte_read(long num, int negflag) { /* used to turn three byte T_INT read into 3 byte uint value */
	if (num < 0 && negflag) {
		return num;
	} else if (num > 0 && negflag) { /* this should only happen with 'c' built from three distinct bytes */
		return num - 0x800000;
	} else if (num < 0 && !negflag) {
                return num + 0x800000;
	}
        return num; /* i.e. if (num > 0 && !negflag) */
}

static long fix_one_byte_read(long num) { /* used to turn one byte T_INT read into 1 byte uint value */
	if (num < 0) {
		return (num + 0x80);
	}
	return num;
}

static int arc_decode(void *ctx, egb_node_t *elem, int arctype, int linetype)
{
	htss_entry_t *e;

	char itoa_buffer[64];
	long c, cx, cy, x1, x2, y1, y2, x3, y3, radius;
	double theta_1, theta_2, delta_theta;
	double delta_x, delta_y;
	int arc_flags = 0;
	int clockwise = 0;
	c = 0;
	if (linetype == 129 || arctype == 0) {
		arc_flags = atoi(egb_node_prop_get(elem, "arc_negflags"));
		for (e = htss_first(&elem->props); e; e = htss_next(&elem->props, e)) {
			if (strcmp(e->key, "arc_x1") == 0) {
				x1 = fix_three_byte_read(atoi(e->value), arc_flags & 0x02);
				sprintf(itoa_buffer, "%ld", x1);
				egb_node_prop_set(elem, "x1", itoa_buffer);
			} else if (strcmp(e->key, "arc_y1") == 0) {
				y1 = fix_three_byte_read(atoi(e->value), arc_flags & 0x04);
				sprintf(itoa_buffer, "%ld", y1);
				egb_node_prop_set(elem, "y1", itoa_buffer);
			} else if (strcmp(e->key, "arc_x2") == 0) {
				x2 = fix_three_byte_read(atoi(e->value), arc_flags & 0x08);
				sprintf(itoa_buffer, "%ld", x2);
				egb_node_prop_set(elem, "x2", itoa_buffer);
			} else if (strcmp(e->key, "arc_y2") == 0) {
				y2 = fix_three_byte_read(atoi(e->value), arc_flags & 0x10);
				sprintf(itoa_buffer, "%ld", y2);
				egb_node_prop_set(elem, "y2", itoa_buffer);
			} else if (strcmp(e->key, "arc_c1") == 0) {
				c += fix_one_byte_read(atoi(e->value));
			} else if (strcmp(e->key, "arc_c2") == 0) {
				c += 256*fix_one_byte_read(atoi(e->value));
			} else if (strcmp(e->key, "arc_c3") == 0) {
				c += 256*256*fix_one_byte_read(atoi(e->value));
			} else if (strcmp(e->key, "clockwise") == 0) {
				clockwise = atoi(e->value);
			}
		}
		c = fix_three_byte_read(c, arc_flags & 0x01);
		x3 = (x1+x2)/2;
		y3 = (y1+y2)/2;

		if (RND_ABS(x2-x1) < RND_ABS(y2-y1)) {
			cx = c;
			cy = (long)((x3-cx)*(x2-x1)/(double)((y2-y1)) + y3);
		} else {
			cy = c;
			cx = (long)((y3-cy)*(y2-y1)/(double)((x2-x1)) + x3);
		}
		radius = (long)rnd_distance((double)cx, (double)cy, (double)x2, (double)y2);
		sprintf(itoa_buffer, "%ld", radius);
		egb_node_prop_set(elem, "radius", itoa_buffer);
		sprintf(itoa_buffer, "%ld", cx);
		egb_node_prop_set(elem, "x", itoa_buffer);
		sprintf(itoa_buffer, "%ld", cy);
		egb_node_prop_set(elem, "y", itoa_buffer);

		if (cx == x2 && cy == y1 && x2 < x1 && y2 > y1) { /* RUQ */
			egb_node_prop_set(elem, "StartAngle", "90");
			egb_node_prop_set(elem, "Delta", "90");
		} else if (cx == x1 && cy == y2 && x2 < x1 && y1 > y2) { /* LUQ */
			egb_node_prop_set(elem, "StartAngle", "0");
			egb_node_prop_set(elem, "Delta", "90");
		} else if (cx == x2 && cy == y1 && x2 > x1 && y1 > y2) { /* LLQ */
			egb_node_prop_set(elem, "StartAngle", "270");
			egb_node_prop_set(elem, "Delta", "90");
		} else if (cx == x1 && cy == y2 && x2 > x1 && y2 > y1) { /* RLQ */
			egb_node_prop_set(elem, "StartAngle", "180");
			egb_node_prop_set(elem, "Delta", "90");
		} else {
			delta_x = (double)(x1 - cx);
			delta_y = (double)(y1 - cy);
			theta_1 = RND_RAD_TO_DEG*atan2(-delta_y, delta_x);
			theta_2 = RND_RAD_TO_DEG*atan2(-(y2 - cy), (x2 - cx));

			theta_1 = 180 - theta_1; /* eagle coord system */
			theta_2 = 180 - theta_2; /* eagle coord system */

			delta_theta = (theta_2 - theta_1);

			while (theta_1 > 360) {
				theta_1 -= 360;
			}

			while (delta_theta < -180) { /* this seems to fix pathological cases */
				delta_theta += 360;
			}
			while (delta_theta > 180) {
				delta_theta -= 360;
			}

			sprintf(itoa_buffer, "%ld", (long)(theta_1));
			egb_node_prop_set(elem, "StartAngle", itoa_buffer);
			sprintf(itoa_buffer, "%ld", (long)(delta_theta));
			egb_node_prop_set(elem, "Delta", itoa_buffer);
		}
TODO("TODO still need to fine tune non-trivial non 90 degree arcs start and delta for 0x81, 0x00")
	} else if ((linetype > 0 && linetype < 129) || arctype > 0) {
		int x1_ok, x2_ok, y1_ok, y2_ok, cxy_ok;
		x1_ok = x2_ok = y1_ok = y2_ok = cxy_ok = 0;
		for (e = htss_first(&elem->props); e; e = htss_next(&elem->props, e)) {
			if (strcmp(e->key, "arctype_other_x1") == 0) {
				x1 = atoi(e->value);
				x1_ok = 1;
			} else if (strcmp(e->key, "arctype_other_y1") == 0) {
				y1 = atoi(e->value);
				y1_ok = 1;
			} else if (strcmp(e->key, "arctype_other_x2") == 0) {
				x2 = atoi(e->value);
				x2_ok = 1;
			} else if (strcmp(e->key, "arctype_other_y2") == 0) {
				y2 = atoi(e->value);
				y2_ok = 1;
			} else if (strcmp(e->key, "linetype_0_x1") == 0) {
				x1 = atoi(e->value);
				x1_ok = 1;
			} else if (strcmp(e->key, "linetype_0_y1") == 0) {
				y1 = atoi(e->value);
				y1_ok = 1;
			} else if (strcmp(e->key, "linetype_0_x2") == 0) {
				x2 = atoi(e->value);
				x2_ok = 1;
			} else if (strcmp(e->key, "linetype_0_y2") == 0) {
				y2 = atoi(e->value);
				y2_ok = 1;
			}
			
		}
		if (linetype == 0x78 || arctype == 0x01) {
			cx = MIN(x1, x2);
			cy = MIN(y1, y2);
			cxy_ok = 1;
			egb_node_prop_set(elem, "StartAngle", "180");
			egb_node_prop_set(elem, "Delta", "90");
		} else if (linetype == 0x79 || arctype == 0x02) {
			cx = MAX(x1, x2);
			cy = MIN(y1, y2);
			cxy_ok = 1;
			egb_node_prop_set(elem, "StartAngle", "270");
			egb_node_prop_set(elem, "Delta", "90");
		} else if (linetype == 0x7a || arctype == 0x03) {
			cx = MAX(x1, x2);
			cy = MAX(y1, y2);
			cxy_ok = 1;
			egb_node_prop_set(elem, "StartAngle", "0");
			egb_node_prop_set(elem, "Delta", "90");
		} else if (linetype == 0x7b || arctype == 0x04) {
			cx = MIN(x1, x2);
			cy = MAX(y1, y2);
			cxy_ok = 1;
			egb_node_prop_set(elem, "StartAngle", "90");
			egb_node_prop_set(elem, "Delta", "90");
		} else if (linetype == 0x7c || arctype == 0x05) { /* 124 */
			cx = (x1 + x2)/2;
			cy = (y1 + y2)/2;
			cxy_ok = 1;
			egb_node_prop_set(elem, "StartAngle", "90");
			egb_node_prop_set(elem, "Delta", "180");
		} else if (linetype == 0x7d || arctype == 0x06) { /* 125 */
			cx = (x1 + x2)/2;
			cy = (y1 + y2)/2;
			cxy_ok = 1;
			egb_node_prop_set(elem, "StartAngle", "270");
			egb_node_prop_set(elem, "Delta", "180");
		} else if (linetype == 0x7e || arctype == 0x07) {
			cx = (x1 + x2)/2;
			cy = (y1 + y2)/2;
			cxy_ok = 1;
			egb_node_prop_set(elem, "StartAngle", "180");
			egb_node_prop_set(elem, "Delta", "180");
		} else if (linetype == 0x7f || arctype == 0x08) {
			cx = (x1 + x2)/2;
			cy = (y1 + y2)/2;
			cxy_ok = 1;
			egb_node_prop_set(elem, "StartAngle", "0");
			egb_node_prop_set(elem, "Delta", "180");
		}

		if (!cxy_ok) {
			rnd_message(RND_MSG_ERROR, "cx and cy not set in arc/linetype: %d/%d\n", linetype, arctype);
			cx = cy = 0;
		} else if (!(x1_ok && x2_ok && y1_ok && y2_ok)) {
			rnd_message(RND_MSG_ERROR, "x1/2 or y1/2 not set in binary arc\n");
		}
		radius = (long)(rnd_distance((double)cx, (double)cy, (double)x2, (double)y2));
		sprintf(itoa_buffer, "%ld", radius);
		egb_node_prop_set(elem, "radius", itoa_buffer);

		sprintf(itoa_buffer, "%ld", cx);
		egb_node_prop_set(elem, "x", itoa_buffer);
		sprintf(itoa_buffer, "%ld", cy);
		egb_node_prop_set(elem, "y", itoa_buffer);

	}
	return 0;
}

/* Return the idxth library from the libraries subtree, or NULL if not found */
static egb_node_t *library_ref_by_idx(egb_node_t *libraries, long idx)
{
	egb_node_t *n;

	/* count children of libraries */
	for(n = libraries->first_child; (n != NULL) && (idx > 1); n = n->next, idx--) ;
	if (n == NULL)
		rnd_message(RND_MSG_ERROR, "io_eagle bin: eagle_library_ref_by_idx() can't find library index %ld\n", idx);
	return n;
}

/* Return the idxth package from the library subtree, or NULL if not found */
static egb_node_t *package_ref_by_idx(egb_node_t *library, long idx)
{
	egb_node_t *n, *pkgs;

	/* find library/0x1500->packages/0x1900 node */
	for(pkgs = library->first_child; (pkgs != NULL) && ((pkgs->id & 0xFF00) != 0x1900); pkgs = pkgs->next);
	if (pkgs == NULL)
		rnd_message(RND_MSG_ERROR, "io_eagle bin: eagle_pkg_ref_by_idx() can't find packages node in library tree\n", idx);
	/* count children of library */
	for(n = pkgs->first_child; (n != NULL) && (idx > 1); n = n->next, idx--);
	if (n == NULL)
		rnd_message(RND_MSG_ERROR, "io_eagle bin: eagle_pkg_ref_by_idx() can't find package index %ld\n", idx);
	return n;
}

/* Return the idxth element instance from the elements subtree, or NULL if not found */
static egb_node_t *elem_ref_by_idx(egb_node_t *elements, long idx)
{
	egb_node_t *n;

	/* count children of elelements */
	for(n = elements->first_child; (n != NULL) && (idx > 1); n = n->next, idx--) ;
	if (n == NULL)
		rnd_message(RND_MSG_ERROR, "io_eagle bin: eagle_elem_ref_by_idx() can't find element placement index %ld\n", idx);
	return n;
}

/* allocate the pin name of the j-th pin of a given element in the elements subtree to contactref node cr; allocate default of pin_number if name text not found for pin */
static int cr_pin_name_by_elem_idx(egb_node_t *cr, egb_ctx_t *egb_ctx, long elem_idx)
{
	int pin_idx = (int)(atoi(egb_node_prop_get(cr, "pin")));
	int pin_num;
	egb_node_t *p, *lib, *pkg;
	egb_node_t *elements = egb_ctx->elements;
	egb_node_t *libraries = egb_ctx->libraries;
	egb_node_t *e = elem_ref_by_idx(elements, elem_idx);

	pin_num = pin_idx;
	if (e == NULL)
		return 1;

	lib = library_ref_by_idx(libraries, atoi(egb_node_prop_get(e, "library")));
	if (lib == NULL)
		return 1;

	pkg = package_ref_by_idx(lib, atoi(egb_node_prop_get(e, "package")));
	if (pkg == NULL)
		return 1;

	for (p = pkg->first_child; (p != NULL) && (pin_num > 1) ; p = p->next) {
		if ((p->id & 0xFF00) == 0x2a00 || (p->id & 0xFF00) == 0x2b00 || (p->id & 0xFF00) == 0x2c00 ) { /* we found a pad _or_ pin _or_ SMD */
			pin_num--;
		}
	}

	if (p == NULL || pin_num > 1) {
		egb_node_prop_set(cr, "pad", "PIN_NOT_FOUND");
		return 0;
	}
	
	if (egb_node_prop_get(p, "name")) {
		egb_node_prop_set(cr, "pad", egb_node_prop_get(p, "name"));
		return 0;
	} else {
		egb_node_prop_set(cr, "pad", egb_node_prop_get(cr, "pin"));
		return 0;
	}
}


/* Return the refdes of the idxth element instance from the elements subtree, or NULL if not found */
static const char *elem_refdes_by_idx(egb_node_t *elements, long idx)
{
	egb_node_t *e = elem_ref_by_idx(elements, idx);
	if (e == NULL)
		return NULL;
	return egb_node_prop_get(e, "name");
}

/* bin path walk; the ... is a 0 terminated list of pcb_eagle_binkw_t IDs */
static egb_node_t *tree_id_path(egb_node_t *subtree, ...)
{
	egb_node_t *nd = subtree;
	pcb_eagle_binkw_t target;
	va_list ap;

	va_start(ap, subtree);

	/* get next path element */
	while((target = va_arg(ap, pcb_eagle_binkw_t)) != 0) {
		/* look for target on this level */
		for(nd = nd->first_child;;nd = nd->next) {
			if (nd == NULL) {/* target not found on this level */
				va_end(ap);
				return NULL;
			}
			if (nd->id == target) /* found, skip to next level */
				break;
		}
	}

	va_end(ap);
	return nd;
}


static int postprocess_wires(void *ctx, egb_node_t *root)
{
	htss_entry_t *e;
	egb_node_t *n;
	int line_type = -1;
	long half_width = 0; 
	char tmp[32];

	if (root->id == PCB_EGKW_SECT_LINE) {
		for (e = htss_first(&root->props); e; e = htss_next(&root->props, e))
			if (strcmp(e->key, "linetype") == 0 && strcmp(e->value, "0") == 0) {
				line_type = 0;
			} else if (strcmp(e->key, "linetype") == 0) {
				line_type = atoi(e->value);
			}
	}
	if(line_type == 0) {
		for (e = htss_first(&root->props); e; e = htss_next(&root->props, e)) {
			if (strcmp(e->key, "linetype_0_x1") == 0) {
				egb_node_prop_set(root, "x1", e->value);
			} else if (strcmp(e->key, "linetype_0_y1") == 0) {
				egb_node_prop_set(root, "y1", e->value);
			} else if (strcmp(e->key, "linetype_0_x2") == 0) {
				egb_node_prop_set(root, "x2", e->value);
			} else if (strcmp(e->key, "linetype_0_y2") == 0) {
				egb_node_prop_set(root, "y2", e->value);
			} else if (strcmp(e->key, "half_width") == 0) {
				half_width = atoi(e->value);
				sprintf(tmp, "%ld", half_width*2);
				egb_node_prop_set(root, "width", tmp);
			} /* <- added width doubling routine here */
		}
	} else if (line_type > 0) { /* avoid parsing non wire and non arc elements */
		for (e = htss_first(&root->props); e; e = htss_next(&root->props, e)) {
			if (strcmp(e->key, "half_width") == 0) {
				half_width = atoi(e->value);
				sprintf(tmp, "%ld", half_width*2);
				egb_node_prop_set(root, "width", tmp);
                        }
		}
		arc_decode(ctx, root, -1, line_type);
	}

	for(n = root->first_child; n != NULL; n = n->next)
		postprocess_wires(ctx, n);
	return 0;
}

static int postprocess_arcs(void *ctx, egb_node_t *root)
{
	htss_entry_t *e;
	egb_node_t *n;
	int arc_type = -1;
	long half_width= 0;
	char tmp[32];

	if (root->id == PCB_EGKW_SECT_ARC) {
		for (e = htss_first(&root->props); e; e = htss_next(&root->props, e))
			if (strcmp(e->key, "arctype") == 0 && strcmp(e->value, "0") == 0) {
				arc_type = 0;
			} else if (strcmp(e->key, "arctype") == 0) { /* found types 5, 3, 2 so far */
				arc_type = atoi(e->value);
			}
	}

	if (arc_type == 0) {
		for (e = htss_first(&root->props); e; e = htss_next(&root->props, e)) {
			if (strcmp(e->key, "arc_x1") == 0) {
				egb_node_prop_set(root, "x1", e->value);
			} else if (strcmp(e->key, "arc_y1") == 0) {
				egb_node_prop_set(root, "y1", e->value);
			} else if (strcmp(e->key, "arc_x2") == 0) {
				egb_node_prop_set(root, "x2", e->value);
			} else if (strcmp(e->key, "arc_y2") == 0) {
				egb_node_prop_set(root, "y2", e->value);
			} else if (strcmp(e->key, "half_width") == 0) {
				half_width = atoi(e->value);
				sprintf(tmp, "%ld", half_width*2);
				egb_node_prop_set(root, "width", tmp);
			} /* <- added width doubling routine here */
		}
	} else if (arc_type > 0) { /* avoid further parsing of non arc elements */
		for (e = htss_first(&root->props); e; e = htss_next(&root->props, e)) {
			if (strcmp(e->key, "arctype_other_x1") == 0) {
				egb_node_prop_set(root, "x1", e->value);
			} else if (strcmp(e->key, "arctype_other_y1") == 0) {
				egb_node_prop_set(root, "y1", e->value);
			} else if (strcmp(e->key, "arctype_other_x2") == 0) {
				egb_node_prop_set(root, "x2", e->value);
			} else if (strcmp(e->key, "arctype_other_y2") == 0) {
				egb_node_prop_set(root, "y2", e->value);
			} else if (strcmp(e->key, "half_width") == 0) {
				half_width = atoi(e->value);
				sprintf(tmp, "%ld", half_width*2);
				egb_node_prop_set(root, "width", tmp);
			} /* <- added width doubling routine here */
		}
	}

	arc_decode(ctx, root, arc_type, -1);
	for(n = root->first_child; n != NULL; n = n->next)
		postprocess_arcs(ctx, n);
	return 0;
}

/* post process circles to double their width */ 
static int postprocess_circles(void *ctx, egb_node_t *root)
{
	htss_entry_t *e;
	egb_node_t *n;
	long half_width= 0;
	char tmp[32];

	if (root->id == PCB_EGKW_SECT_CIRCLE) {
		for (e = htss_first(&root->props); e; e = htss_next(&root->props, e)) {
			if (strcmp(e->key, "half_width") == 0) {
				half_width = atoi(e->value);
				sprintf(tmp, "%ld", half_width*2);
				egb_node_prop_set(root, "width", tmp);
				break;
			} /* <- added width doubling routine here */
		}
	}

	for(n = root->first_child; n != NULL; n = n->next)
		postprocess_circles(ctx, n);
	return 0;
}

/* post process rotation to make the rotation values xml format 'RXXX' compliant */
static int postprocess_rotation(void *ctx, egb_node_t *root, int node_type)
{
	egb_node_t *n;
	char *v, tmp[32];
	int spin = 0, mirrored = 0; /* default not mirrored, not spinning */

	if (root != NULL && root->id == node_type) {
		v = htss_get(&root->props, "mirrored");
		if (v != NULL)
			mirrored = (*v != '0');

		v = htss_get(&root->props, "spin");
		if (v != NULL)
			spin = (*v != '0');

		v = htss_get(&root->props, "bin_rot");
		if (v != NULL) {
			bin_rot2degrees(v, tmp, mirrored, spin);
			egb_node_prop_set(root, "rot", tmp);
		}
	}
	for(n = root->first_child; n != NULL; n = n->next)
		postprocess_rotation(ctx, n, node_type);
	return 0;
}

/* we post process the PCB_EGKW_SECT_SMD nodes to double the dx and dy dimensions pre XML parsing */
static int postprocess_smd(void *ctx, egb_node_t *root)
{
	htss_entry_t *e;
	egb_node_t *n;
	long half_dx = 0;
	long half_dy = 0;
	char tmp[32];

	if (root != NULL && root->id == PCB_EGKW_SECT_SMD) {
		for (e = htss_first(&root->props); e; e = htss_next(&root->props, e)) {
			if (strcmp(e->key, "half_dx") == 0) {
				half_dx = atoi(e->value);
				sprintf(tmp, "%ld", half_dx*2);
				egb_node_prop_set(root, "dx", tmp);
			} else if (strcmp(e->key, "half_dy") == 0) {
				half_dy = atoi(e->value);
				sprintf(tmp, "%ld", half_dy*2);
				egb_node_prop_set(root, "dy", tmp);
			}
		}
	}

	for(n = root->first_child; n != NULL; n = n->next)
		postprocess_smd(ctx, n);
	return 0;
}

/* we post process the
PCB_EGKW_SECT_PAD and PCB_EGKW_SECT_HOLE and PCB_EGKW_SECT_VIA and PCB_EGKW_SECT_TEXT
nodes to double the drill, diameter, text height dimensions pre XML parsing */
static int postprocess_dimensions(void *ctx, egb_node_t *root)
{
	htss_entry_t *e;
	egb_node_t *n;
	long half_drill = 0;
	long half_diameter = 0;
	long half_size = 0;
	char tmp[32];
TODO("TODO padstacks - need to convert obround pins to appropriate padstack types")
	if (root != NULL && (root->id == PCB_EGKW_SECT_PAD
		|| root->id == PCB_EGKW_SECT_HOLE || root->id == PCB_EGKW_SECT_VIA
		|| root->id == PCB_EGKW_SECT_TEXT)) {
		for (e = htss_first(&root->props); e; e = htss_next(&root->props, e)) {
			if (strcmp(e->key, "half_drill") == 0) {
				half_drill = atoi(e->value);
				sprintf(tmp, "%ld", half_drill*2);
				egb_node_prop_set(root, "drill", tmp);
			} else if (strcmp(e->key, "half_diameter") == 0) {
				half_diameter = atoi(e->value);
				sprintf(tmp, "%ld", half_diameter*2);
				egb_node_prop_set(root, "diameter", tmp);
			} else if (strcmp(e->key, "half_size") == 0) {
				half_size = atoi(e->value);
				sprintf(tmp, "%ld", half_size*2);
				egb_node_prop_set(root, "size", tmp);
			}
		}
	}

	for(n = root->first_child; n != NULL; n = n->next)
		postprocess_dimensions(ctx, n);
	return 0;
}

static void fix_long_text(egb_ctx_t *egb_ctx, egb_node_t *root, const char *field_name)
{
	htss_entry_t *e;

	if (root->props.table == NULL) return;
	e = htss_getentry(&root->props, field_name);
	if (e == NULL) return;
	if (*e->value == 127) {
		free(e->value);
		e->value = rnd_strdup(egb_next_long_text(egb_ctx));
	}
}

/* we post process the PCB_EGKW_SECT_TEXT for 'free text' (long text) substitution */
static int postprocess_free_text(egb_ctx_t *egb_ctx, egb_node_t *root)
{
	egb_node_t *n;

	switch(root->id) {
		case PCB_EGKW_SECT_TEXT:
		case PCB_EGKW_SECT_NETBUSLABEL:
		case PCB_EGKW_SECT_SMASHEDNAME:
		case PCB_EGKW_SECT_SMASHEDVALUE:
		case PCB_EGKW_SECT_SMASHEDPART:
		case PCB_EGKW_SECT_SMASHEDGATE:
		case PCB_EGKW_SECT_ATTRIBUTE:
		case PCB_EGKW_SECT_SMASHEDXREF:
			fix_long_text(egb_ctx, root, "textfield");
			break;
		case PCB_EGKW_SECT_LAYER:
		case PCB_EGKW_SECT_LIBRARY:
		case PCB_EGKW_SECT_SIGNAL:
		case PCB_EGKW_SECT_SYMBOL:
		case PCB_EGKW_SECT_SCHEMANET:
		case PCB_EGKW_SECT_PAD:
		case PCB_EGKW_SECT_SMD:
		case PCB_EGKW_SECT_PIN:
		case PCB_EGKW_SECT_GATE:
			fix_long_text(egb_ctx, root, "name");
			break;
		case PCB_EGKW_SECT_ELEMENT2:
		case PCB_EGKW_SECT_PART:
			fix_long_text(egb_ctx, root, "name");
			fix_long_text(egb_ctx, root, "value");
			break;
		case PCB_EGKW_SECT_DEVICES:
		case PCB_EGKW_SECT_SYMBOLS:
			fix_long_text(egb_ctx, root, "library");
			break;
		case PCB_EGKW_SECT_PACKAGEVARIANT:
			fix_long_text(egb_ctx, root, "table");
			fix_long_text(egb_ctx, root, "name");
			break;
		case PCB_EGKW_SECT_PACKAGE:
			fix_long_text(egb_ctx, root, "desc");
			fix_long_text(egb_ctx, root, "name");
			break;
		case PCB_EGKW_SECT_PACKAGES:
			fix_long_text(egb_ctx, root, "desc");
			fix_long_text(egb_ctx, root, "library");
			break;
		case PCB_EGKW_SECT_SCHEMA:
			fix_long_text(egb_ctx, root, "xref_format");
			break;
		case PCB_EGKW_SECT_ATTRIBUTEVALUE:
			fix_long_text(egb_ctx, root, "symbol");
			fix_long_text(egb_ctx, root, "attribute");
			break;
		case PCB_EGKW_SECT_DEVICE:
			fix_long_text(egb_ctx, root, "prefix");
			fix_long_text(egb_ctx, root, "desc");
			fix_long_text(egb_ctx, root, "name");
			break;
		default:;
	}

	for(n = root->first_child; n != NULL; n = n->next)
		postprocess_free_text(egb_ctx, n);
	return 0;
}

/* look for contactrefs, and  1) append "name"="refdes" fields to contactref nodes as "element" "refdes" and 2) append "pad" = (pin_name(if present) or default pin_num) for netlist loading */
static int postproc_contactrefs(void *ctx, egb_ctx_t *egb_ctx)
{
	htss_entry_t *e;
	egb_node_t *cr, *n, *next, *next2;

	if (egb_ctx->signals == NULL)
		return 0; /* probably a library file */

	for(n = egb_ctx->signals->first_child; n != NULL; n = next) {
		next = n->next;
		if (n->first_child != NULL && n->first_child->id == PCB_EGKW_SECT_CONTACTREF) {
			for (cr = n->first_child; cr != NULL; cr = next2) {
				next2 = cr->next;
				for (e = htss_first(&cr->props); e; e = htss_next(&cr->props, e)) {
					if (strcmp(e->key, "partnumber") == 0) {
						int element_num = atoi(e->value);
						egb_node_prop_set(cr, "element", elem_refdes_by_idx(egb_ctx->elements, (long) element_num));
						cr_pin_name_by_elem_idx(cr, egb_ctx, (long) element_num);
					}
				}
			}
		}
	}
	return 0;
}


/* look for elements, and subsequent element2 blocks, and copy name/value fields to element */
static int postproc_elements(void *ctx, egb_ctx_t *egb_ctx)
{
	htss_entry_t *e;
	egb_node_t *el2, *n, *q, *next, *next2;

	if (egb_ctx->elements == NULL) 
		return 0; /* probably a library file */

	for(n = egb_ctx->firstel; n != NULL; n = next) {
		next = n->next;
		if (n->first_child && n->first_child->id == PCB_EGKW_SECT_ELEMENT2) {
			el2 = n;
			for(q = el2->first_child; q != NULL; q = next2) {
				next2 = q->next;
				for (e = htss_first(&q->props); e; e = htss_next(&q->props, e)) {
					if (strcmp(e->key, "name") == 0) {
						if (e->value != NULL && e->value[0] == '-' && e->value[1] == '\0') {
							egb_node_prop_set(n, "name", "HYPHEN");
							rnd_message(RND_MSG_WARNING, "Substituted invalid name %s in PCB_EKGW_SECT_ELEMENT with 'HYPHEN'\n", e->value);
						} else {
							egb_node_prop_set(n, "name", e->value);
						}
					}
					else if (strcmp(e->key, "value") == 0) {
						egb_node_prop_set(n, "value", e->value);
					}
				}
			}
		}
		/* we now add element x,y fields to refdes/value element2 node */
TODO("What this code is supposed to do? element2 doesn't have x;y, when this is enabled it overwrites perfectly good subc x;y with 0");
#if 0
		for (e = htss_first(&n->props); e; e = htss_next(&n->props, e)) {
			if (strcmp(e->key, "x") == 0) {
				egb_node_prop_set(el2, "x", e->value);
			}
			else if (strcmp(e->key, "y") == 0) {
				egb_node_prop_set(el2, "y", e->value);
			}
		}
#endif
		/* could potentially add default size, rot to text somewhere around here
		   or look harder for other optional nodes defining these parameters here */
	}
	return 0;
}


static int postproc_signal_nested(void *ctx, egb_ctx_t *egb_ctx, egb_node_t *parent, egb_node_t *top)
{
	egb_node_t *p, *prev2, *next2;

	for(p = parent->first_child, prev2 = NULL; p != NULL; p = next2) {
		next2 = p->next;
		if (p->id == PCB_EGKW_SECT_SIGNAL) {
			egb_node_unlink(parent, prev2, p);
			egb_node_append(top, p);
		}
		else
			prev2 = p;
	}
}

TODO("TODO netlist labels - eagle bin often has invalid net labels, i.e.'-', '+' so may need to filter#")
/* take any sub level signal /signals/signal1/signal2 and move it up a level
   to /signals/signal2, recursively. This flattens the signals so the XML
   parser finds everything, but connectivity info for nested nets is not
   preserved in the process (but converting the binary to xml with eagle seems
   to do the same flattening) */
static int postproc_signal(void *ctx, egb_ctx_t *egb_ctx)
{
	egb_node_t *n, *next;

	if (egb_ctx->signals == NULL)
		return  0; /* probably a library */

	/* signals/signal1/signal2 nodes are appended as singals/singal2, but
	   then this loop also visits them again, so if there was a 3rd level of /singal3,
	   that is then also removed and appended to signals/ directly */
	for(n = egb_ctx->signals->first_child; n != NULL; n = n->next)
		if (n->id == PCB_EGKW_SECT_SIGNAL)
			postproc_signal_nested(ctx, egb_ctx, n, egb_ctx->signals);

	return 0;
}

/* populate the newly created drc node in the binary tree before XML parsing, if board present */
static int postproc_drc(void *ctx, egb_ctx_t *egb_ctx)
{
	char tmp[32];
	egb_node_t *current;

	if (egb_ctx->drc == NULL)
		return 0; /* probably a library with no board node */

	current = egb_node_append(egb_ctx->drc, egb_node_alloc(PCB_EGKW_SECT_DRC, "param"));
	sprintf(tmp, "%ldmil", egb_ctx->mdWireWire);
	egb_node_prop_set(current, "name", "mdWireWire");
	egb_node_prop_set(current, "value", tmp);

	current = egb_node_append(egb_ctx->drc, egb_node_alloc(PCB_EGKW_SECT_DRC, "param"));
	sprintf(tmp, "%ldmil", egb_ctx->msWidth);
	egb_node_prop_set(current, "name", "msWidth");
	egb_node_prop_set(current, "value", tmp);

	current = egb_node_append(egb_ctx->drc, egb_node_alloc(PCB_EGKW_SECT_DRC, "param"));
	sprintf(tmp, "%f", egb_ctx->rvPadTop);
	egb_node_prop_set(current, "name", "rvPadTop");
	egb_node_prop_set(current, "value", tmp);

	current = egb_node_append(egb_ctx->drc, egb_node_alloc(PCB_EGKW_SECT_DRC, "param"));
	sprintf(tmp, "%f", egb_ctx->rvPadInner);
	egb_node_prop_set(current, "name", "rvPadInner");
	egb_node_prop_set(current, "value", tmp);

	current = egb_node_append(egb_ctx->drc, egb_node_alloc(PCB_EGKW_SECT_DRC, "param"));
	sprintf(tmp, "%f", egb_ctx->rvPadBottom);
	egb_node_prop_set(current, "name", "rvPadBottom");
	egb_node_prop_set(current, "value", tmp);

	return 0;
}

/* take each /drawing/layer and move them into a newly created /drawing/layers/ */
static int postproc_layers(void *ctx, egb_ctx_t *egb_ctx)
{
	egb_node_t *n, *prev, *next;

	for(n = egb_ctx->drawing->first_child, prev = NULL; n != NULL; n = next) {
		next = n->next; /* need to save this because unlink() will ruin it */
		if (n->id == PCB_EGKW_SECT_LAYER) {
			egb_node_unlink(egb_ctx->drawing, prev, n);
			egb_node_append(egb_ctx->layers, n);
		}
		else
			prev = n;
	}

	return 0;
}

/* insert a library node above each packages node to match the xml */
static int postproc_libs(void *ctx, egb_ctx_t *egb_ctx)
{
	egb_node_t *n, *lib;

	if (egb_ctx->libraries == NULL) /* a library .lbr has a sole library in it, unlike a board */
		return 0;

	for(n = egb_ctx->libraries->first_child; n != NULL; n = egb_ctx->libraries->first_child) {
		if (n->id == PCB_EGKW_SECT_LIBRARY)
			break;

		if (n->id != PCB_EGKW_SECT_PACKAGES) {
			rnd_message(RND_MSG_ERROR, "postproc_libs(): unexpected node under libraries (must be packages)\n");
			return -1;
		}

		egb_node_unlink(egb_ctx->libraries, NULL, n);
		lib = egb_node_append(egb_ctx->libraries, egb_node_alloc(PCB_EGKW_SECT_LIBRARY, "library"));
		egb_node_append(lib, n);
	}

	return 0;
}

static int postproc(void *ctx, egb_node_t *root, egb_ctx_t *drc_ctx)
{

	egb_node_t *n, *signal;
	egb_ctx_t eagle_bin_ctx;
	egb_ctx_t *egb_ctx_p;
	egb_ctx_p = &eagle_bin_ctx;

	/* this preliminary code does not assume a board node is present, i.e. could be library .lbr */
	eagle_bin_ctx.root = root;
	eagle_bin_ctx.drawing = root->first_child;
	eagle_bin_ctx.layers = egb_node_append(root, egb_node_alloc(PCB_EGKW_SECT_LAYERS, "layers"));
	eagle_bin_ctx.drc = NULL;      /* this will be looked for if board node present */
	eagle_bin_ctx.signals = NULL;  /* this will be looked for if board node present */
	eagle_bin_ctx.elements = NULL; /* this will be looked for if board node present */
	eagle_bin_ctx.libraries = NULL;/* this will be looked for if board node present */

	/* populate context with default DRC settings, since DRC is not present in binary v3 */
	eagle_bin_ctx.mdWireWire = drc_ctx->mdWireWire;
	eagle_bin_ctx.msWidth = drc_ctx->msWidth;
	eagle_bin_ctx.rvPadTop = drc_ctx->rvPadTop;
	eagle_bin_ctx.rvPadInner = drc_ctx->rvPadInner;
	eagle_bin_ctx.rvPadBottom = drc_ctx->rvPadBottom;

	eagle_bin_ctx.board = find_node(eagle_bin_ctx.drawing->first_child, PCB_EGKW_SECT_BOARD);
	if (eagle_bin_ctx.board == NULL) {
TODO(": convert this to proper error reporting")
		rnd_trace("No board node found, this may be a library file.\n");
	} else {
		/* the following code relies on the board node being present, i.e. a layout */
		/* create a drc node, since DRC block if present in binary file comes after the tree */
		eagle_bin_ctx.drc = egb_node_append(eagle_bin_ctx.board, egb_node_alloc(PCB_EGKW_SECT_DRC, "designrules"));
		eagle_bin_ctx.libraries = find_node_name(eagle_bin_ctx.board->first_child, "libraries");
		if (eagle_bin_ctx.libraries == NULL) { /* layouts have a libraries node it seems */
TODO(": convert this to proper error reporting")
			rnd_trace("Eagle binary layout is missing a board/libraries node.\n");
			return -1;
		}

		for(n = eagle_bin_ctx.board->first_child, signal = NULL; signal == NULL && n != NULL; n = n->next) {
			if (n->first_child && n->first_child->id == PCB_EGKW_SECT_SIGNAL) {
				signal = n->first_child;
				eagle_bin_ctx.signals = signal->parent;
			}
		}

		for(n = eagle_bin_ctx.board->first_child, eagle_bin_ctx.firstel = NULL;
				eagle_bin_ctx.firstel == NULL && n != NULL; n = n->next) {
			if (n->first_child && n->first_child->id == PCB_EGKW_SECT_ELEMENT) {
				eagle_bin_ctx.firstel = n->first_child;
				eagle_bin_ctx.elements = eagle_bin_ctx.firstel->parent;
			}
		}
	}

	/* after post processing layers, we populate the DRC node first, if present... */

	return postproc_layers(ctx, egb_ctx_p) || postproc_drc(ctx, egb_ctx_p)
		|| postproc_libs(ctx, egb_ctx_p) || postproc_elements(ctx, egb_ctx_p)
		|| postproc_signal(ctx, egb_ctx_p) || postproc_contactrefs(ctx, egb_ctx_p)
		|| postprocess_wires(ctx, root) || postprocess_arcs(ctx, root)
		|| postprocess_circles(ctx, root) || postprocess_smd(ctx, root)
		|| postprocess_dimensions(ctx, root)
		|| postprocess_free_text(drc_ctx, root)
		|| postprocess_rotation(ctx, root, PCB_EGKW_SECT_SMD)
		|| postprocess_rotation(ctx, root, PCB_EGKW_SECT_PIN)
		|| postprocess_rotation(ctx, root, PCB_EGKW_SECT_RECTANGLE)
		|| postprocess_rotation(ctx, root, PCB_EGKW_SECT_PAD)
		|| postprocess_rotation(ctx, root, PCB_EGKW_SECT_TEXT)
		|| postprocess_rotation(ctx, root, PCB_EGKW_SECT_SMASHEDVALUE)
		|| postprocess_rotation(ctx, root, PCB_EGKW_SECT_SMASHEDNAME)
		|| postprocess_rotation(ctx, root, PCB_EGKW_SECT_NETBUSLABEL)
		|| postprocess_rotation(ctx, root, PCB_EGKW_SECT_SMASHEDXREF)
		|| postprocess_rotation(ctx, root, PCB_EGKW_SECT_ATTRIBUTE)
		|| postprocess_rotation(ctx, root, PCB_EGKW_SECT_SMASHEDGATE)
		|| postprocess_rotation(ctx, root, PCB_EGKW_SECT_SMASHEDPART)
		|| postprocess_rotation(ctx, root, PCB_EGKW_SECT_INSTANCE)
		|| postprocess_rotation(ctx, root, PCB_EGKW_SECT_ELEMENT);
}

static void egb_free_ctx(egb_ctx_t *egb_ctx)
{
	free(egb_ctx->free_text);
	egb_ctx->free_text = egb_ctx->free_text_cursor = NULL;
}

int pcb_egle_bin_load(void *ctx, FILE *f, const char *fn, egb_node_t **root)
{
	long test = -1;
	long *numblocks = &test;
	int res = 0;

	egb_ctx_t eagle_bin_ctx;

/*	rnd_trace("blocks remaining prior to function call = %ld\n", *numblocks);*/

	*root = egb_node_alloc(0, "eagle");

	res = read_block(numblocks, 1, ctx, f, fn, *root);
	if (res < 0) {
TODO(": convert this to proper error reporting")
		rnd_trace("Problem with remaining blocks... is this a library file?\n");
		return res;
	}
/*	rnd_trace("blocks remaining after outer function call = %ld (after reading %d blocks)\n\n", *numblocks, res);*/

	/* could test if < v4 as v3.xx seems to have no DRC or Netclass or Free Text end blocks */
	read_notes(ctx, f, fn, &eagle_bin_ctx);
	/* read_drc will determine sane defaults if no DRC block found */
	if (read_drc(ctx, f, fn, &eagle_bin_ctx) != 0) {
TODO(": convert this to proper error reporting")
		rnd_trace("No DRC section found, either a v3 binary file or a binary library file.\n");
	} /* we now use the eagle_bin_ctx results for post_proc */

	res = postproc(ctx, *root, &eagle_bin_ctx);
	egb_free_ctx(&eagle_bin_ctx);
	return res;
}

