예제 #1
0
        public static void ReorderSubrecords(Record rec)
        {
            var records = rec.GetStructures();

            if (rec == null || records == null)
            {
                return;
            }

            RecordStructure recStruct;

            if (!records.TryGetValue(rec.Name, out recStruct))
            {
                return;
            }

            SubrecordStructure[] sss = recStruct.subrecords;
            var subs = new List <SubRecord>(rec.SubRecords);

            foreach (var sub in subs)
            {
                sub.DetachStructure();
            }

            var newsubs = new List <SubRecord>();

            for (int ssidx = 0, sslen = 0; ssidx < sss.Length; ssidx += sslen)
            {
                SubrecordStructure ss = sss[ssidx];
                bool repeat           = ss.repeat > 0;
                sslen = Math.Max(1, ss.repeat);

                bool found = false;
                do
                {
                    found = false;
                    for (int ssoff = 0; ssoff < sslen; ++ssoff)
                    {
                        ss = sss[ssidx + ssoff];
                        for (int i = 0; i < subs.Count; ++i)
                        {
                            var sr = subs[i];
                            if (sr.Name == ss.name)
                            {
                                newsubs.Add(sr);
                                subs.RemoveAt(i);
                                found = true;
                                break;
                            }
                        }
                    }
                } while (found && repeat);
            }

            newsubs.AddRange(subs);
            rec.SubRecords.Clear();
            rec.SubRecords.AddRange(newsubs);
        }
예제 #2
0
 /// <summary>
 /// Clear any state
 /// </summary>
 public void ClearControl()
 {
     r = null;
     sr = null;
     ss = null;
     controlMap.Clear();
     fpanel1.Controls.Clear();
     Enabled = false;
 }
예제 #3
0
 /// <summary>
 /// Clear any state.
 /// </summary>
 public void ClearControl()
 {
     this.r  = null;
     this.sr = null;
     this.ss = null;
     this.controlMap.Clear();
     this.fpanel1.Controls.Clear();
     Enabled = false;
 }
예제 #4
0
 /// <summary>
 /// Clear any state.
 /// </summary>
 public void ClearControl()
 {
     this.r = null;
     this.sr = null;
     this.ss = null;
     this.controlMap.Clear();
     this.fpanel1.Controls.Clear();
     Enabled = false;
 }
예제 #5
0
 /// <summary>
 /// Clear any state
 /// </summary>
 public void ClearControl()
 {
     r  = null;
     sr = null;
     ss = null;
     controlMap.Clear();
     fpanel1.Controls.Clear();
     Enabled = false;
 }
        public MediumLevelRecordEditor(SubRecord sr, SubrecordStructure ss, dFormIDLookupS formIDLookup, dFormIDScan formIDScan, dLStringLookup strIDLookup)
        {
            this.InitializeComponent();
            Icon = Resources.tesv_ico;
            SuspendLayout();
            this.sr           = sr;
            this.ss           = ss;
            this.formIDLookup = formIDLookup;
            this.formIDScan   = formIDScan;
            this.strIDLookup  = strIDLookup;

            int offset = 0;

            byte[] data = sr.GetReadonlyData();
            this.boxes      = new List <TextBox>(ss.elements.Length);
            this.valueTypes = new List <ElementValueType>(ss.elements.Length);
            this.elements   = new List <Panel>();
            int groupOffset  = 0;
            int CurrentGroup = 0;

            try
            {
                for (int i = 0; i < ss.elements.Length; i++)
                {
                    if (ss.elements[i].optional && offset == data.Length)
                    {
                        this.AddElement(ss.elements[i]);
                    }
                    else
                    {
                        this.AddElement(ss.elements[i], ref offset, data, ref groupOffset, ref CurrentGroup);
                        if (ss.elements[i].repeat > 0)
                        {
                            this.repeatcount++;
                            if (offset < data.Length)
                            {
                                i--;
                            }
                        }
                    }
                }

                if (ss.elements[ss.elements.Length - 1].repeat > 0 && this.repeatcount > 0)
                {
                    this.AddElement(ss.elements[ss.elements.Length - 1]);
                }
            }
            catch
            {
                MessageBox.Show("The subrecord doesn't appear to conform to the expected structure.\n" + "Saving is disabled, and the formatted information may be incorrect", "Warning");
                this.bSave.Enabled = false;
            }

            ResumeLayout();
        }
예제 #7
0
        public MediumLevelRecordEditor(SubRecord sr, SubrecordStructure ss, dFormIDLookupS formIDLookup, dFormIDScan formIDScan, dLStringLookup strIDLookup)
        {
            this.InitializeComponent();
            Icon = Resources.tesv_ico;
            SuspendLayout();
            this.sr = sr;
            this.ss = ss;
            this.formIDLookup = formIDLookup;
            this.formIDScan = formIDScan;
            this.strIDLookup = strIDLookup;

            int offset = 0;
            byte[] data = sr.GetReadonlyData();
            this.boxes = new List<TextBox>(ss.elements.Length);
            this.valueTypes = new List<ElementValueType>(ss.elements.Length);
            this.elements = new List<Panel>();
            int groupOffset = 0;
            int CurrentGroup = 0;
            try
            {
                for (int i = 0; i < ss.elements.Length; i++)
                {
                    if (ss.elements[i].optional > 0 && offset == data.Length)
                    {
                        this.AddElement(ss.elements[i]);
                    }
                    else
                    {
                        this.AddElement(ss.elements[i], ref offset, data, ref groupOffset, ref CurrentGroup);
                        if (ss.elements[i].repeat > 0)
                        {
                            this.repeatcount++;
                            if (offset < data.Length)
                            {
                                i--;
                            }
                        }
                    }
                }

                if (ss.elements[ss.elements.Length - 1].repeat > 0 && this.repeatcount > 0)
                {
                    this.AddElement(ss.elements[ss.elements.Length - 1]);
                }
            }
            catch
            {
                MessageBox.Show("The subrecord doesn't appear to conform to the expected structure.\n" + "Saving is disabled, and the formatted information may be incorrect", "Warning");
                this.bSave.Enabled = false;
            }

            ResumeLayout();
        }
예제 #8
0
        /// <summary>
        ///     Initializes a new instance of the <see cref="SubRecord" /> class.
        ///     Create new subrecord using Structure as template
        /// </summary>
        /// <param name="srs">
        /// </param>
        public SubRecord(SubrecordStructure srs)
            : this()
        {
            if (srs != null)
            {
                Name = srs.name;
                int size = 0;
                if (srs.size > 0)
                {
                    size = srs.size;
                }
                else
                {
                    foreach (var elem in srs.elements)
                    {
                        if (elem.optional == 0 || elem.repeat == 0)
                        {
                            switch (elem.type)
                            {
                            case ElementValueType.FormID:
                            case ElementValueType.LString:
                            case ElementValueType.Int:
                            case ElementValueType.UInt:
                            case ElementValueType.Float:
                            case ElementValueType.Str4:
                            case ElementValueType.IString:
                                size += 4;
                                break;

                            case ElementValueType.BString:
                            case ElementValueType.Short:
                            case ElementValueType.UShort:
                                size += 2;
                                break;

                            case ElementValueType.String:
                            case ElementValueType.Byte:
                            case ElementValueType.SByte:
                                size += 1;
                                break;
                            }
                        }
                    }
                }

                this.Data = new byte[size];

                // TODO: populate with defaults if provided...
            }
        }
