/// <summary> /// cf. GeneralGFF.fileclick_Create() /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void contextclick_AddTopLevelStruct(object sender, EventArgs e) { var field = new GffData.Field(); field.type = FieldTypes.Struct; if (_f.GffData != null) { field.label = Path.GetFileNameWithoutExtension(_f.GffData.Pfe).ToUpper(); } else { _f.GffData = new GffData(); // init GffData! -> _f.GffData.TypeVer = "GFF V3.2"; _f.GffData.Type = GffType.generic; field.label = Globals.TopLevelStruct; } field.Struct = new Struct(); field.Struct.typeid = UInt32.MaxValue; Nodes.Add(field.label); SelectedNode = Nodes[0]; }
void contextclick_AddInt64(object sender, EventArgs e) { var field = new GffData.Field(); field.type = FieldTypes.INT64; field.label = GetUniqueLabel(); field.INT64 = 0; AddField(field); }
void contextclick_AddFloat(object sender, EventArgs e) { var field = new GffData.Field(); field.type = FieldTypes.FLOAT; field.label = GetUniqueLabel(); field.FLOAT = 0; AddField(field); }
void contextclick_AddDouble(object sender, EventArgs e) { var field = new GffData.Field(); field.type = FieldTypes.DOUBLE; field.label = GetUniqueLabel(); field.DOUBLE = 0; AddField(field); }
void contextclick_AddCExoString(object sender, EventArgs e) { var field = new GffData.Field(); field.type = FieldTypes.CExoString; field.label = GetUniqueLabel(); field.CExoString = String.Empty; AddField(field); }
void contextclick_AddCExoLocString(object sender, EventArgs e) { var field = new GffData.Field(); field.type = FieldTypes.CExoLocString; field.label = GetUniqueLabel(); field.CExoLocStrref = UInt32.MaxValue; AddField(field); }
void contextclick_AddVoid(object sender, EventArgs e) { var field = new GffData.Field(); field.type = FieldTypes.VOID; field.label = GetUniqueLabel(); field.VOID = new byte[0]; AddField(field); }
void contextclick_AddList(object sender, EventArgs e) { var field = new GffData.Field(); field.type = FieldTypes.List; field.label = GetUniqueLabel(); // field.List = new List<uint>(); AddField(field); }
void contextclick_AddChar(object sender, EventArgs e) { var field = new GffData.Field(); field.type = FieldTypes.CHAR; field.label = GetUniqueLabel(); field.CHAR = 0; AddField(field); }
void contextclick_AddDword(object sender, EventArgs e) { var field = new GffData.Field(); field.type = FieldTypes.DWORD; field.label = GetUniqueLabel(); field.DWORD = 0; AddField(field); }
/// <summary> /// Adds a field to the TreeList and selects it. /// </summary> /// <param name="field"></param> /// <param name="locale"></param> void AddField(GffData.Field field, GffData.Locale locale = null) { string text = GeneralGFF.ConstructNodetext(field, locale); var node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes.Add(node); SelectedNode = node; _f.GffData.Changed = true; _f.GffData = _f.GffData; }
/// <summary> /// Adds a Struct (a Feat structure) to the "FeatList" List. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void contextclick_AddFeat(object sender, EventArgs e) { BeginUpdate(); TreeNode top = TopNode; var field = new GffData.Field(); field.type = FieldTypes.Struct; field.label = SelectedNode.Nodes.Count.ToString(); // Structs in Lists do not have a Label field.Struct = new Struct(); field.Struct.typeid = 1; // <- that's what's in the UTCs I've looked at. string text = GeneralGFF.ConstructNodetext(field); var node = new Sortable(text, field.label); node.Tag = field; int id = SelectedNode.Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.WORD; field.label = "Feat"; field.WORD = 0; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); SelectedNode = SelectedNode.Nodes[id]; SelectedNode.Expand(); TopNode = top; _f.GffData.Changed = true; _f.GffData = _f.GffData; EndUpdate(); node.EnsureVisible(); // yes those calls are in a specific sequence. }
void contextclick_AddStruct(object sender, EventArgs e) { var field = new GffData.Field(); field.type = FieldTypes.Struct; if (SelectedNode.Tag != null && ((GffData.Field)SelectedNode.Tag).type == FieldTypes.List) { field.label = SelectedNode.Nodes.Count.ToString(); } else { field.label = GetUniqueLabel(); } field.Struct = new Struct(); field.Struct.typeid = 0; AddField(field); }
/// <summary> /// Adds the three Structs of a tint to a specified node. /// </summary> /// <param name="node"></param> void AddTints(TreeNode node) { GffData.Field field; Sortable part, val; for (int i = 1; i != 4; ++i) { field = new GffData.Field(); field.type = FieldTypes.Struct; field.label = i.ToString(); field.Struct = new Struct(); field.Struct.typeid = 0; // <- that's what's in the UTCs I've looked at. string text = GeneralGFF.ConstructNodetext(field); part = new Sortable(text, field.label); part.Tag = field; node.Nodes.Add(part); var colors = new List <string>() { "a", "b", "g", "r" }; foreach (var color in colors) { field = new GffData.Field(); field.type = FieldTypes.BYTE; field.BYTE = Byte.MaxValue; field.label = color; text = GeneralGFF.ConstructNodetext(field); val = new Sortable(text, field.label); val.Tag = field; part.Nodes.Add(val); } part.Expand(); } node.Expand(); }
/// <summary> /// Adds a Locale to a CExoLocString Field. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void contextclick_AddLocale(object sender, EventArgs e) { var field = (GffData.Field)SelectedNode.Tag; if (field.localeflags != LocaleDialog.Loc_ALL) { using (var f = new LocaleDialog(field.localeflags)) { if (f.ShowDialog(this) == DialogResult.OK) { var locale = new GffData.Locale(); locale.local = String.Empty; LocaleDialog.SetLocaleFlag(ref field.localeflags, locale.langid = _langid, locale.F = _langf); if (field.Locales == null) { field.Locales = new List <GffData.Locale>(); } var fieldloc = new GffData.Field(); fieldloc.type = FieldTypes.locale; fieldloc.label = GffData.Locale.GetLanguageString(_langid, _langf); fieldloc.localeid = (uint)field.Locales.Count; field.Locales.Add(locale); AddField(fieldloc, locale); } } } else { using (var f = new InfoDialog(Globals.Error, "All locales are taken.")) f.ShowDialog(this); } }
/// <summary> /// Adds a Field to a treenode. /// </summary> /// <param name="field">a Field to add</param> /// <param name="parent">a treenode to add it to</param> /// <param name="locale">a locale if applicable</param> static void AddField(GffData.Field field, TreeNode parent, GffData.Locale locale = null) { // TODO: Refactor things throughout the code such that the Tag of a // treenode is *either* a GffData.Field *or* a GffData.Locale. string text = GeneralGFF.ConstructNodetext(field, locale); var node = new Sortable(text, field.label); node.Tag = field; parent.Nodes.Add(node); switch (field.type) { case FieldTypes.Struct: // childs can be of any Type. { List <uint> fieldids = field.Struct.fieldids; for (int i = 0; i != fieldids.Count; ++i) { AddField(GffReader.Fields[(int)fieldids[i]], node); } break; } case FieldTypes.List: // childs are Structs. { List <uint> list = field.List; for (int i = 0; i != list.Count; ++i) { // NOTE: Structs in Lists do not have a Label inside a GFF-file. // so give Structs in Lists a pseudo-label for their treenode(s) field = new GffData.Field(); field.type = FieldTypes.Struct; field.label = i.ToString(); field.Struct = GffReader.Structs[(int)list[i]]; AddField(field, node); } break; } case FieldTypes.CExoLocString: // childs are Locales. if (field.Locales != null) { int locales = field.Locales.Count; for (int i = 0; i != locales; ++i) { locale = field.Locales[i]; var fieldloc = new GffData.Field(); fieldloc.type = FieldTypes.locale; fieldloc.localeid = (uint)i; fieldloc.label = GffData.Locale.GetLanguageString(locale.langid, locale.F); AddField(fieldloc, node, locale); LocaleDialog.SetLocaleFlag(ref field.localeflags, locale.langid, locale.F); } } break; } }
/// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void contextclick_AddDamageReduction(object sender, EventArgs e) { BeginUpdate(); TreeNode top = TopNode; var field = new GffData.Field(); field.type = FieldTypes.Struct; field.label = SelectedNode.Nodes.Count.ToString(); // Structs in Lists do not have a Label field.Struct = new Struct(); field.Struct.typeid = 2; // <- that's what's in the UTCs I've looked at. string text = GeneralGFF.ConstructNodetext(field); var node = new Sortable(text, field.label); node.Tag = field; int id = SelectedNode.Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.SHORT; field.label = "DmgRedctAmt"; field.SHORT = 0; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.SHORT; field.label = "DmgRedctFlags"; field.SHORT = 0; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.List; field.label = "DmgRedctSubList"; text = GeneralGFF.ConstructNodetext(field); var list = new Sortable(text, field.label); list.Tag = field; SelectedNode.Nodes[id].Nodes.Add(list); field = new GffData.Field(); field.type = FieldTypes.Struct; field.label = "0"; // Structs in Lists do not have a Label field.Struct = new Struct(); field.Struct.typeid = 2; // <- that's what's in the UTCs I've looked at. text = GeneralGFF.ConstructNodetext(field); var @struct = new Sortable(text, field.label); @struct.Tag = field; list.Nodes.Add(@struct); field = new GffData.Field(); field.type = FieldTypes.SHORT; field.label = "DmgRedctSubType"; field.SHORT = 0; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; @struct.Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.SHORT; field.label = "DmgRedctType"; field.SHORT = 0; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; @struct.Nodes.Add(node); SelectedNode = SelectedNode.Nodes[id]; SelectedNode.Expand(); list.Expand(); @struct.Expand(); TopNode = top; _f.GffData.Changed = true; _f.GffData = _f.GffData; EndUpdate(); node.EnsureVisible(); // yes those calls are in a specific sequence. }
/// <summary> /// Adds an apparel-type. /// </summary> void AddApparel(string label) { BeginUpdate(); TreeNode top = TopNode; var field = new GffData.Field(); field.type = FieldTypes.Struct; field.label = label; field.Struct = new Struct(); field.Struct.typeid = 0; // <- that's what's in the UTCs I've looked at. string text = GeneralGFF.ConstructNodetext(field); var node = new Sortable(text, field.label); node.Tag = field; int id = SelectedNode.Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.Struct; field.label = "ArmorTint"; field.Struct = new Struct(); field.Struct.typeid = 0; // <- that's what's in the UTCs I've looked at. text = GeneralGFF.ConstructNodetext(field); var tint = new Sortable(text, field.label); tint.Tag = field; SelectedNode.Nodes[id].Nodes.Add(tint); AddTints(tint); field = new GffData.Field(); field.type = FieldTypes.BYTE; field.BYTE = 0; field.label = "ArmorVisualType"; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.BYTE; field.BYTE = 0; field.label = "Variation"; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); SelectedNode = SelectedNode.Nodes[id]; SelectedNode.Expand(); TopNode = top; _f.GffData.Changed = true; _f.GffData = _f.GffData; EndUpdate(); node.EnsureVisible(); // yes those calls are in a specific sequence. }
/// <summary> /// Adds a Struct (a Class structure) to the "ClassList" List. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void contextclick_AddClass(object sender, EventArgs e) { BeginUpdate(); TreeNode top = TopNode; var field = new GffData.Field(); field.type = FieldTypes.Struct; field.label = SelectedNode.Nodes.Count.ToString(); // Structs in Lists do not have a Label field.Struct = new Struct(); field.Struct.typeid = 2; // <- that's what's in the UTCs I've looked at. string text = GeneralGFF.ConstructNodetext(field); var node = new Sortable(text, field.label); node.Tag = field; int id = SelectedNode.Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.INT; field.label = "Class"; field.INT = 0; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.SHORT; field.label = "ClassLevel"; field.SHORT = 1; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); for (int i = 0; i != 10; ++i) { field = new GffData.Field(); field.type = FieldTypes.List; field.label = LABEL_PREFIX_KNOWN + i; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); } for (int i = 0; i != 10; ++i) { field = new GffData.Field(); field.type = FieldTypes.List; field.label = LABEL_PREFIX_MEMORIZED + i; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); } SelectedNode = SelectedNode.Nodes[id]; SelectedNode.Expand(); TopNode = top; _f.GffData.Changed = true; _f.GffData = _f.GffData; EndUpdate(); node.EnsureVisible(); // yes those calls are in a specific sequence. }
/// <summary> /// Adds a Struct (an InventoryItem structure) to the "ItemList" List. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void contextclick_AddInventoryItem(object sender, EventArgs e) { BeginUpdate(); TreeNode top = TopNode; int id = SelectedNode.Nodes.Count; var field = new GffData.Field(); field.type = FieldTypes.Struct; field.label = id.ToString(); // Structs in Lists do not have a Label field.Struct = new Struct(); field.Struct.typeid = (uint)id; // <- that's what's in the UTCs I've looked at. string text = GeneralGFF.ConstructNodetext(field); var node = new Sortable(text, field.label); node.Tag = field; id = SelectedNode.Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.BYTE; field.label = "Dropable"; field.BYTE = 0; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.CResRef; field.label = "EquippedRes"; field.CResRef = String.Empty; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.BYTE; field.label = "Pickpocketable"; field.BYTE = 0; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.WORD; field.label = "Repos_PosX"; field.WORD = 0; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.WORD; field.label = "Repos_PosY"; field.WORD = 0; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); SelectedNode = SelectedNode.Nodes[id]; SelectedNode.Expand(); TopNode = top; _f.GffData.Changed = true; _f.GffData = _f.GffData; EndUpdate(); node.EnsureVisible(); // yes those calls are in a specific sequence. }
/// <summary> /// Adds a Struct (an EquippedItem structure) to the "Equip_ItemList" List. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void contextclick_AddEquippedItem(object sender, EventArgs e) { GffData.Field field; _bitslot = 0; for (int i = 0; i != SelectedNode.Nodes.Count; ++i) { field = (GffData.Field)SelectedNode.Nodes[i].Tag; _bitslot |= field.Struct.typeid; } using (var f = new EquippedItemDialog(_bitslot)) { _bitslot = 0; if (f.ShowDialog(this) == DialogResult.OK) { BeginUpdate(); TreeNode top = TopNode; field = new GffData.Field(); field.type = FieldTypes.Struct; field.label = SelectedNode.Nodes.Count.ToString(); // Structs in Lists do not have a Label field.Struct = new Struct(); field.Struct.typeid = _bitslot; // <- that's what's in the UTCs I've looked at. string text = GeneralGFF.ConstructNodetext(field); var node = new Sortable(text, field.label); node.Tag = field; int id = SelectedNode.Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.BYTE; field.label = "Dropable"; field.BYTE = 0; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.CResRef; field.label = "EquippedRes"; field.CResRef = String.Empty; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.BYTE; field.label = "Pickpocketable"; field.BYTE = 0; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.WORD; field.label = "Repos_PosX"; field.WORD = 0; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.WORD; field.label = "Repos_PosY"; field.WORD = 0; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); // keep the List's nodes in the correct sequence -> // they are arranged by Struct.typeid ascending int i; // iterator for the sortables int j; // tracker for the node # added // store all the sortable Structs in the List -> // NOTE: This is not an ideal routine. Because it adds all // the nodes to the tree, then removes them, then adds them // all again. It should, rather, just construct the Struct // and its fields, then add the Struct to the tree. var sortables = new List <Sortable>(); for (i = 0; i != SelectedNode.Nodes.Count; ++i) { sortables.Add((Sortable)SelectedNode.Nodes[i]); } SelectedNode.Nodes.Clear(); // NOTE: 'SelectedNode' is the "Equip_ItemList" list-field. // re-add the sortables before the added sortable -> i = j = 0; while (i != sortables.Count) { field = (GffData.Field)sortables[i].Tag; if (field.Struct.typeid < _bitslot) { // NOTE: 'field.label' doesn't need to be changed here sortables[i].Text = GeneralGFF.ConstructNodetextEquipped(field); SelectedNode.Nodes.Add(sortables[i]); j = i; } ++i; } id = ++j; // re-add the last sortable into the sequence -> // replace vals that were set above for the Struct's label and its Sortable's text and label field = (GffData.Field)sortables[sortables.Count - 1].Tag; field.label = sortables[sortables.Count - 1]._label = j.ToString(); sortables[sortables.Count - 1].Text = GeneralGFF.ConstructNodetextEquipped(field); SelectedNode.Nodes.Add(sortables[sortables.Count - 1]); // re-add the sortables that go after the added sortable -> i = 0; while (i != sortables.Count) { field = (GffData.Field)sortables[i].Tag; if (field.Struct.typeid > _bitslot) { field.label = sortables[i]._label = (++j).ToString(); sortables[i].Text = GeneralGFF.ConstructNodetextEquipped(field); SelectedNode.Nodes.Add(sortables[i]); } ++i; } SelectedNode = SelectedNode.Nodes[id]; SelectedNode.Expand(); TopNode = top; _f.GffData.Changed = true; _f.GffData = _f.GffData; EndUpdate(); node.EnsureVisible(); // yes those calls are in a specific sequence. } } }
/// <summary> /// Adds a Struct (a Variable structure) to the "VarTable" List. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void contextclick_AddVariable(object sender, EventArgs e) { using (var dialog = new VariableDialog()) { if (dialog.ShowDialog(this) == DialogResult.OK) { BeginUpdate(); TreeNode top = TopNode; var field = new GffData.Field(); field.type = FieldTypes.Struct; field.label = SelectedNode.Nodes.Count.ToString(); // Structs in Lists do not have a Label field.Struct = new Struct(); field.Struct.typeid = 0; // <- that's what's in the UTCs I've looked at. string text = GeneralGFF.ConstructNodetext(field); var node = new Sortable(text, field.label); node.Tag = field; int id = SelectedNode.Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.CExoString; field.label = "Name"; field.CExoString = _varLabel; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); field = new GffData.Field(); field.type = FieldTypes.DWORD; field.label = "Type"; field.DWORD = _varType; text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); field = new GffData.Field(); field.label = "Value"; switch (_varType) // TODO: delete the nodes above or put this above there { case VariableDialog.Type_non: // not stable in toolset. is Disabled in the dialog return; case VariableDialog.Type_INT: field.type = FieldTypes.INT; field.INT = Int32.Parse(_varValue); break; case VariableDialog.Type_FLOAT: field.type = FieldTypes.FLOAT; field.FLOAT = Single.Parse(_varValue); break; case VariableDialog.Type_STRING: field.type = FieldTypes.CExoString; field.CExoString = _varValue; break; case VariableDialog.Type_LOCATION: // not stable in toolset. is Disabled in the dialog return; case VariableDialog.Type_UINT: // and I can't see this being useful at all. field.type = FieldTypes.DWORD; field.DWORD = UInt32.Parse(_varValue); break; } text = GeneralGFF.ConstructNodetext(field); node = new Sortable(text, field.label); node.Tag = field; SelectedNode.Nodes[id].Nodes.Add(node); SelectedNode = SelectedNode.Nodes[id]; SelectedNode.Expand(); TopNode = top; _f.GffData.Changed = true; _f.GffData = _f.GffData; EndUpdate(); node.EnsureVisible(); // yes those calls are in a specific sequence. } } }
/* static readonly List<uint> _fieldids = new List<uint>(); * /// <summary> * /// * /// </summary> * internal static List<uint> FieldIds * { get { return _fieldids; } } */ #endregion Properties (static) #region Methods (static) /// <summary> /// Reads a GFF-file and parses out its data to a GFFData object. /// @note Sections will be extracted in a non-arbitrary sequence because /// the data in a specific section could rely on the data in a different /// section. The order I have chosen is: /// 1. Header data (ofc) /// 2. FieldIndices /// 3. Structs - relies on FieldIndices /// 4. Labels /// 5. Fields - relies on Structs and Labels and extracts FieldData and ListIndices /// </summary> /// <param name="pfe">path-file-extension - ensure file exists before call</param> internal static GffData ReadGFFfile(string pfe) { Structs.Clear(); // Structs and Fields will be cleared after load completes Fields.Clear(); // but do it here (before load starts) also - jic. byte[] bytes = FileService.ReadFile(pfe); if (bytes != null) { if (bytes.Length != 0) { uint pos = 0; uint b; var buffer = new byte[8]; for (b = 0; b != 8; ++b) { buffer[b] = bytes[pos++]; } string ver = Encoding.ASCII.GetString(buffer, 0, buffer.Length); if (ver.Substring(3) != Globals.SupportedVersion) { FileService.error("That is not a version 3.2 GFF file."); return(null); } bool le = BitConverter.IsLittleEndian; // hardware architecture var data = new GffData(pfe); data.TypeVer = ver; data.Type = GffData.GetGffType(ver.Substring(0, 3)); data.Latest = File.GetLastWriteTime(pfe); FileWatchDialog.Bypass = false; // HEADER METADATA -> pos = head_StructOffset; buffer = new byte[4]; for (b = 0; b != 4; ++b) { buffer[b] = bytes[pos++]; } if (!le) { Array.Reverse(buffer); } uint StructOffset = BitConverter.ToUInt32(buffer, 0); //logfile.Log("StructOffset= " + StructOffset); // The Struct-section will always start at 56-bytes (0x38) if (StructOffset != Globals.Length_HEADER) { FileService.error("That does not appear to be a GFF file."); return(null); } pos = head_StructCount; buffer = new byte[4]; for (b = 0; b != 4; ++b) { buffer[b] = bytes[pos++]; } if (!le) { Array.Reverse(buffer); } uint StructCount = BitConverter.ToUInt32(buffer, 0); // count of elements //logfile.Log("StructCount= " + StructCount); pos = head_FieldOffset; buffer = new byte[4]; for (b = 0; b != 4; ++b) { buffer[b] = bytes[pos++]; } if (!le) { Array.Reverse(buffer); } uint FieldOffset = BitConverter.ToUInt32(buffer, 0); //logfile.Log("FieldOffset= " + FieldOffset); pos = head_FieldCount; buffer = new byte[4]; for (b = 0; b != 4; ++b) { buffer[b] = bytes[pos++]; } if (!le) { Array.Reverse(buffer); } uint FieldCount = BitConverter.ToUInt32(buffer, 0); // count of elements //logfile.Log("FieldCount= " + FieldCount); pos = head_LabelOffset; buffer = new byte[4]; for (b = 0; b != 4; ++b) { buffer[b] = bytes[pos++]; } if (!le) { Array.Reverse(buffer); } uint LabelOffset = BitConverter.ToUInt32(buffer, 0); //logfile.Log("LabelOffset= " + LabelOffset); pos = head_LabelCount; buffer = new byte[4]; for (b = 0; b != 4; ++b) { buffer[b] = bytes[pos++]; } if (!le) { Array.Reverse(buffer); } uint LabelCount = BitConverter.ToUInt32(buffer, 0); // count of elements //logfile.Log("LabelCount= " + LabelCount); pos = head_FieldDataOffset; buffer = new byte[4]; for (b = 0; b != 4; ++b) { buffer[b] = bytes[pos++]; } if (!le) { Array.Reverse(buffer); } uint FieldDataOffset = BitConverter.ToUInt32(buffer, 0); //logfile.Log("FieldDataOffset= " + FieldDataOffset); pos = head_FieldDataLength; buffer = new byte[4]; for (b = 0; b != 4; ++b) { buffer[b] = bytes[pos++]; } if (!le) { Array.Reverse(buffer); } uint FieldDataCount = BitConverter.ToUInt32(buffer, 0); // count of bytes (not used.) //logfile.Log("FieldDataCount= " + FieldDataCount); pos = head_FieldIndicesOffset; buffer = new byte[4]; for (b = 0; b != 4; ++b) { buffer[b] = bytes[pos++]; } if (!le) { Array.Reverse(buffer); } uint FieldIndicesOffset = BitConverter.ToUInt32(buffer, 0); //logfile.Log("FieldIndicesOffset= " + FieldIndicesOffset); pos = head_FieldIndicesLength; buffer = new byte[4]; for (b = 0; b != 4; ++b) { buffer[b] = bytes[pos++]; } if (!le) { Array.Reverse(buffer); } uint FieldIndicesCount = BitConverter.ToUInt32(buffer, 0); // count of bytes //logfile.Log("FieldIndicesCount= " + FieldIndicesCount); pos = head_ListIndicesOffset; buffer = new byte[4]; for (b = 0; b != 4; ++b) { buffer[b] = bytes[pos++]; } if (!le) { Array.Reverse(buffer); } uint ListIndicesOffset = BitConverter.ToUInt32(buffer, 0); //logfile.Log("ListIndicesOffset= " + ListIndicesOffset); pos = head_ListIndicesLength; buffer = new byte[4]; for (b = 0; b != 4; ++b) { buffer[b] = bytes[pos++]; } if (!le) { Array.Reverse(buffer); } uint ListIndicesCount = BitConverter.ToUInt32(buffer, 0); // count of bytes (not used.) //logfile.Log("ListIndicesCount= " + ListIndicesCount); //logfile.Log(""); // FIELDID DATA -> contains the FieldIds of Structs that contain > 1 field (is req'd for parsing Structs) // - a list of DWORD ids into the Fields section //logfile.Log("FIELDIDS"); //int fid = 0; // log var fieldids = new List <uint>(); pos = FieldIndicesOffset; while (pos != FieldIndicesOffset + FieldIndicesCount) { //logfile.Log(". start= " + FieldIndicesOffset + " stop= " + (FieldIndicesOffset + FieldIndicesCount)); //logfile.Log(". fid #" + fid++); buffer = new byte[4]; // 4-byte DWORD - field id for (b = 0; b != 4; ++b) { //logfile.Log(". . pos= " + pos); buffer[b] = bytes[pos++]; } if (!le) { Array.Reverse(buffer); } fieldids.Add(BitConverter.ToUInt32(buffer, 0)); // WARNING: There is no safety on the count below. } // STRUCT DATA -> // - DWORD type-id // - DWORD dataordataoffset // - DWORD fieldcount uint fields, idoroffset, i, j; pos = StructOffset; for (i = 0; i != StructCount; ++i) { //logfile.Log("Struct #" + i); var st = new Struct(); buffer = new byte[4]; // type-id -> for (b = 0; b != 4; ++b) { buffer[b] = bytes[pos++]; } if (!le) { Array.Reverse(buffer); } st.typeid = BitConverter.ToUInt32(buffer, 0); //logfile.Log(". typid= " + st.typeid); buffer = new byte[4]; // dataordataoffset -> for (b = 0; b != 4; ++b) { buffer[b] = bytes[pos++]; } if (!le) { Array.Reverse(buffer); } idoroffset = BitConverter.ToUInt32(buffer, 0); // if fields=1 -> id into the FieldArray // if fields>1 -> offset into FieldIndices -> list of ids into the FieldArray buffer = new byte[4]; // fieldcount -> for (b = 0; b != 4; ++b) { buffer[b] = bytes[pos++]; } if (!le) { Array.Reverse(buffer); } fields = BitConverter.ToUInt32(buffer, 0); //logfile.Log(". fields= " + fields); st.fieldids = new List <uint>(); if (fields == 1) // get the FieldId directly w/ the Struct's 'idoroffset' id -> { //logfile.Log(". . idoroffset/id= " + idoroffset); //logfile.Log(". . data._fields.Count= " + data._fields.Count); st.fieldids.Add(idoroffset); } else if (fields > 1) // get the FieldIds out of the FieldIndices w/ the 'idoroffset' offset -> { uint fieldid; for (j = 0; j != fields; ++j) { //logfile.Log(". . [" + j + "] idoroffset/offset= " + idoroffset + " -> fieldids id= " + (idoroffset / 4 + j)); //logfile.Log(". . data._fieldids Length= " + (data._fieldids.Count * 4)); //logfile.Log(". . data._fields.Count= " + data._fields.Count); fieldid = fieldids[(int)(idoroffset / Globals.Length_DWORD + j)]; // 4 bytes in each DWORD (ie. convert offset to id id) //logfile.Log(". . fieldid= " + fieldid); st.fieldids.Add(fieldid); // isn't the GFF format wonderful ... at least it works } // the Bioware documentation could be better. } // Ps. it contains inaccurate and unspecific info Structs.Add(st); } // LABEL DATA -> contains Labels for the Fields (is req'd for parsing Fields) // - each label shall be unique across the entire GFF data // - 16-CHAR var labels = new List <string>(); string label; pos = LabelOffset; for (i = 0; i != LabelCount; ++i) { buffer = new byte[Globals.Length_LABEL]; // 16-byte CHAR(s) - label length for (b = 0; b != Globals.Length_LABEL; ++b) { buffer[b] = bytes[pos++]; } label = Encoding.ASCII.GetString(buffer, 0, buffer.Length).TrimEnd('\0'); labels.Add(label); } // FIELD DATA -> // - the doc contradicts itself by saying that the TopLevelStruct is the 1st // entry in the Fields section but that the Fields section does not contain // the TopLevelStruct ... the latter appears to be correct: the first Field in // a toolset-written FieldsArray is "Description" eg. // - DWORD datatype // - DWORD label-id // - DWORD dataordataoffset // - if BYTE,CHAR,WORD,SHORT,DWORD,INT,FLOAT -> dataordataoffset is a value. // - if DWORD64,INT64,DOUBLE,CExoString,CResRef,CExoLocString -> dataordataoffset // is an offset from the start of the FieldDataBlock section to complex data: // - DWORD64 8-bytes // - INT64 8-bytes // - DOUBLE 8-bytes // - CExoString DWORD (length) + chars // - CExoLocString DWORD (total length) + DWORD (strref) + DWORD (stringcount) + [INT (id) + INT (length) + chars] // - CResRef 1-byte (length) + chars (lowercase) // - VOID arbitrary // - if Struct -> dataordataoffset is an id into the Struct section. // - if List -> dataordataoffset is an offset from the start of the ListIndices // section to an array of DWORDs, the first of which is the count of DWORDS // that follow, which are ids into the Struct section. uint offset, length, count; pos = FieldOffset; for (i = 0; i != FieldCount; ++i) { var field = new GffData.Field(); buffer = new byte[4]; // 4-byte DWORD - field type for (b = 0; b != 4; ++b) { buffer[b] = bytes[pos++]; } if (!le) { Array.Reverse(buffer); } field.type = (FieldTypes)BitConverter.ToUInt32(buffer, 0); buffer = new byte[4]; // 4-byte DWORD - field label id for (b = 0; b != 4; ++b) { buffer[b] = bytes[pos++]; } if (!le) { Array.Reverse(buffer); } field.label = labels[(int)BitConverter.ToUInt32(buffer, 0)]; //logfile.Log("label= " + field.label); buffer = new byte[4]; // 4-byte DWORD - field data (val, is not a DWORD per se) or data for (b = 0; b != 4; ++b) // offset into (a) DataBlock or (b) ListIds or id into (c) Structs { buffer[b] = bytes[pos++]; } switch (field.type) { // WARNING: non-Complex types whose size is less than or // equal to 4-bytes are (according to the doc) contained // in the first byte(s) of the dataordataoffset 'DWORD'. case FieldTypes.BYTE: field.BYTE = buffer[0]; break; case FieldTypes.CHAR: { var a = (sbyte[])(object)new[] { buffer[0] }; field.CHAR = a[0]; break; } case FieldTypes.WORD: { var a = new byte[2]; if (le) { a[0] = buffer[0]; a[1] = buffer[1]; } else { a[0] = buffer[1]; a[1] = buffer[0]; } field.WORD = BitConverter.ToUInt16(a, 0); break; } case FieldTypes.SHORT: { var a = new byte[2]; if (le) { a[0] = buffer[0]; a[1] = buffer[1]; } else { a[0] = buffer[1]; a[1] = buffer[0]; } field.SHORT = BitConverter.ToInt16(a, 0); break; } case FieldTypes.DWORD: if (!le) { Array.Reverse(buffer); } field.DWORD = BitConverter.ToUInt32(buffer, 0); break; case FieldTypes.INT: if (!le) { Array.Reverse(buffer); } field.INT = BitConverter.ToInt32(buffer, 0); break; case FieldTypes.DWORD64: if (!le) { Array.Reverse(buffer); } offset = FieldDataOffset + BitConverter.ToUInt32(buffer, 0); buffer = new byte[8]; for (b = 0; b != 8; ++b) { buffer[b] = bytes[offset++]; } if (!le) { Array.Reverse(buffer); } field.DWORD64 = BitConverter.ToUInt64(buffer, 0); break; case FieldTypes.INT64: if (!le) { Array.Reverse(buffer); } offset = FieldDataOffset + BitConverter.ToUInt32(buffer, 0); buffer = new byte[8]; for (b = 0; b != 8; ++b) { buffer[b] = bytes[offset++]; } if (!le) { Array.Reverse(buffer); } field.INT64 = BitConverter.ToInt64(buffer, 0); break; case FieldTypes.FLOAT: if (!le) { Array.Reverse(buffer); } field.FLOAT = BitConverter.ToSingle(buffer, 0); break; case FieldTypes.DOUBLE: if (!le) { Array.Reverse(buffer); } offset = FieldDataOffset + BitConverter.ToUInt32(buffer, 0); buffer = new byte[8]; for (b = 0; b != 8; ++b) { buffer[b] = bytes[offset++]; } if (!le) { Array.Reverse(buffer); } field.DOUBLE = BitConverter.ToDouble(buffer, 0); break; case FieldTypes.CResRef: if (!le) { Array.Reverse(buffer); } offset = FieldDataOffset + BitConverter.ToUInt32(buffer, 0); length = bytes[offset]; // 1-byte size ++offset; buffer = new byte[(int)length]; for (b = 0; b != length; ++b) { buffer[b] = bytes[offset++]; } field.CResRef = Encoding.ASCII.GetString(buffer, 0, buffer.Length); break; case FieldTypes.CExoString: if (!le) { Array.Reverse(buffer); } offset = FieldDataOffset + BitConverter.ToUInt32(buffer, 0); buffer = new byte[4]; for (b = 0; b != 4; ++b) { buffer[b] = bytes[offset++]; } if (!le) { Array.Reverse(buffer); } length = BitConverter.ToUInt32(buffer, 0); // 4-byte size buffer = new byte[(int)length]; for (b = 0; b != length; ++b) { buffer[b] = bytes[offset++]; } field.CExoString = Encoding.UTF8.GetString(buffer, 0, buffer.Length); field.CExoString = field.CExoString.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", "\r\n"); break; case FieldTypes.CExoLocString: if (!le) { Array.Reverse(buffer); } offset = FieldDataOffset + BitConverter.ToUInt32(buffer, 0); buffer = new byte[4]; // total length (not incl/ these 4 bytes) -> for (b = 0; b != 4; ++b) // aka Pointless. just advance the offset val { buffer[b] = bytes[offset++]; } if (!le) { Array.Reverse(buffer); } length = BitConverter.ToUInt32(buffer, 0); // 4-byte size buffer = new byte[4]; // strref (-1 no strref) for (b = 0; b != 4; ++b) { buffer[b] = bytes[offset++]; } if (!le) { Array.Reverse(buffer); } field.CExoLocStrref = BitConverter.ToUInt32(buffer, 0); // 4-byte size buffer = new byte[4]; // substring count for (b = 0; b != 4; ++b) { buffer[b] = bytes[offset++]; } if (!le) { Array.Reverse(buffer); } count = BitConverter.ToUInt32(buffer, 0); // 4-byte size if (count != 0) { field.Locales = new List <GffData.Locale>(); //logfile.Log("label= " + field.label); for (j = 0; j != count; ++j) { var locale = new GffData.Locale(); buffer = new byte[4]; // langid for (b = 0; b != 4; ++b) { buffer[b] = bytes[offset++]; } if (!le) { Array.Reverse(buffer); } locale.SetLocaleLanguage(BitConverter.ToUInt32(buffer, 0)); buffer = new byte[4]; // stringlength for (b = 0; b != 4; ++b) { buffer[b] = bytes[offset++]; } if (!le) { Array.Reverse(buffer); } length = BitConverter.ToUInt32(buffer, 0); //logfile.Log("length= " + length); buffer = new byte[(int)length]; for (b = 0; b != length; ++b) { buffer[b] = bytes[offset++]; } locale.local = Encoding.UTF8.GetString(buffer, 0, buffer.Length); locale.local = locale.local.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", "\r\n"); //logfile.Log("local= " + locale.local); field.Locales.Add(locale); } } break; case FieldTypes.VOID: if (!le) { Array.Reverse(buffer); } offset = FieldDataOffset + BitConverter.ToUInt32(buffer, 0); buffer = new byte[4]; for (b = 0; b != 4; ++b) { buffer[b] = bytes[offset++]; } if (!le) { Array.Reverse(buffer); } length = BitConverter.ToUInt32(buffer, 0); field.VOID = new byte[(int)length]; for (j = 0; j != length; ++j) { field.VOID[j] = bytes[offset++]; } break; case FieldTypes.List: // a list-type Field is an offset into the FieldIndices; the later contains a list of StructIds. if (!le) { Array.Reverse(buffer); } offset = ListIndicesOffset + BitConverter.ToUInt32(buffer, 0); // offset into the (not)FieldIndices(not) -> try ListIndices buffer = new byte[4]; // 4-byte DWORD - count of structids for (b = 0; b != 4; ++b) { buffer[b] = bytes[offset++]; } if (!le) { Array.Reverse(buffer); } count = BitConverter.ToUInt32(buffer, 0); var list = new List <uint>(); for (j = 0; j != count; ++j) { buffer = new byte[4]; // 4-byte DWORD - structid for (b = 0; b != 4; ++b) { buffer[b] = bytes[offset++]; } if (!le) { Array.Reverse(buffer); } list.Add(BitConverter.ToUInt32(buffer, 0)); } field.List = list; break; case FieldTypes.Struct: if (!le) { Array.Reverse(buffer); } field.Struct = Structs[(int)BitConverter.ToUInt32(buffer, 0)]; // NOTE: That is an id into the Structs not an offset. break; } Fields.Add(field); } return(data); } FileService.error("That file has no data."); } return(null); }