/////////////////////////////////////////////////////////////////////// int ScanChild(BinaryReader br, int max_len, ref CTypeChild root) { int cur_len = 0; string name; using (StringStore tmp = new StringStore().Read(br)) { name = tmp.Value; cur_len += tmp.ReadSize; } CType last_node = new CType(); if (cur_len < max_len) { Types type = (Types)br.ReadByte(); cur_len += 1; switch (type) { case Types.Empty: { CTypeEmpty me = new CTypeEmpty(name); last_node = me; break; } case Types.Enum: { string val; using (StringStore tmp = new StringStore().Read(br)) { val = tmp.Value; cur_len += tmp.ReadSize; } int ival; using (NumberStore tmp = new NumberStore().Read(br)) { ival = tmp.Value; cur_len += sizeof(int); } CTypeEnum me = new CTypeEnum(name); me.SValue = val; me.IValue = ival; last_node = me; break; } case Types.String: { string val; using (StringStore tmp = new StringStore().Read(br)) { val = tmp.Value; cur_len += tmp.ReadSize; } CTypeString me = new CTypeString(name); me.SValue = val; last_node = me; break; } case Types.Number: { CTypeNumber me = new CTypeNumber(name); // required. some number nodes end before they have any data. yup. if (cur_len != max_len) { int ival; using (NumberStore tmp = new NumberStore().Read(br)) { ival = tmp.Value; cur_len += sizeof(int); } me.HasValue = true; me.IValue = ival; } last_node = me; break; } case Types.StringList: { CTypeStringList me = new CTypeStringList(name); string val; using (StringStore tmp = new StringStore().Read(br)) { val = tmp.Value; me.SValues.Add(val); cur_len += tmp.ReadSize; } using (StringStore tmp = new StringStore().Read(br)) { val = tmp.Value; me.SValues.Add(val); cur_len += tmp.ReadSize; } // are there any more? last_node = me; break; } case Types.List: { byte Count = br.ReadByte(); cur_len += sizeof(byte); CTypeChild me = new CTypeChild(name); if (Count > 0) { // multiple child nodes have a list of lengths if (Count > 1) { int real_count = Count - 1; var items = new List <int>(); for (int i = 0; i < real_count; ++i) { items.Add(br.ReadInt32()); } cur_len += real_count * sizeof(int); int last = 0; for (int i = 0; i < real_count; ++i) { int child_len = ScanChild(br, items[i] - last, ref me); cur_len += child_len; last = items[i]; } } int ret_len = ScanChild(br, max_len - cur_len, ref me); cur_len += ret_len; } last_node = me; break; } default: { throw new Exception("Unknown node type. Please report this file"); } } } // oh, there is also random tail data // we store this as 'tail' data, but in xml this is 'metadata' int tail_length = max_len - cur_len; if (tail_length != 0) { if ((tail_length & 3) != 0) { throw new Exception("Unexpected tail"); } int tail_count = tail_length / sizeof(int); for (int i = 0; i < tail_count; ++i) { int ival = br.ReadInt32(); // swap the endian if (runtimeOptions.ExportForConsole) { EndianHelper.Swap(ref ival); } last_node.Tail.Add(ival); } cur_len = max_len; } root.Children.Add(last_node); return(cur_len); }
/////////////////////////////////////////////////////////////////////// public bool ReadXml(string file_name) { bool valid = true; if (rootChild != null) { // trash the existing loaded data rootChild = null; } valid &= File.Exists(file_name); if (valid) { StreamReader src = new StreamReader(file_name); XmlReader xml = XmlReader.Create(src); // we need to track the parent node stack as child nodes only track their own children var child_stack = new Stack <CTypeChild>(); // dummy root rootChild = new CTypeChild("_root"); child_stack.Push(rootChild); // abtract node so we can determine the true node type at a later stage CType last_node = null; // dummy count for valid subnodes int expected_subnodes = 0; bool parsing_subnode = false; while (xml.Read()) { // good info on msdn about this - https://msdn.microsoft.com/en-us/library/cc189056%28v=vs.95%29.aspx switch (xml.NodeType) { case XmlNodeType.Element: if (xml.Name == "dummy") { // should only be the first node continue; } if (xml.Name == "subnode") { if (!parsing_subnode) { throw new Exception("subnode read error"); } expected_subnodes--; continue; } if (xml.Name != "node") { throw new Exception("Horribly wrong!"); } // deal with unhandled last node. as we've started a new element, it has children now if (last_node != null) { if (last_node.CoreType != CInternalType.List) { // should only throw when a node didn't end with an EndElement // which suggests we failed parsing midway through throw new Exception("Parsing elements failed"); } // append this new node to the last one child_stack.First().Children.Add(last_node); // and begin anew child_stack.Push(last_node as CTypeChild); // dealt with. last_node = null; } // we store this as an attribute because some names have spaces, which breaks xml node names string name = xml.GetAttribute("name"); if (name == null) { throw new Exception("Wrong data (no name attribute)"); } string type = xml.GetAttribute("type"); if (type == null) { throw new Exception("Wrong data (no type attribute)"); } switch (type) { case "Empty": { last_node = new CTypeEmpty(name); } break; case "Enum": { var tmp = new CTypeEnum(name); string enum_val = xml.GetAttribute("enum"); if (enum_val == null) { throw new Exception("Bad enum"); } tmp.IValue = Convert.ToInt32(enum_val); last_node = tmp; } break; case "List": { last_node = new CTypeChild(name); } break; case "Number": { var tmp = new CTypeNumber(name); string int_val = xml.GetAttribute("value"); if (int_val == null) { throw new Exception("Bad number"); } if (int_val != "") // determines if there is a value or not (there may not be..) { tmp.IValue = Convert.ToInt32(int_val); tmp.HasValue = true; } last_node = tmp; } break; case "String": { last_node = new CTypeString(name); // no actual string until we parse that part of the xml } break; case "StringList": { var tmp = new CTypeStringList(name); string count_val = xml.GetAttribute("count"); if (count_val != null) { expected_subnodes = Convert.ToInt32(count_val); if (expected_subnodes > 0) { // mark as parsing subnodes parsing_subnode = true; } } last_node = tmp; } break; default: throw new Exception("Unhandled node type"); } // get metadata and copy to tail if it exists string metadata = xml.GetAttribute("metadata"); if (metadata != null) // check we have any tail data { // whoops, silly bug where new char[','] allocated however many characters the comma ascii is! string[] _metadata = metadata.Split(new char[] { ',' }); foreach (string str in _metadata) { int ival = Convert.ToInt32(str); // swap the endian if (runtimeOptions.ExportForConsole) { EndianHelper.Swap(ref ival); } last_node.Tail.Add(ival); } } break; case XmlNodeType.Text: string string_val = xml.Value; switch (last_node.CoreType) { case CInternalType.Enum: { // just assign the string value to ctypenum var last = last_node as CTypeEnum; last.SValue = string_val; } break; case CInternalType.String: { // just assign the string to the ctypestring var last = last_node as CTypeString; last.SValue = string_val; } break; case CInternalType.StringList: { if (!parsing_subnode) { throw new Exception("subnode string error"); } // add the string value to the ctypestringlist var last = last_node as CTypeStringList; last.SValues.Add(string_val); } break; default: { throw new Exception("Unknown CoreType with string data"); } } break; case XmlNodeType.EndElement: // all elements must end, but last_node may not always be an item // must be a child which has just ended?? if (last_node == null) { // subnodes have finished if (parsing_subnode) { parsing_subnode = false; } else { // parent node ends child_stack.Pop(); } } else { if (last_node.CoreType == CInternalType.StringList && parsing_subnode) { if (expected_subnodes > 0) { // we expect more subnodes, don't null last_node continue; } } // add this as a child of the last (well, first on the stack) parent child_stack.Peek().Children.Add(last_node); last_node = null; } break; } } #if DEBUG if (last_node != null) { throw new Exception("Something went very wrong!"); } #endif src.Close(); } return(valid); }