예제 #9
0
        public NewMediumLevelRecordEditor(Plugin p, Record r, SubRecord sr, SubrecordStructure ss)
        {
            this.InitializeComponent();
            Icon = Resources.tesv_ico;
            SuspendLayout();
            this.sr = sr;
            this.ss = ss;
            this.p  = p;
            this.r  = r;

            // walk each element in standard fashion
            int panelOffset = 0;

            try
            {
                foreach (var elem in ss.elements)
                {
                    Control c = null;
                    if (elem.options != null && elem.options.Length > 1)
                    {
                        c = new OptionsElement();
                    }
                    else if (elem.flags != null && elem.flags.Length > 1)
                    {
                        c = new FlagsElement();
                    }
                    else
                    {
                        switch (elem.type)
                        {
                        case ElementValueType.LString:
                            c = new LStringElement();
                            break;

                        case ElementValueType.FormID:
                            c = new FormIDElement();
                            break;

                        case ElementValueType.Blob:
                            c = new HexElement();
                            break;

                        default:
                            c = new TextElement();
                            break;
                        }
                    }

                    if (c is IElementControl)
                    {
                        var ec = c as IElementControl;
                        ec.formIDLookup = p.GetRecordByID;
                        ec.formIDScan   = p.EnumerateRecords;
                        ec.strIDLookup  = p.LookupFormStrings;
                        ec.Element      = elem;

                        if (elem.repeat > 0)
                        {
                            var ge = new RepeatingElement();
                            c        = ge;
                            c.Left   = 8;
                            c.Width  = this.fpanel1.Width - 16;
                            c.Top    = panelOffset;
                            c.Anchor = c.Anchor | AnchorStyles.Left | AnchorStyles.Right;

                            ge.InnerControl = ec;
                            ge.Element      = elem;
                            ec = ge;
                        }
                        else if (elem.optional)
                        {
                            var re = new OptionalElement();
                            c        = re;
                            c.Left   = 8;
                            c.Width  = this.fpanel1.Width - 16;
                            c.Top    = panelOffset;
                            c.Anchor = c.Anchor | AnchorStyles.Left | AnchorStyles.Right;

                            re.InnerControl = ec;
                            re.Element      = elem;
                            ec = re;
                            c  = re;
                        }
                        else
                        {
                            c.Left   = 8;
                            c.Width  = this.fpanel1.Width - 16;
                            c.Top    = panelOffset;
                            c.Anchor = c.Anchor | AnchorStyles.Left | AnchorStyles.Right;
                        }

                        c.MinimumSize = c.Size;

                        this.controlMap.Add(elem, ec);
                        this.fpanel1.Controls.Add(c);
                        panelOffset = c.Bottom;
                    }
                }

                foreach (Element elem in r.EnumerateElements(sr, true))
                {
                    var es = elem.Structure;

                    IElementControl c;
                    if (this.controlMap.TryGetValue(es, out c))
                    {
                        if (c is IGroupedElementControl)
                        {
                            var gc = c as IGroupedElementControl;
                            gc.Elements.Add(elem.Data);
                        }
                        else
                        {
                            c.Data = elem.Data;
                        }
                    }
                }
            }
            catch
            {
                this.strWarnOnSave = "The subrecord doesn't appear to conform to the expected structure.\nThe formatted information may be incorrect.";
                this.Error.SetError(this.bSave, this.strWarnOnSave);
                this.Error.SetIconAlignment(this.bSave, ErrorIconAlignment.MiddleLeft);
                AcceptButton = this.bCancel; // remove save as default button when exception occurs
                CancelButton = this.bCancel;
                UpdateDefaultButton();
            }

            ResumeLayout();
        }
예제 #10
0
        public void ConfigureRecords(IEnumerable<Record> records)
        {
            var recs = records.Select(rec => rec.GetStructure()).Distinct();
            this.rec = (recs.Count() == 1) ? recs.FirstOrDefault() : null;
            if (this.rec == null)
            {
                this.filterTree.Roots = null;
            }
            else
            {
                //this.records = records;

                var srs = (from sr in this.rec.subrecords
                           let children = sr.elements.Select(se => new BatchElement() { Name = se.name, Parent = null, Record = se, Type = BatchCondElementType.Set, Checked = false }).ToList()
                           select new BatchSubrecord() { Name = string.Format("{0}: {1}", sr.name, sr.desc), Record = sr, Children = children, Checked = false, }).ToList();

                // Construct specialized editors for FormID and related headers
            #if false
                {
                    var elems = new List<TESVSnip.Data.SubrecordElement>
                    {
                        new TESVSnip.Data.SubrecordElement{name = "FormID", desc = "Form ID", hexview = true, type = "uint"},
                        new TESVSnip.Data.SubrecordElement{name = "Flags1", desc = "Flags 1", hexview = true, type = "uint"},
                        new TESVSnip.Data.SubrecordElement{name = "Flags2", desc = "Flags 2", hexview = true, type = "uint"},
                        new TESVSnip.Data.SubrecordElement{name = "Flags3", desc = "Flags 3", hexview = true, type = "uint"},
                    };
                    var frmHdr = new TESVSnip.Data.Subrecord { name = "Header", desc = "Record Header", Elements = elems };
                    var hdr = new SubrecordStructure(frmHdr);
                    var hdrElems = hdr.elements.Select(se =>
                        new BatchElement { Name = se.name, Parent = null, Record = se, Type = BatchCondElementType.Set, Checked = false }
                        ).ToList();
                    srs.Insert(0, new BatchSubrecord
                    {
                        Name = string.Format("{0}: {1}", hdr.name, hdr.desc),
                        Record = hdr,
                        Children = hdrElems,
                        Checked = false,
                    });
                }
            #endif

                // fix parents after assignments
                foreach (var sr in srs)
                {
                    foreach (var se in sr.Children)
                    {
                        se.Parent = sr;
                    }
                }

                this.filterTree.Roots = srs;
            }
        }
예제 #11
0
        public void ConfigureRecords(IEnumerable <Record> records)
        {
            var recs = records.Select(
                x => {
                RecordStructure rs;
                return(RecordStructure.Records.TryGetValue(x.Name, out rs) ? rs : null);
            }).Distinct();

            this.rec = (recs.Count() == 1) ? recs.FirstOrDefault() : null;
            if (this.rec == null)
            {
                this.filterTree.Roots = null;
            }
            else
            {
                this.records = records;

                var srs = (from sr in this.rec.subrecords
                           let children = sr.elements.Select(se => new BatchElement()
                {
                    Name = se.name, Parent = null, Record = se, Type = BatchCondElementType.Set, Checked = false
                }).ToList()
                                          select new BatchSubrecord()
                {
                    Name = string.Format("{0}: {1}", sr.name, sr.desc), Record = sr, Children = children, Checked = false,
                }).ToList();

                // Construct specialized editors for FormID and related headers
#if false
                {
                    var elems = new List <TESVSnip.Data.SubrecordElement>
                    {
                        new TESVSnip.Data.SubrecordElement {
                            name = "FormID", desc = "Form ID", hexview = true, type = "uint"
                        },
                        new TESVSnip.Data.SubrecordElement {
                            name = "Flags1", desc = "Flags 1", hexview = true, type = "uint"
                        },
                        new TESVSnip.Data.SubrecordElement {
                            name = "Flags2", desc = "Flags 2", hexview = true, type = "uint"
                        },
                        new TESVSnip.Data.SubrecordElement {
                            name = "Flags3", desc = "Flags 3", hexview = true, type = "uint"
                        },
                    };
                    var frmHdr = new TESVSnip.Data.Subrecord {
                        name = "Header", desc = "Record Header", Elements = elems
                    };
                    var hdr      = new SubrecordStructure(frmHdr);
                    var hdrElems = hdr.elements.Select(se =>
                                                       new BatchElement {
                        Name = se.name, Parent = null, Record = se, Type = BatchCondElementType.Set, Checked = false
                    }
                                                       ).ToList();
                    srs.Insert(0, new BatchSubrecord
                    {
                        Name     = string.Format("{0}: {1}", hdr.name, hdr.desc),
                        Record   = hdr,
                        Children = hdrElems,
                        Checked  = false,
                    });
                }
#endif

                // fix parents after assignments
                foreach (var sr in srs)
                {
                    foreach (var se in sr.Children)
                    {
                        se.Parent = sr;
                    }
                }

                this.filterTree.Roots = srs;
            }
        }
