public bool Load(string path)
        {
            var sb = new StringBuilder(1024);

            using (var r = new StreamReader(path, Encoding.GetEncoding("iso-8859-1")))
            {
                int lineIndex = 1;
                var state     = State.Idle;
                while (!r.EndOfStream)
                {
                    try
                    {
                        var str = r.ReadLine();
                        Decode(str, sb);
                        Update(ref state, sb);
                        lineIndex++;
                    }
                    catch (Exception e)
                    {
                        var fname = Path.GetFileName(path);
                        BoardUtil.OnUnknownFormat(fname, lineIndex);
                        return(false);
                    }
                }
            }
            // process nail parts
            foreach (var part in nailParts)
            {
                var nail = new Nail();
                nail.Location = Pins[part.FirstPin].Location;
                nail.Net      = Pins[part.FirstPin].Net;
                nail.Side     = part.Side;
                nail.Name     = Pins[part.FirstPin].Name;
                nail.Id       = -99;
                Nails.Add(nail);
            }
            nailParts.Clear();
            // assign pin sides
            foreach (var part in Parts)
            {
                for (int i = part.FirstPin; i < part.FirstPin + part.PinCount; i++)
                {
                    Pins[i].Side = part.Side;
                }
            }
            // generate bottom contour
            for (int i = 0; i < TopContour.Count; i++)
            {
                var v = TopContour[i];
                v.Y *= -1;
                BottomContour.Add(v);
            }
            return(true);
        }
        public bool Load(string path)
        {
            var sb = new StringBuilder(1024);

            using (var r = new StreamReader(path, Encoding.GetEncoding("iso-8859-1")))
            {
                var state     = State.Idle;
                var fState    = FormatState.Idle;
                var nState    = NailsState.Idle;
                var pState    = PinsState.Idle;
                int lineIndex = 1;
                while (!r.EndOfStream)
                {
                    try
                    {
                        var str = r.ReadLine();
                        Decode(str, sb, lineIndex++);
                        Update(ref state, ref fState, ref nState, ref pState, sb);
                    }
                    catch (Exception e)
                    {
                        var fname = Path.GetFileName(path);
                        BoardUtil.OnUnknownFormat(fname, lineIndex);
                        return(false);
                    }
                }
            }
            // assign pin sides
            foreach (var part in Parts)
            {
                for (int i = part.FirstPin; i < part.FirstPin + part.PinCount; i++)
                {
                    Pins[i].Side = part.Side;
                }
            }
            // generate bottom contour
            for (int i = 0; i < TopContour.Count; i++)
            {
                var v = TopContour[i];
                v.Y *= -1;
                BottomContour.Add(v);
            }
            return(true);
        }
        public bool Load(string path)
        {
            using (var r = new StreamReader(path, Encoding.GetEncoding("iso-8859-1")))
            {
                int lineIndex = 1;
                var state     = State.Idle;
                while (!r.EndOfStream)
                {
                    try
                    {
                        var str = r.ReadLine();
                        Update(ref state, str);
                        lineIndex++;
                    }
                    catch (Exception e)
                    {
                        var fname = Path.GetFileName(path);
                        BoardUtil.OnUnknownFormat(fname, lineIndex);
                        return(false);
                    }
                }
            }
            // fix pin count for last part
            if (Parts.Count > 0)
            {
                var lastPart = Parts[Parts.Count - 1];
                lastPart.PinCount = Pins.Count - lastPart.FirstPin;
            }
            // calculate contour bbox
            var cbox = Box2.Empty;

            foreach (var cv in TopContour)
            {
                cbox.Merge(cv);
            }
            // shift up all non-bottom stuff
            foreach (var pin in Pins)
            {
                var v = pin.Location;
                if (v.Y < cbox.Min.Y && pin.Side != BoardSide.Bottom)
                {
                    v.Y         += cbox.Height;
                    pin.Location = v;
                }
            }
            foreach (var nail in Nails)
            {
                var v = nail.Location;
                if (v.Y < cbox.Min.Y && nail.Side != BoardSide.Bottom)
                {
                    v.Y          += cbox.Height;
                    nail.Location = v;
                }
            }
            // flip bottom stuff
            foreach (var pin in Pins)
            {
                var v = pin.Location;
                if (pin.Side == BoardSide.Bottom)
                {
                    v.Y         *= -1;
                    pin.Location = v;
                }
            }
            foreach (var nail in Nails)
            {
                var v = nail.Location;
                if (nail.Side == BoardSide.Bottom)
                {
                    v.Y          *= -1;
                    nail.Location = v;
                }
            }
            // generate inverses for through-hole pins
            foreach (var pin in Pins)
            {
                if (pin.Side == BoardSide.Both)
                {
                    var inv = new Pin();
                    var v   = pin.Location;
                    v.Y         *= -1;
                    inv.Location = v;
                    inv.Net      = pin.Net;
                    inv.Name     = pin.Name;
                    inv.Side     = BoardSide.Bottom;
                    inv.Inverse  = pin;
                    pin.Side     = BoardSide.Top;
                    pin.Inverse  = inv;
                }
            }
            // rearrange mating pins
            foreach (var part in Parts)
            {
                for (int i = part.FirstPin; i < part.FirstPin + part.PinCount; i++)
                {
                    var pin = Pins[i];
                    if (part.Side != pin.Side && pin.Inverse != null)
                    {
                        Pins[i] = pin.Inverse; // swap Pins[i] and pin.Inverse
                    }
                }
            }
            // generate bottom contour
            for (int i = 0; i < TopContour.Count; i++)
            {
                var v = TopContour[i];
                v.Y *= -1;
                BottomContour.Add(v);
            }
            return(true);
        }