예제 #12
0
 public void DetachStructure()
 {
     this.Structure = null;
 }
        public NewMediumLevelRecordEditor(Plugin p, Record r, SubRecord sr, SubrecordStructure ss)
        {
            this.InitializeComponent();
            Icon = Resources.fosnip;
            SuspendLayout();
            this.sr = sr;
            this.ss = ss;
            this.p = p;
            this.r = r;

            // walk each element in standard fashion
            int panelOffset = 0;
            try
            {
                this.fpanel1.ColumnStyles[0] = new ColumnStyle(SizeType.Percent, 100.0f);
                int maxWidth = this.fpanel1.Width - SystemInformation.VerticalScrollBarWidth - 8;
                int leftOffset = 0; // 8;
                foreach (var elem in ss.elements)
                {
                    Control c = null;
                    if (elem.options != null && elem.options.Length > 1)
                    {
                        c = new OptionsElement();
                    }
                    else if (elem.flags != null && elem.flags.Length > 1)
                    {
                        c = new FlagsElement();
                    }
                    else
                    {
                        switch (elem.type)
                        {
                            case ElementValueType.LString:
                                c = new LStringElement();
                                break;
                            case ElementValueType.FormID:
                                c = new FormIDElement();
                                break;
                            case ElementValueType.Blob:
                                c = new HexElement();
                                break;
                            default:
                                c = new TextElement();
                                break;
                        }
                    }

                    if (c is IElementControl)
                    {
                        var ec = c as IElementControl;
                        ec.formIDLookup = p.GetRecordByID;
                        ec.formIDScan = p.EnumerateRecords;
                        ec.strIDLookup = p.LookupFormStrings;
                        ec.Element = elem;

                        if (elem.repeat > 0)
                        {
                            var ge = new RepeatingElement();
                            c = ge;
                            c.Left = leftOffset;
                            c.Width = maxWidth;
                            c.Top = panelOffset;
                            c.Anchor = c.Anchor | AnchorStyles.Left | AnchorStyles.Right;

                            ge.InnerControl = ec;
                            ge.Element = elem;
                            ec = ge;
                        }
                        else if (elem.optional > 0)
                        {
                            var re = new OptionalElement();
                            c = re;
                            c.Left = leftOffset;
                            c.Width = maxWidth;
                            c.Top = panelOffset;
                            c.Anchor = c.Anchor | AnchorStyles.Left | AnchorStyles.Right;

                            re.InnerControl = ec;
                            re.Element = elem;
                            ec = re;
                            c = re;
                        }
                        else
                        {
                            c.Left = leftOffset;
                            c.Width = maxWidth;
                            c.Top = panelOffset;
                            c.Anchor = c.Anchor | AnchorStyles.Left | AnchorStyles.Right;
                        }

                        this.controlMap.Add(elem, ec);
                        int idx = this.fpanel1.RowCount - 1;
                        this.fpanel1.Controls.Add(c, 0, idx);
                        var info = new RowStyle(SizeType.Absolute, c.Size.Height+2);
                        if (idx == 0)
                            this.fpanel1.RowStyles[0] = info;
                        else
                            this.fpanel1.RowStyles.Add(info);
                        panelOffset = 0;
                        ++this.fpanel1.RowCount;
                    }
                }

                foreach (Element elem in r.EnumerateElements(sr, true))
                {
                    var es = elem.Structure;

                    IElementControl c;
                    if (this.controlMap.TryGetValue(es, out c))
                    {
                        if (c is IGroupedElementControl)
                        {
                            var gc = c as IGroupedElementControl;
                            gc.Elements.Add(elem.Data);
                        }
                        else
                        {
                            c.Data = elem.Data;
                        }
                    }
                }
            }
            catch
            {
                this.strWarnOnSave = "The subrecord doesn't appear to conform to the expected structure.\nThe formatted information may be incorrect.";
                this.Error.SetError(this.bSave, this.strWarnOnSave);
                this.Error.SetIconAlignment(this.bSave, ErrorIconAlignment.MiddleLeft);
                AcceptButton = this.bCancel; // remove save as default button when exception occurs
                CancelButton = this.bCancel;
                UpdateDefaultButton();
            }

            ResumeLayout();
        }
예제 #14
0
 internal void AttachStructure(SubrecordStructure ss)
 {
     this.Structure = ss;
 }
예제 #15
0
        public static void GetFormattedData(this SubRecord rec, StringBuilder s)
        {
            SubrecordStructure ss = rec.Structure;

            if (ss == null)
            {
                return;
            }

            var            p             = rec.GetPlugin();
            dFormIDLookupI formIDLookup  = p.LookupFormID;
            dLStringLookup strLookup     = p.LookupFormStrings;
            dFormIDLookupR formIDLookupR = p.GetRecordByID;

            var recdata = rec.GetReadonlyData();
            int offset  = 0;

            s.AppendFormat("{0} ({1})", ss.name, ss.desc);
            s.AppendLine();
            try
            {
                for (int eidx = 0, elen = 1; eidx < ss.elements.Length; eidx += elen)
                {
                    var  sselem = ss.elements[eidx];
                    bool repeat = sselem.repeat > 0;
                    elen = sselem.repeat > 1 ? sselem.repeat : 1;
                    do
                    {
                        for (int eoff = 0; eoff < elen && offset < recdata.Length; ++eoff)
                        {
                            sselem = ss.elements[eidx + eoff];

                            if (offset == recdata.Length && eidx == ss.elements.Length - 1 && sselem.optional > 0)
                            {
                                break;
                            }

                            if (!sselem.notininfo)
                            {
                                s.Append(sselem.name).Append(": ");
                            }

                            switch (sselem.type)
                            {
                            case ElementValueType.Int:
                            {
                                string tmps = TypeConverter.h2si(recdata[offset], recdata[offset + 1], recdata[offset + 2], recdata[offset + 3]).ToString();
                                if (!sselem.notininfo)
                                {
                                    if (sselem.hexview)
                                    {
                                        s.Append(TypeConverter.h2i(recdata[offset], recdata[offset + 1], recdata[offset + 2], recdata[offset + 3]).ToString("X8"));
                                    }
                                    else
                                    {
                                        s.Append(tmps);
                                    }

                                    if (sselem.options != null && sselem.options.Length > 0)
                                    {
                                        for (int k = 0; k < sselem.options.Length; k += 2)
                                        {
                                            if (tmps == sselem.options[k + 1])
                                            {
                                                s.AppendFormat(" ({0})", sselem.options[k]);
                                            }
                                        }
                                    }
                                    else if (sselem.flags != null && sselem.flags.Length > 0)
                                    {
                                        uint val  = TypeConverter.h2i(recdata[offset], recdata[offset + 1], recdata[offset + 2], recdata[offset + 3]);
                                        var  tmp2 = new StringBuilder();
                                        for (int k = 0; k < sselem.flags.Length; k++)
                                        {
                                            if ((val & (1 << k)) != 0)
                                            {
                                                if (tmp2.Length > 0)
                                                {
                                                    tmp2.Append(", ");
                                                }

                                                tmp2.Append(sselem.flags[k]);
                                            }
                                        }

                                        if (tmp2.Length > 0)
                                        {
                                            s.AppendFormat(" ({0})", tmp2);
                                        }
                                    }
                                }

                                offset += 4;
                            }

                            break;

                            case ElementValueType.UInt:
                            {
                                string tmps = TypeConverter.h2i(recdata[offset], recdata[offset + 1], recdata[offset + 2], recdata[offset + 3]).ToString();
                                if (!sselem.notininfo)
                                {
                                    if (sselem.hexview)
                                    {
                                        s.Append(TypeConverter.h2i(recdata[offset], recdata[offset + 1], recdata[offset + 2], recdata[offset + 3]).ToString("X8"));
                                    }
                                    else
                                    {
                                        s.Append(tmps);
                                    }

                                    if (sselem.options != null && sselem.options.Length > 0)
                                    {
                                        for (int k = 0; k < sselem.options.Length; k += 2)
                                        {
                                            if (tmps == sselem.options[k + 1])
                                            {
                                                s.AppendFormat(" ({0})", sselem.options[k]);
                                            }
                                        }
                                    }
                                    else if (sselem.flags != null && sselem.flags.Length > 0)
                                    {
                                        uint val  = TypeConverter.h2i(recdata[offset], recdata[offset + 1], recdata[offset + 2], recdata[offset + 3]);
                                        var  tmp2 = new StringBuilder();
                                        for (int k = 0; k < sselem.flags.Length; k++)
                                        {
                                            if ((val & (1 << k)) != 0)
                                            {
                                                if (tmp2.Length > 0)
                                                {
                                                    tmp2.Append(", ");
                                                }

                                                tmp2.Append(sselem.flags[k]);
                                            }
                                        }

                                        if (tmp2.Length > 0)
                                        {
                                            s.AppendFormat(" ({0})", tmp2);
                                        }
                                    }
                                }

                                offset += 4;
                            }

                            break;

                            case ElementValueType.Short:
                            {
                                string tmps = TypeConverter.h2ss(recdata[offset], recdata[offset + 1]).ToString();
                                if (!sselem.notininfo)
                                {
                                    if (sselem.hexview)
                                    {
                                        s.Append(TypeConverter.h2ss(recdata[offset], recdata[offset + 1]).ToString("X4"));
                                    }
                                    else
                                    {
                                        s.Append(tmps);
                                    }

                                    if (sselem.options != null && sselem.options.Length > 0)
                                    {
                                        for (int k = 0; k < sselem.options.Length; k += 2)
                                        {
                                            if (tmps == sselem.options[k + 1])
                                            {
                                                s.AppendFormat(" ({0})", sselem.options[k]);
                                            }
                                        }
                                    }
                                    else if (sselem.flags != null && sselem.flags.Length > 0)
                                    {
                                        uint val  = TypeConverter.h2s(recdata[offset], recdata[offset + 1]);
                                        var  tmp2 = new StringBuilder();
                                        for (int k = 0; k < sselem.flags.Length; k++)
                                        {
                                            if ((val & (1 << k)) != 0)
                                            {
                                                if (tmp2.Length > 0)
                                                {
                                                    tmp2.Append(", ");
                                                }

                                                tmp2.Append(sselem.flags[k]);
                                            }
                                        }

                                        if (tmp2.Length > 0)
                                        {
                                            s.AppendFormat(" ({0})", tmp2);
                                        }
                                    }
                                }

                                offset += 2;
                            }

                            break;

                            case ElementValueType.UShort:
                            {
                                string tmps = TypeConverter.h2s(recdata[offset], recdata[offset + 1]).ToString();
                                if (!sselem.notininfo)
                                {
                                    if (sselem.hexview)
                                    {
                                        s.Append(TypeConverter.h2s(recdata[offset], recdata[offset + 1]).ToString("X4"));
                                    }
                                    else
                                    {
                                        s.Append(tmps);
                                    }

                                    if (sselem.options != null && sselem.options.Length > 0)
                                    {
                                        for (int k = 0; k < sselem.options.Length; k += 2)
                                        {
                                            if (tmps == sselem.options[k + 1])
                                            {
                                                s.Append(" (").Append(sselem.options[k]).Append(")");
                                            }
                                        }
                                    }
                                    else if (sselem.flags != null && sselem.flags.Length > 0)
                                    {
                                        uint val  = TypeConverter.h2s(recdata[offset], recdata[offset + 1]);
                                        var  tmp2 = new StringBuilder();
                                        for (int k = 0; k < sselem.flags.Length; k++)
                                        {
                                            if ((val & (1 << k)) != 0)
                                            {
                                                if (tmp2.Length > 0)
                                                {
                                                    tmp2.Append(", ");
                                                }

                                                tmp2.Append(sselem.flags[k]);
                                            }
                                        }

                                        if (tmp2.Length > 0)
                                        {
                                            s.AppendFormat(" ({0})", tmp2);
                                        }
                                    }
                                }

                                offset += 2;
                            }

                            break;

                            case ElementValueType.Byte:
                            {
                                string tmps = recdata[offset].ToString();
                                if (!sselem.notininfo)
                                {
                                    if (sselem.hexview)
                                    {
                                        s.Append(recdata[offset].ToString("X2"));
                                    }
                                    else
                                    {
                                        s.Append(tmps);
                                    }

                                    if (sselem.options != null && sselem.options.Length > 0)
                                    {
                                        for (int k = 0; k < sselem.options.Length; k += 2)
                                        {
                                            if (tmps == sselem.options[k + 1])
                                            {
                                                s.AppendFormat(" ({0})", sselem.options[k]);
                                            }
                                        }
                                    }
                                    else if (sselem.flags != null && sselem.flags.Length > 0)
                                    {
                                        int val  = recdata[offset];
                                        var tmp2 = new StringBuilder();
                                        for (int k = 0; k < sselem.flags.Length; k++)
                                        {
                                            if ((val & (1 << k)) != 0)
                                            {
                                                if (tmp2.Length > 0)
                                                {
                                                    tmp2.Append(", ");
                                                }

                                                tmp2.Append(sselem.flags[k]);
                                            }
                                        }

                                        if (tmp2.Length > 0)
                                        {
                                            s.AppendFormat(" ({0})", tmp2);
                                        }
                                    }
                                }

                                offset++;
                            }

                            break;

                            case ElementValueType.SByte:
                            {
                                string tmps = ((sbyte)recdata[offset]).ToString();
                                if (!sselem.notininfo)
                                {
                                    if (sselem.hexview)
                                    {
                                        s.Append(recdata[offset].ToString("X2"));
                                    }
                                    else
                                    {
                                        s.Append(tmps);
                                    }

                                    if (sselem.options != null && sselem.options.Length > 0)
                                    {
                                        for (int k = 0; k < sselem.options.Length; k += 2)
                                        {
                                            if (tmps == sselem.options[k + 1])
                                            {
                                                s.AppendFormat(" ({0})", sselem.options[k]);
                                            }
                                        }
                                    }
                                    else if (sselem.flags != null && sselem.flags.Length > 0)
                                    {
                                        int val  = recdata[offset];
                                        var tmp2 = new StringBuilder();
                                        for (int k = 0; k < sselem.flags.Length; k++)
                                        {
                                            if ((val & (1 << k)) != 0)
                                            {
                                                if (tmp2.Length > 0)
                                                {
                                                    tmp2.Append(", ");
                                                }

                                                tmp2.Append(sselem.flags[k]);
                                            }
                                        }

                                        if (tmp2.Length > 0)
                                        {
                                            s.AppendFormat(" ({0})", tmp2);
                                        }
                                    }
                                }

                                offset++;
                            }

                            break;

                            case ElementValueType.FormID:
                            {
                                uint id = TypeConverter.h2i(recdata[offset], recdata[offset + 1], recdata[offset + 2], recdata[offset + 3]);
                                if (!sselem.notininfo)
                                {
                                    s.Append(id.ToString("X8"));
                                }

                                if (id != 0 && formIDLookup != null)
                                {
                                    s.Append(": ").Append(formIDLookup(id));
                                }

                                offset += 4;
                            }

                            break;

                            case ElementValueType.Float:
                                if (!sselem.notininfo)
                                {
                                    s.Append(TypeConverter.h2f(recdata[offset], recdata[offset + 1], recdata[offset + 2], recdata[offset + 3]));
                                }

                                offset += 4;
                                break;

                            case ElementValueType.String:
                                if (!sselem.notininfo)
                                {
                                    while (recdata[offset] != 0)
                                    {
                                        s.Append((char)recdata[offset++]);
                                    }
                                }
                                else
                                {
                                    while (recdata[offset] != 0)
                                    {
                                        offset++;
                                    }
                                }

                                offset++;
                                break;

                            case ElementValueType.Blob:
                                if (!sselem.notininfo)
                                {
                                    s.Append(TypeConverter.GetHexData(recdata, offset, recdata.Length - offset));
                                }

                                offset += recdata.Length - offset;
                                break;

                            case ElementValueType.BString:
                            {
                                int len = TypeConverter.h2s(recdata[offset], recdata[offset + 1]);
                                if (!sselem.notininfo)
                                {
                                    s.Append(TESVSnip.Framework.Services.Encoding.Instance.GetString(recdata, offset + 2, len));
                                }

                                offset += 2 + len;
                            }

                            break;

                            case ElementValueType.IString:
                            {
                                int len = TypeConverter.h2si(recdata[offset], recdata[offset + 1], recdata[offset + 2], recdata[offset + 3]);
                                if (!sselem.notininfo)
                                {
                                    s.Append(TESVSnip.Framework.Services.Encoding.Instance.GetString(recdata, offset + 4, len));
                                }

                                offset += 4 + len;
                            }

                            break;

                            case ElementValueType.LString:
                            {
                                // Try to guess if string or string index.  Do not know if the external string checkbox is set or not in this code
                                int    left     = recdata.Length - offset;
                                var    data     = new ArraySegment <byte>(recdata, offset, left);
                                bool   isString = TypeConverter.IsLikelyString(data);
                                uint   id       = TypeConverter.h2i(data);
                                string lvalue   = strLookup(id);
                                if (!string.IsNullOrEmpty(lvalue) || !isString)
                                {
                                    if (!sselem.notininfo)
                                    {
                                        s.Append(id.ToString("X8"));
                                    }

                                    if (strLookup != null)
                                    {
                                        s.Append(": ").Append(lvalue);
                                    }

                                    offset += 4;
                                }
                                else
                                {
                                    if (!sselem.notininfo)
                                    {
                                        while (recdata[offset] != 0)
                                        {
                                            s.Append((char)recdata[offset++]);
                                        }
                                    }
                                    else
                                    {
                                        while (recdata[offset] != 0)
                                        {
                                            offset++;
                                        }
                                    }

                                    offset++;
                                }
                            }

                            break;

                            case ElementValueType.Str4:
                            {
                                if (!sselem.notininfo)
                                {
                                    s.Append(TESVSnip.Framework.Services.Encoding.Instance.GetString(recdata, offset, 4));
                                }

                                offset += 4;
                            }

                            break;

                            default:
                                throw new ApplicationException();
                            }

                            if (!sselem.notininfo)
                            {
                                s.AppendLine();
                            }
                        }
                    }while (repeat && offset < recdata.Length);
                }

                if (offset < recdata.Length)
                {
                    s.AppendLine();
                    s.AppendLine("Remaining Data: ");
                    s.Append(TypeConverter.GetHexData(recdata, offset, recdata.Length - offset));
                }
            }
            catch
            {
                s.AppendLine("Warning: Subrecord doesn't seem to match the expected structure");
            }
        }
예제 #16
0
        public void SetContext(Record r, SubRecord sr, bool hexView)
        {
            if (this.r == r && this.sr == sr && this.hexView == hexView)
            {
                return;
            }

            if (r == null || sr == null)
            {
                this.ClearControl();
                return;
            }

            // walk each element in standard fashion
            int panelOffset = 0;

            try
            {
                this.BeginUpdate();

                this.ClearControl();
                SuspendLayout();
                this.fpanel1.SuspendLayout();
                this.fpanel1.Width = Parent.Width;
                this.controlMap.Clear();

                this.hexView = hexView;
                this.r       = r;
                this.sr      = sr;
                var p = this.GetPluginFromNode(r);
                this.ss = sr.Structure;

                // default to blob if no elements
                if (this.ss == null || this.ss.elements == null || hexView)
                {
                    var c = new HexElement();
                    c.Left   = 8;
                    c.Width  = this.fpanel1.Width - 16;
                    c.Top    = panelOffset;
                    c.Anchor = c.Anchor | AnchorStyles.Left | AnchorStyles.Right;

                    var elem = r.EnumerateElements(sr, true).FirstOrDefault();
                    if (elem != null)
                    {
                        this.controlMap.Add(elem.Structure, c);
                        this.fpanel1.Controls.Add(c);
                        c.Data = elem.Data;
                    }
                }
                else
                {
                    foreach (var elem in this.ss.elements)
                    {
                        Control c = null;
                        if (elem.options != null && elem.options.Length > 1)
                        {
                            c = new OptionsElement();
                        }
                        else if (elem.flags != null && elem.flags.Length > 1)
                        {
                            c = new FlagsElement();
                        }
                        else
                        {
                            switch (elem.type)
                            {
                            case ElementValueType.LString:
                                c = new LStringElement();
                                break;

                            case ElementValueType.FormID:
                                c = new FormIDElement();
                                break;

                            case ElementValueType.Blob:
                                c = new HexElement();
                                break;

                            default:
                                c = new TextElement();
                                break;
                            }
                        }

                        if (c is IElementControl)
                        {
                            var ec = c as IElementControl;
                            ec.formIDLookup = p.GetRecordByID;
                            ec.formIDScan   = p.EnumerateRecords;
                            ec.strIDLookup  = p.LookupFormStrings;
                            ec.Element      = elem;

                            if (elem.repeat > 0)
                            {
                                var ge = new RepeatingElement();
                                c        = ge;
                                c.Left   = 8;
                                c.Width  = this.fpanel1.Width - 16;
                                c.Top    = panelOffset;
                                c.Anchor = c.Anchor | AnchorStyles.Left | AnchorStyles.Right;

                                ge.InnerControl = ec;
                                ge.Element      = elem;
                                ec = ge;
                            }
                            else if (elem.optional > 0)
                            {
                                var re = new OptionalElement();
                                c        = re;
                                c.Left   = 8;
                                c.Width  = this.fpanel1.Width - 16;
                                c.Top    = panelOffset;
                                c.Anchor = c.Anchor | AnchorStyles.Left | AnchorStyles.Right;

                                re.InnerControl = ec;
                                re.Element      = elem;
                                ec = re;
                                c  = re;
                            }
                            else
                            {
                                c.Left   = 8;
                                c.Width  = this.fpanel1.Width - 16;
                                c.Top    = panelOffset;
                                c.Anchor = c.Anchor | AnchorStyles.Left | AnchorStyles.Right;
                            }

                            c.MinimumSize = c.Size;

                            this.controlMap.Add(elem, ec);
                            this.fpanel1.Controls.Add(c);
                            panelOffset = c.Bottom;
                        }
                    }

                    foreach (Element elem in r.EnumerateElements(sr, true))
                    {
                        var es = elem.Structure;

                        IElementControl c;
                        if (this.controlMap.TryGetValue(es, out c))
                        {
                            if (c is IGroupedElementControl)
                            {
                                var gc = c as IGroupedElementControl;
                                gc.Elements.Add(elem.Data);
                            }
                            else
                            {
                                c.Data = elem.Data;
                            }
                        }
                    }
                }

                Enabled = true;
            }
            catch
            {
                this.strWarnOnSave = "The subrecord doesn't appear to conform to the expected structure.\nThe formatted information may be incorrect.";
            }
            finally
            {
                this.fpanel1.ResumeLayout();
                ResumeLayout();
                this.EndUpdate();
                Refresh();
            }
        }
예제 #17
0
        private static bool MatchRecordCheckCondition(Dictionary <int, Conditional> conditions, SubrecordStructure ss)
        {
            if (ss.Condition == CondType.Exists)
            {
                if (conditions.ContainsKey(ss.CondID))
                {
                    return(true);
                }
                else
                {
                    return(false);
                }
            }
            else if (ss.Condition == CondType.Missing)
            {
                if (conditions.ContainsKey(ss.CondID))
                {
                    return(false);
                }
                else
                {
                    return(true);
                }
            }

            Conditional cond;

            if (!conditions.TryGetValue(ss.CondID, out cond))
            {
                return(false);
            }

            switch (cond.type)
            {
            case ElementValueType.SByte:
            case ElementValueType.Byte:
            case ElementValueType.UShort:
            case ElementValueType.Short:
            case ElementValueType.Int:
            case ElementValueType.UInt:
            case ElementValueType.FormID:
            {
                int i = Convert.ToInt32(cond.value), i2;
                if (!int.TryParse(ss.CondOperand, out i2))
                {
                    return(false);
                }

                switch (ss.Condition)
                {
                case CondType.Equal:
                    return(i == i2);

                case CondType.Not:
                    return(i != i2);

                case CondType.Less:
                    return(i < i2);

                case CondType.Greater:
                    return(i > i2);

                case CondType.GreaterEqual:
                    return(i >= i2);

                case CondType.LessEqual:
                    return(i <= i2);

                default:
                    return(false);
                }
            }

            case ElementValueType.Float:
            {
                float i = (float)cond.value, i2;
                if (!float.TryParse(ss.CondOperand, out i2))
                {
                    return(false);
                }

                switch (ss.Condition)
                {
                case CondType.Equal:
                    return(i == i2);

                case CondType.Not:
                    return(i != i2);

                case CondType.Less:
                    return(i < i2);

                case CondType.Greater:
                    return(i > i2);

                case CondType.GreaterEqual:
                    return(i >= i2);

                case CondType.LessEqual:
                    return(i <= i2);

                default:
                    return(false);
                }
            }

            case ElementValueType.Str4:
            case ElementValueType.BString:
            case ElementValueType.IString:
            case ElementValueType.String:
            {
                var s = (string)cond.value;
                switch (ss.Condition)
                {
                case CondType.Equal:
                    return(s == ss.CondOperand);

                case CondType.Not:
                    return(s != ss.CondOperand);

                case CondType.StartsWith:
                    return(s.StartsWith(ss.CondOperand));

                case CondType.EndsWith:
                    return(s.EndsWith(ss.CondOperand));

                case CondType.Contains:
                    return(s.Contains(ss.CondOperand));

                default:
                    return(false);
                }
            }

            case ElementValueType.LString:
            {
                int i = (int)cond.value, i2;
                if (!int.TryParse(ss.CondOperand, out i2))
                {
                    return(false);
                }

                switch (ss.Condition)
                {
                case CondType.Equal:
                    return(i == i2);

                case CondType.Not:
                    return(i != i2);

                case CondType.Less:
                    return(i < i2);

                case CondType.Greater:
                    return(i > i2);

                case CondType.GreaterEqual:
                    return(i >= i2);

                case CondType.LessEqual:
                    return(i <= i2);

                default:
                    return(false);
                }
            }

            default:
                return(false);
            }
        }
예제 #18
0
        public void SetContext(Record r, SubRecord sr, bool hexView)
        {
            if (this.r == r && this.sr == sr && this.hexView == hexView)
            {
                return;
            }

            if (r == null || sr == null)
            {
                this.ClearControl();
                return;
            }

            // walk each element in standard fashion
            int panelOffset = 0;
            try
            {
                this.BeginUpdate();

                this.ClearControl();
                SuspendLayout();
                this.fpanel1.SuspendLayout();
                this.fpanel1.Width = Parent.Width;
                this.controlMap.Clear();

                this.hexView = hexView;
                this.r = r;
                this.sr = sr;
                var p = this.GetPluginFromNode(r);
                this.ss = sr.Structure;

                // default to blob if no elements
                if (this.ss == null || this.ss.elements == null || hexView)
                {
                    var c = new HexElement();
                    c.Left = 8;
                    c.Width = this.fpanel1.Width - 16;
                    c.Top = panelOffset;
                    c.Anchor = c.Anchor | AnchorStyles.Left | AnchorStyles.Right;

                    var elem = r.EnumerateElements(sr, true).FirstOrDefault();
                    if (elem != null)
                    {
                        this.controlMap.Add(elem.Structure, c);
                        this.fpanel1.Controls.Add(c);
                        c.Data = elem.Data;
                    }
                }
                else
                {
                    foreach (var elem in this.ss.elements)
                    {
                        Control c = null;
                        if (elem.options != null && elem.options.Length > 1)
                        {
                            c = new OptionsElement();
                        }
                        else if (elem.flags != null && elem.flags.Length > 1)
                        {
                            c = new FlagsElement();
                        }
                        else
                        {
                            switch (elem.type)
                            {
                                case ElementValueType.LString:
                                    c = new LStringElement();
                                    break;
                                case ElementValueType.FormID:
                                    c = new FormIDElement();
                                    break;
                                case ElementValueType.Blob:
                                    c = new HexElement();
                                    break;
                                default:
                                    c = new TextElement();
                                    break;
                            }
                        }

                        if (c is IElementControl)
                        {
                            var ec = c as IElementControl;
                            ec.formIDLookup = p.GetRecordByID;
                            ec.formIDScan = p.EnumerateRecords;
                            ec.strIDLookup = p.LookupFormStrings;
                            ec.Element = elem;

                            if (elem.repeat > 0)
                            {
                                var ge = new RepeatingElement();
                                c = ge;
                                c.Left = 8;
                                c.Width = this.fpanel1.Width - 16;
                                c.Top = panelOffset;
                                c.Anchor = c.Anchor | AnchorStyles.Left | AnchorStyles.Right;

                                ge.InnerControl = ec;
                                ge.Element = elem;
                                ec = ge;
                            }
                            else if (elem.optional > 0)
                            {
                                var re = new OptionalElement();
                                c = re;
                                c.Left = 8;
                                c.Width = this.fpanel1.Width - 16;
                                c.Top = panelOffset;
                                c.Anchor = c.Anchor | AnchorStyles.Left | AnchorStyles.Right;

                                re.InnerControl = ec;
                                re.Element = elem;
                                ec = re;
                                c = re;
                            }
                            else
                            {
                                c.Left = 8;
                                c.Width = this.fpanel1.Width - 16;
                                c.Top = panelOffset;
                                c.Anchor = c.Anchor | AnchorStyles.Left | AnchorStyles.Right;
                            }

                            c.MinimumSize = c.Size;

                            this.controlMap.Add(elem, ec);
                            this.fpanel1.Controls.Add(c);
                            panelOffset = c.Bottom;
                        }
                    }

                    foreach (Element elem in r.EnumerateElements(sr, true))
                    {
                        var es = elem.Structure;

                        IElementControl c;
                        if (this.controlMap.TryGetValue(es, out c))
                        {
                            if (c is IGroupedElementControl)
                            {
                                var gc = c as IGroupedElementControl;
                                gc.Elements.Add(elem.Data);
                            }
                            else
                            {
                                c.Data = elem.Data;
                            }
                        }
                    }
                }

                Enabled = true;
            }
            catch
            {
                this.strWarnOnSave = "The subrecord doesn't appear to conform to the expected structure.\nThe formatted information may be incorrect.";
            }
            finally
            {
                this.fpanel1.ResumeLayout();
                ResumeLayout();
                this.EndUpdate();
                Refresh();
            }
        }
예제 #19
0
        public static void GetFormattedData(this SubRecord rec, RTFBuilder s)
        {
            SubrecordStructure ss = rec.Structure;

            if (ss == null || ss.elements == null)
            {
                s.Append("String:\t").AppendLine(rec.GetStrData()).AppendLine();
                s.Append("Hex: \t").AppendLine(rec.GetHexData());
                s.AppendPara();
                return;
            }

            bool addTerminatingParagraph = false;

            try
            {
                var p = rec.GetPlugin();

                dFormIDLookupI formIDLookup  = p.LookupFormID;
                dLStringLookup strLookup     = p.LookupFormStrings;
                dFormIDLookupR formIDLookupR = p.GetRecordByID;

                // Table of items
                var table = new List <List <RTFCellDefinition> >();

                // set up elements
                float maxWidth          = 0;
                int   maxFirstCellWidth = 0;

                var elems = rec.EnumerateElements(true).Where(x => x.Structure != null && !x.Structure.notininfo).ToList();
                if (elems.Count == 0)
                {
                    return;
                }

                foreach (var element in elems)
                {
                    Size sz    = s.MeasureText(element.Structure.name);
                    int  width = Math.Max(sz.Width / 11, 10);

                    // approximate convert pixels to twips as the rtflib has crap documentation
                    if (width > maxFirstCellWidth)
                    {
                        maxFirstCellWidth = width;
                    }
                }

                foreach (var element in elems)
                {
                    var row = new List <RTFCellDefinition>();
                    table.Add(row);
                    var  sselem     = element.Structure;
                    bool hasOptions = sselem.options != null && sselem.options.Length > 0;
                    bool hasFlags   = sselem.flags != null && sselem.flags.Length > 1;

                    // setup borders for header
                    var value    = element.Value;
                    var nameCell = new RTFCellDefinition(maxFirstCellWidth, RTFAlignment.MiddleLeft, RTFBorderSide.Default, 15, Color.DarkGray, Padding.Empty);
                    row.Add(nameCell);
                    switch (sselem.type)
                    {
                    case ElementValueType.FormID:
                        row.Add(new RTFCellDefinition(12, RTFAlignment.MiddleLeft, RTFBorderSide.Default, 15, Color.DarkGray, Padding.Empty));
                        row.Add(new RTFCellDefinition(30, RTFAlignment.MiddleLeft, RTFBorderSide.Default, 15, Color.DarkGray, Padding.Empty));

                        // Optional Add cell for
                        break;

                    case ElementValueType.LString:
                        row.Add(new RTFCellDefinition(12, RTFAlignment.MiddleLeft, RTFBorderSide.Default, 15, Color.DarkGray, Padding.Empty));
                        row.Add(new RTFCellDefinition(30, RTFAlignment.MiddleLeft, RTFBorderSide.Default, 15, Color.DarkGray, Padding.Empty));
                        break;

                    case ElementValueType.BString:
                    case ElementValueType.IString:
                    case ElementValueType.String:
                        row.Add(new RTFCellDefinition(42, RTFAlignment.MiddleLeft, RTFBorderSide.Default, 15, Color.DarkGray, Padding.Empty));
                        break;

                    case ElementValueType.Int:
                    case ElementValueType.UInt:
                    case ElementValueType.Byte:
                    case ElementValueType.SByte:
                    case ElementValueType.Short:
                    case ElementValueType.UShort:
                    case ElementValueType.Float:
                        row.Add(new RTFCellDefinition(20, RTFAlignment.MiddleLeft, RTFBorderSide.Default, 15, Color.DarkGray, Padding.Empty));
                        row.Add(new RTFCellDefinition(30, RTFAlignment.MiddleLeft, RTFBorderSide.Default, 15, Color.DarkGray, Padding.Empty));
                        break;

                    case ElementValueType.Blob:
                        row.Add(new RTFCellDefinition(42, RTFAlignment.MiddleLeft, RTFBorderSide.Default, 15, Color.DarkGray, Padding.Empty));
                        break;

                    case ElementValueType.Str4:
                        row.Add(new RTFCellDefinition(42, RTFAlignment.MiddleLeft, RTFBorderSide.Default, 15, Color.DarkGray, Padding.Empty));
                        break;

                    default:
                        row.Add(new RTFCellDefinition(42, RTFAlignment.MiddleLeft, RTFBorderSide.Default, 15, Color.DarkGray, Padding.Empty));
                        break;
                    }

                    maxWidth = Math.Max(maxWidth, row.Sum(x => x.CellWidthRaw));
                }

                var rowWidth = (int)(maxWidth * 100.0f);
                var pd       = new Padding {
                    All = 50
                };

                var hdrd   = new RTFRowDefinition(rowWidth, RTFAlignment.TopLeft, RTFBorderSide.Default, 15, SystemColors.WindowText, pd);
                var hdrcds = new[] { new RTFCellDefinition(rowWidth, RTFAlignment.MiddleLeft, RTFBorderSide.Default, 15, Color.DarkGray, Padding.Empty) };

                addTerminatingParagraph = true;
                s.Reset();
                using (IRTFRow ie = s.CreateRow(hdrd, hdrcds))
                {
                    foreach (var item in ie)
                    {
                        var rb = item.Content;
                        item.Content.FontSize(s.DefaultFontSize + 1).FontStyle(FontStyle.Bold).ForeColor(KnownColor.DarkCyan).AppendFormat("{0} ({1})", ss.name, ss.desc);
                    }
                }

                for (int rowIdx = 0; rowIdx < elems.Count; ++rowIdx)
                {
                    var rd     = new RTFRowDefinition(rowWidth, RTFAlignment.TopLeft, RTFBorderSide.Default, 15, SystemColors.WindowText, pd);
                    var cds    = table[rowIdx];
                    var elem   = elems[rowIdx];
                    var sselem = elem.Structure;
                    var value  = elem.Value;

                    string recprefix  = null;
                    Record record     = null;
                    string strValue   = null; // value to display
                    string strDesc    = null; // first description
                    string strDesc2   = null; // second description
                    bool   hasOptions = sselem.options != null && sselem.options.Length > 0;
                    bool   hasFlags   = sselem.flags != null && sselem.flags.Length > 1;

                    if (!string.IsNullOrWhiteSpace(elem.Structure.funcr))
                    {
                        if (elem.Type == ElementValueType.Float)
                        {
                            value = PyInterpreter.ExecuteFunction <float>(elem, FunctionOperation.ForReading);
                        }
                        else if (elem.Type == ElementValueType.Int)
                        {
                            value = PyInterpreter.ExecuteFunction <int>(elem, FunctionOperation.ForReading);
                        }
                        else if (elem.Type == ElementValueType.Short)
                        {
                            value = PyInterpreter.ExecuteFunction <short>(elem, FunctionOperation.ForReading);
                        }
                        else if (elem.Type == ElementValueType.UShort)
                        {
                            value = PyInterpreter.ExecuteFunction <ushort>(elem, FunctionOperation.ForReading);
                        }
                        else if (elem.Type == ElementValueType.UInt)
                        {
                            value = PyInterpreter.ExecuteFunction <uint>(elem, FunctionOperation.ForReading);
                        }
                    }

                    // Pre row write caching to avoid expensive duplicate calls between cells
                    switch (sselem.type)
                    {
                    case ElementValueType.FormID:
                    {
                        var id = (uint)value;
                        strValue = id.ToString("X8");
                        if (id != 0)
                        {
                            record = formIDLookupR != null?formIDLookupR(id) : null;
                        }

                        if (record != null)
                        {
                            var pref = record.GetPlugin();
                            recprefix = pref != null?string.Format("{0}@{1}", pref.Name, record.Name) : record.Name;

                            strDesc = record.DescriptiveName;
                            var full = record.SubRecords.FirstOrDefault(x => x.Name == "FULL");
                            if (full != null)
                            {
                                // split the cell 2 in 2 if full name found
                                var    data     = new ArraySegment <byte>(full.GetReadonlyData());
                                bool   isString = TypeConverter.IsLikelyString(data);
                                string lvalue   = isString ? full.GetStrData() : strLookup != null?strLookup(TypeConverter.h2i(data)) : null;

                                if (!string.IsNullOrEmpty(lvalue))
                                {
                                    var  first = cds[cds.Count - 1];
                                    Size sz    = s.MeasureText(lvalue);
                                    int  width = Math.Min(40, Math.Max(sz.Width / 12, 10));

                                    // approximate convert pixels to twips as the rtflib has crap documentation
                                    var second = new RTFCellDefinition(width, RTFAlignment.MiddleLeft, RTFBorderSide.Default, 0, Color.DarkGray, Padding.Empty);
                                    cds.Add(second);
                                    strDesc2 = lvalue;
                                }
                            }
                        }
                    }

                    break;

                    case ElementValueType.LString:
                    {
                        if (elem.Type == ElementValueType.String)
                        {
                            strValue = string.Empty;
                            strDesc  = value.ToString();
                        }
                        else if (TypeConverter.IsLikelyString(elem.Data))
                        {
                            strValue = string.Empty;
                            strDesc  = TypeConverter.GetString(elem.Data);
                        }
                        else
                        {
                            uint id = TypeConverter.h2i(elem.Data);
                            strValue = id.ToString("X8");
                            strDesc  = strLookup != null?strLookup(id) : null;
                        }
                    }

                    break;

                    case ElementValueType.Blob:
                        strValue = TypeConverter.GetHexData(elem.Data);
                        break;

                    case ElementValueType.SByte:
                    case ElementValueType.Int:
                    case ElementValueType.Short:
                    {
                        if (sselem.hexview || hasFlags)
                        {
                            if (sselem.hexviewwithdec)
                            {
                                strValue = string.Format(string.Format("{{0:X{0}}}", elem.Data.Count * 2), value) + string.Format(" : {0}", value);
                            }
                            else
                            {
                                strValue = string.Format(string.Format("{{0:X{0}}}", elem.Data.Count * 2), value);
                            }
                        }
                        else
                        {
                            strValue = value == null ? string.Empty : value.ToString();
                        }

                        if (hasOptions)
                        {
                            int intVal = Convert.ToInt32(value);
                            for (int k = 0; k < sselem.options.Length; k += 2)
                            {
                                int intValOption;
                                if (int.TryParse(sselem.options[k + 1], out intValOption) && intVal == intValOption)
                                {
                                    strDesc = sselem.options[k];
                                }
                            }
                        }
                        else if (hasFlags)
                        {
                            int intVal = Convert.ToInt32(value);
                            var tmp2   = new StringBuilder();
                            for (int k = 0; k < sselem.flags.Length; k++)
                            {
                                if ((intVal & (1 << k)) != 0)
                                {
                                    if (tmp2.Length > 0)
                                    {
                                        tmp2.Append(", ");
                                    }

                                    tmp2.Append(sselem.flags[k]);
                                }
                            }

                            strDesc = tmp2.ToString();
                        }
                    }

                    break;

                    case ElementValueType.UInt:
                    case ElementValueType.Byte:
                    case ElementValueType.UShort:
                    {
                        if (sselem.hexview || hasFlags)
                        {
                            if (sselem.hexviewwithdec)
                            {
                                strValue = string.Format(string.Format("{{0:X{0}}}", elem.Data.Count * 2), value) + string.Format(" : {0}", value);
                            }
                            else
                            {
                                strValue = string.Format(string.Format("{{0:X{0}}}", elem.Data.Count * 2), value);
                            }
                        }
                        else
                        {
                            strValue = value == null ? string.Empty : value.ToString();
                        }

                        if (hasOptions)
                        {
                            uint intVal = Convert.ToUInt32(value);
                            for (int k = 0; k < sselem.options.Length; k += 2)
                            {
                                if (intVal == uint.Parse(sselem.options[k + 1]))
                                {
                                    strDesc = sselem.options[k];
                                }
                            }
                        }
                        else if (hasFlags)
                        {
                            uint intVal = Convert.ToUInt32(value);
                            var  tmp2   = new StringBuilder();
                            for (int k = 0; k < sselem.flags.Length; k++)
                            {
                                if ((intVal & (1 << k)) != 0)
                                {
                                    if (tmp2.Length > 0)
                                    {
                                        tmp2.Append(", ");
                                    }

                                    tmp2.Append(sselem.flags[k]);
                                }
                            }

                            strDesc = tmp2.ToString();
                        }
                    }

                    break;

                    case ElementValueType.Str4:
                        strValue = TypeConverter.GetString(elem.Data);
                        break;

                    case ElementValueType.BString:
                        strValue = TypeConverter.GetBString(elem.Data);
                        break;

                    case ElementValueType.IString:
                        strValue = TypeConverter.GetIString(elem.Data);
                        break;

                    default:
                        strValue = value == null ? string.Empty : value.ToString();
                        break;
                    }

                    // Now create row and fill in cells
                    using (IRTFRow ie = s.CreateRow(rd, cds))
                    {
                        int colIdx = 0;
                        IEnumerator <IBuilderContent> ie2 = ie.GetEnumerator();
                        for (bool ok = ie2.MoveNext(); ok; ok = ie2.MoveNext(), ++colIdx)
                        {
                            using (var item = ie2.Current)
                            {
                                var rb = item.Content;
                                if (colIdx == 0)
                                {
                                    // name
                                    rb.FontStyle(FontStyle.Bold).Append(sselem.name);
                                }
                                else if (colIdx == 1)
                                {
                                    // value
                                    switch (sselem.type)
                                    {
                                    case ElementValueType.FormID:
                                        if (((uint)value) == 0)
                                        {
                                            rb.Append(strValue);
                                        }
                                        else if (record != null)
                                        {
                                            RTFRenderer.AppendLink(rb, strValue, record.GetLink());
                                        }
                                        else if (!string.IsNullOrEmpty(sselem.FormIDType))
                                        {
                                            RTFRenderer.AppendLink(rb, strValue, string.Format("{0}:{1}", sselem.FormIDType, strValue));
                                        }
                                        else
                                        {
                                            RTFRenderer.AppendLink(rb, strValue, string.Format("XXXX:{0}", strValue));
                                        }

                                        break;

                                    default:
                                        rb.Append(strValue);
                                        break;
                                    }
                                }
                                else if (colIdx == 2)
                                {
                                    // desc
                                    if (!string.IsNullOrEmpty(strDesc))
                                    {
                                        rb.Append(strDesc);
                                    }
                                }
                                else if (colIdx == 3)
                                {
                                    // desc2
                                    if (!string.IsNullOrEmpty(strDesc2))
                                    {
                                        rb.Append(strDesc2);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch
            {
                s.AppendLine("Warning: Subrecord doesn't seem to match the expected structure");
            }
            finally
            {
                if (addTerminatingParagraph)
                {
                    s.Reset();
                    s.AppendPara();
                }
            }
        }