diff --git a/CodeWalker.csproj b/CodeWalker.csproj
index 9b7e7d6..24e9ea8 100644
--- a/CodeWalker.csproj
+++ b/CodeWalker.csproj
@@ -274,6 +274,7 @@
+
diff --git a/GameFiles/MetaTypes/XmlMeta.cs b/GameFiles/MetaTypes/XmlMeta.cs
new file mode 100644
index 0000000..7c49ed1
--- /dev/null
+++ b/GameFiles/MetaTypes/XmlMeta.cs
@@ -0,0 +1,665 @@
+using System;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using System.Xml;
+using SharpDX;
+
+namespace CodeWalker.GameFiles
+{
+ class XmlMeta
+ {
+ public static Meta GetMeta(XmlDocument doc)
+ {
+ MetaBuilder mb = new MetaBuilder();
+
+ Traverse(doc.DocumentElement, mb, 0, true);
+
+ var meta = mb.GetMeta();
+
+ return meta;
+ }
+
+ private static byte[] Traverse(XmlNode node, MetaBuilder mb, MetaName type = 0, bool isRoot = false)
+ {
+ if(type == 0)
+ {
+ type = (MetaName)(uint)GetHash(node.Name);
+ }
+
+ var infos = MetaTypes.GetStructureInfo(type);
+
+ if (infos != null)
+ {
+ byte[] data = new byte[infos.StructureSize];
+ var arrayResults = new ArrayResults();
+
+ arrayResults.Structures = new Dictionary();
+ arrayResults.StructurePointers = new Dictionary();
+ arrayResults.UInts = new Dictionary();
+ arrayResults.UShorts = new Dictionary();
+ arrayResults.UBytes = new Dictionary();
+ arrayResults.Floats = new Dictionary();
+ arrayResults.Float_XYZs = new Dictionary();
+ arrayResults.Hashes = new Dictionary();
+
+ Array.Clear(data, 0, infos.StructureSize);
+
+ MetaStructureEntryInfo_s arrEntry = new MetaStructureEntryInfo_s();
+
+ if (isRoot)
+ {
+ mb.EnsureBlock(type);
+ }
+
+ for (int i = 0; i < infos.Entries.Length; i++)
+ {
+ var entry = infos.Entries[i];
+
+ var cnode = GetEntryNode(node.ChildNodes, entry);
+
+ if (entry.EntryNameHash == MetaName.ARRAYINFO)
+ {
+ arrEntry = entry;
+ continue;
+ }
+
+ if (cnode == null)
+ {
+ continue;
+ }
+
+ switch (entry.DataType)
+ {
+ case MetaStructureEntryDataType.Array:
+ {
+ TraverseArray(cnode, mb, arrEntry, entry.DataOffset, arrayResults, data);
+ break;
+ }
+
+ case MetaStructureEntryDataType.ArrayOfBytes:
+ case MetaStructureEntryDataType.ArrayOfChars:
+ {
+ int offset = entry.DataOffset;
+ var split = Split(cnode.InnerText, 2);
+
+ for (int j = 0; j < split.Length; j++)
+ {
+ byte val = Convert.ToByte(split[j], 16);
+ data[offset] = val;
+ offset += sizeof(byte);
+ }
+
+ break;
+ }
+
+ case MetaStructureEntryDataType.Boolean:
+ {
+ byte val = (cnode.Attributes["value"].Value == "false") ? (byte)0 : (byte)1;
+ data[entry.DataOffset] = val;
+ break;
+ }
+
+ case MetaStructureEntryDataType.ByteEnum:
+ {
+ byte val = Convert.ToByte(cnode.Attributes["value"].Value);
+ data[entry.DataOffset] = val;
+ break;
+ }
+
+
+ case MetaStructureEntryDataType.CharPointer:
+ {
+ if (!string.IsNullOrEmpty(cnode.InnerText))
+ {
+ var ptr = mb.AddStringPtr(cnode.InnerText);
+ var val = MetaTypes.ConvertToBytes(ptr);
+
+ Buffer.BlockCopy(val, 0, data, entry.DataOffset, val.Length);
+ }
+
+ break;
+ }
+
+ case MetaStructureEntryDataType.DataBlockPointer:
+ {
+ // TODO
+ break;
+ }
+
+ case MetaStructureEntryDataType.Float:
+ {
+ float val = FloatUtil.Parse(cnode.Attributes["value"].Value);
+ Write(val, data, entry.DataOffset);
+ break;
+ }
+
+ case MetaStructureEntryDataType.Float_XYZ:
+ {
+ float x = FloatUtil.Parse(cnode.Attributes["x"].Value);
+ float y = FloatUtil.Parse(cnode.Attributes["y"].Value);
+ float z = FloatUtil.Parse(cnode.Attributes["z"].Value);
+
+ Write(x, data, entry.DataOffset);
+ Write(y, data, entry.DataOffset + sizeof(float));
+ Write(z, data, entry.DataOffset + sizeof(float) * 2);
+
+ break;
+ }
+
+
+ case MetaStructureEntryDataType.Float_XYZW:
+ {
+ float x = FloatUtil.Parse(cnode.Attributes["x"].Value);
+ float y = FloatUtil.Parse(cnode.Attributes["y"].Value);
+ float z = FloatUtil.Parse(cnode.Attributes["z"].Value);
+ float w = FloatUtil.Parse(cnode.Attributes["w"].Value);
+
+ Write(x, data, entry.DataOffset);
+ Write(y, data, entry.DataOffset + sizeof(float));
+ Write(z, data, entry.DataOffset + sizeof(float) * 2);
+ Write(w, data, entry.DataOffset + sizeof(float) * 3);
+
+ break;
+ }
+
+ case MetaStructureEntryDataType.Hash:
+ {
+ var hash = GetHash(cnode.InnerText);
+ Write(hash, data, entry.DataOffset);
+ break;
+ }
+
+ case MetaStructureEntryDataType.IntEnum:
+ case MetaStructureEntryDataType.IntFlags1:
+ case MetaStructureEntryDataType.IntFlags2:
+ {
+ var _infos = MetaTypes.GetEnumInfo(entry.ReferenceKey);
+
+ mb.AddEnumInfo(_infos.EnumNameHash);
+
+ int val = GetEnumInt(entry.ReferenceKey, cnode.InnerText);
+ Write(val, data, entry.DataOffset);
+ break;
+ }
+
+ case MetaStructureEntryDataType.ShortFlags:
+ {
+ var _infos = MetaTypes.GetEnumInfo(entry.ReferenceKey);
+
+ mb.AddEnumInfo(_infos.EnumNameHash);
+
+ int val = GetEnumInt(entry.ReferenceKey, cnode.InnerText);
+ Write((short)val, data, entry.DataOffset);
+ break;
+ }
+
+ case MetaStructureEntryDataType.SignedByte:
+ {
+ var val = Convert.ToSByte(cnode.Attributes["value"].Value);
+ data[entry.DataOffset] = (byte)val;
+ break;
+ }
+
+ case MetaStructureEntryDataType.SignedInt:
+ {
+ var val = Convert.ToInt32(cnode.Attributes["value"].Value);
+ Write(val, data, entry.DataOffset);
+ break;
+ }
+
+ case MetaStructureEntryDataType.SignedShort:
+ {
+ var val = Convert.ToInt16(cnode.Attributes["value"].Value);
+ Write(val, data, entry.DataOffset);
+ break;
+ }
+
+ case MetaStructureEntryDataType.Structure:
+ {
+ var struc = Traverse(cnode, mb, entry.ReferenceKey);
+
+ if(struc != null)
+ {
+ Buffer.BlockCopy(struc, 0, data, entry.DataOffset, struc.Length);
+ }
+
+ break;
+ }
+
+ case MetaStructureEntryDataType.StructurePointer:
+ {
+ // TODO
+ break;
+ }
+
+ case MetaStructureEntryDataType.UnsignedByte:
+ {
+ var val = Convert.ToByte(cnode.Attributes["value"].Value);
+ data[entry.DataOffset] = val;
+ break;
+ }
+
+ case MetaStructureEntryDataType.UnsignedInt:
+ {
+ switch (entry.EntryNameHash)
+ {
+ case MetaName.color:
+ {
+ var val = Convert.ToUInt32(cnode.Attributes["value"].Value, 16);
+ Write(val, data, entry.DataOffset);
+ break;
+ }
+
+ default:
+ {
+ var val = Convert.ToUInt32(cnode.Attributes["value"].Value);
+ Write(val, data, entry.DataOffset);
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case MetaStructureEntryDataType.UnsignedShort:
+ {
+ var val = Convert.ToUInt16(cnode.Attributes["value"].Value);
+ Write(val, data, entry.DataOffset);
+ break;
+ }
+
+ default: break;
+
+ }
+ }
+
+ arrayResults.WriteArrays(data, mb, type);
+
+ mb.AddStructureInfo(infos.StructureNameHash);
+
+ if (isRoot)
+ {
+ mb.AddItem(type, data);
+ }
+
+ return data;
+ }
+
+ return null;
+ }
+
+ private static void TraverseArray(XmlNode node, MetaBuilder mb, MetaStructureEntryInfo_s arrEntry, int offset, ArrayResults results, byte[] data)
+ {
+ switch (arrEntry.DataType)
+ {
+ case MetaStructureEntryDataType.Structure:
+ {
+ results.Structures[offset] = TraverseArrayStructure(node, mb, arrEntry.ReferenceKey);
+ break;
+ }
+
+ case MetaStructureEntryDataType.StructurePointer:
+ {
+ results.StructurePointers[offset] = TraverseArrayStructurePointer(node, mb);
+ break;
+ }
+
+ case MetaStructureEntryDataType.UnsignedInt:
+ {
+ results.UInts[offset] = TraverseRawUIntArray(node, mb);
+ break;
+ }
+ case MetaStructureEntryDataType.UnsignedShort:
+ {
+ results.UShorts[offset] = TraverseRawUShortArray(node, mb);
+ break;
+ }
+ case MetaStructureEntryDataType.UnsignedByte:
+ {
+ results.UBytes[offset] = TraverseRawUByteArray(node, mb);
+ break;
+ }
+ case MetaStructureEntryDataType.Float:
+ {
+ results.Floats[offset] = TraverseRawFloatArray(node, mb);
+ break;
+ }
+ case MetaStructureEntryDataType.Float_XYZ:
+ {
+ results.Float_XYZs[offset] = TraverseRawVector3Array(node, mb);
+ break;
+ }
+ case MetaStructureEntryDataType.Hash:
+ {
+ results.Hashes[offset] = TraverseHashArray(node, mb);
+ break;
+ }
+ case MetaStructureEntryDataType.CharPointer:
+ {
+ // TODO
+ break;
+ }
+ case MetaStructureEntryDataType.DataBlockPointer:
+ {
+ // TODO
+ break;
+ }
+
+ default: break;
+ }
+
+ }
+
+ private static Array_Structure TraverseArrayStructure(XmlNode node, MetaBuilder mb, MetaName type)
+ {
+ var strucs = new List();
+
+ foreach (XmlNode cnode in node.ChildNodes)
+ {
+ var struc = Traverse(cnode, mb, type);
+
+ if (struc != null)
+ {
+ strucs.Add(struc);
+ }
+ }
+
+ return mb.AddItemArrayPtr(type, strucs.ToArray());
+ }
+
+ private static Array_StructurePointer TraverseArrayStructurePointer(XmlNode node, MetaBuilder mb)
+ {
+ var ptrs = new List();
+
+ foreach (XmlNode cnode in node.ChildNodes)
+ {
+ var type = (MetaName)(uint)GetHash(cnode.Attributes["type"].Value);
+ var struc = Traverse(cnode, mb, type);
+
+ if(struc != null)
+ {
+ var ptr = mb.AddItemPtr(type, struc);
+ ptrs.Add(ptr);
+ }
+
+ }
+
+ return mb.AddPointerArray(ptrs.ToArray());
+
+ }
+
+ private static Array_uint TraverseRawUIntArray(XmlNode node, MetaBuilder mb)
+ {
+ var data = new List();
+
+ if (node.InnerText != "")
+ {
+ var split = Regex.Split(node.InnerText, @"[\s\r\n\t]");
+
+ for (int i = 0; i < split.Length; i++)
+ {
+ if(!string.IsNullOrEmpty(split[i]))
+ {
+ var val = Convert.ToUInt32(split[i]);
+ data.Add(val);
+ }
+
+ }
+ }
+
+ return mb.AddUintArrayPtr(data.ToArray());
+ }
+
+ private static Array_ushort TraverseRawUShortArray(XmlNode node, MetaBuilder mb)
+ {
+ var data = new List();
+
+ if (node.InnerText != "")
+ {
+ var split = Regex.Split(node.InnerText, @"[\s\r\n\t]");
+
+ for (int i = 0; i < split.Length; i++)
+ {
+ if (!string.IsNullOrEmpty(split[i]))
+ {
+ var val = Convert.ToUInt16(split[i]);
+ data.Add(val);
+ }
+ }
+ }
+
+ return mb.AddUshortArrayPtr(data.ToArray());
+ }
+
+ private static Array_byte TraverseRawUByteArray(XmlNode node, MetaBuilder mb)
+ {
+ var data = new List();
+
+ if (node.InnerText != "")
+ {
+ var split = Regex.Split(node.InnerText, @"[\s\r\n\t]");
+
+ for (int i = 0; i < split.Length; i++)
+ {
+ if (!string.IsNullOrEmpty(split[i]))
+ {
+ var val = Convert.ToByte(split[i]);
+ data.Add(val);
+ }
+ }
+ }
+
+ return mb.AddByteArrayPtr(data.ToArray());
+ }
+
+ private static Array_float TraverseRawFloatArray(XmlNode node, MetaBuilder mb)
+ {
+ var data = new List();
+
+ if(node.InnerText != "")
+ {
+ var split = Regex.Split(node.InnerText, @"[\s\r\n\t]");
+
+ for (int i = 0; i < split.Length; i++)
+ {
+ if (!string.IsNullOrEmpty(split[i]))
+ {
+ var val = Convert.ToSingle(split[i]);
+ data.Add(val);
+ }
+ }
+ }
+
+ return mb.AddFloatArrayPtr(data.ToArray());
+ }
+
+ private static Array_Vector3 TraverseRawVector3Array(XmlNode node, MetaBuilder mb)
+ {
+ var items = new List();
+
+ foreach (XmlNode cnode in node.ChildNodes)
+ {
+
+ var split = Regex.Split(node.InnerText, @",\s");
+
+ float x = FloatUtil.Parse(split[0]);
+ float y = FloatUtil.Parse(split[1]);
+ float z = FloatUtil.Parse(split[2]);
+ float w = 0f;
+
+ var val = new Vector4(x, y, z, w);
+
+ items.Add(val);
+ break;
+ }
+
+ return mb.AddPaddedVector3ArrayPtr(items.ToArray());
+ }
+
+ private static Array_uint TraverseHashArray(XmlNode node, MetaBuilder mb)
+ {
+ var items = new List();
+
+ foreach (XmlNode cnode in node.ChildNodes)
+ {
+ var val = GetHash(cnode.InnerText);
+ items.Add(val);
+ }
+
+ return mb.AddHashArrayPtr(items.ToArray());
+ }
+
+ private static void Write(int val, byte[] data, int offset)
+ {
+ byte[] bytes = BitConverter.GetBytes(val);
+ Buffer.BlockCopy(bytes, 0, data, offset, sizeof(int));
+ }
+
+ private static void Write(uint val, byte[] data, int offset)
+ {
+ byte[] bytes = BitConverter.GetBytes(val);
+ Buffer.BlockCopy(bytes, 0, data, offset, sizeof(uint));
+ }
+
+ private static void Write(short val, byte[] data, int offset)
+ {
+ byte[] bytes = BitConverter.GetBytes(val);
+ Buffer.BlockCopy(bytes, 0, data, offset, sizeof(short));
+ }
+
+ private static void Write(ushort val, byte[] data, int offset)
+ {
+ byte[] bytes = BitConverter.GetBytes(val);
+ Buffer.BlockCopy(bytes, 0, data, offset, sizeof(ushort));
+ }
+
+ private static void Write(float val, byte[] data, int offset)
+ {
+ byte[] bytes = BitConverter.GetBytes(val);
+ Buffer.BlockCopy(bytes, 0, data, offset, sizeof(float));
+ }
+
+ private static MetaHash GetHash(string str)
+ {
+ if (str.StartsWith("hash_"))
+ {
+ return (MetaHash) Convert.ToUInt32(str.Substring(5), 16);
+ }
+ else
+ {
+ return JenkHash.GenHash(str);
+ }
+ }
+
+ private static XmlNode GetEntryNode(XmlNodeList nodes, MetaStructureEntryInfo_s entry)
+ {
+ foreach (XmlNode node in nodes)
+ {
+ if (GetHash(node.Name) == (uint)entry.EntryNameHash)
+ {
+ return node;
+ }
+ }
+
+ return null;
+ }
+
+ private static string[] Split(string str, int maxChunkSize)
+ {
+ var chunks = new List();
+
+ for (int i = 0; i < str.Length; i += maxChunkSize)
+ {
+ chunks.Add(str.Substring(i, Math.Min(maxChunkSize, str.Length - i)));
+ }
+
+ return chunks.ToArray();
+ }
+
+ private static int GetEnumInt(MetaName type, string enumString)
+ {
+ var enumName = (MetaName)(uint)GetHash(enumString);
+ var infos = MetaTypes.GetEnumInfo(type);
+
+ if (infos == null)
+ {
+ return 0;
+ }
+
+ for (int j = 0; j < infos.Entries.Length; j++)
+ {
+ var entry = infos.Entries[j];
+
+ if (entry.EntryNameHash == enumName)
+ {
+ return entry.EntryValue;
+ }
+ }
+
+ return 0;
+ }
+ }
+
+ struct ArrayResults
+ {
+ public Dictionary Structures;
+ public Dictionary StructurePointers;
+ public Dictionary UInts;
+ public Dictionary UShorts;
+ public Dictionary UBytes;
+ public Dictionary Floats;
+ public Dictionary Float_XYZs;
+ public Dictionary Hashes;
+
+ public void WriteArrays(byte[] data, MetaBuilder mb, MetaName type)
+ {
+ foreach (KeyValuePair ptr in Structures)
+ {
+ var _data = MetaTypes.ConvertToBytes(ptr.Value);
+ Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
+ }
+
+ foreach (KeyValuePair ptr in StructurePointers)
+ {
+ var _data = MetaTypes.ConvertToBytes(ptr.Value);
+ Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
+ }
+
+ foreach (KeyValuePair ptr in UInts)
+ {
+ var _data = MetaTypes.ConvertToBytes(ptr.Value);
+ Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
+ }
+
+ foreach (KeyValuePair ptr in UShorts)
+ {
+ var _data = MetaTypes.ConvertToBytes(ptr.Value);
+ Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
+ }
+
+ foreach (KeyValuePair ptr in UBytes)
+ {
+ var _data = MetaTypes.ConvertToBytes(ptr.Value);
+ Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
+ }
+
+ foreach (KeyValuePair ptr in Floats)
+ {
+ var _data = MetaTypes.ConvertToBytes(ptr.Value);
+ Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
+ }
+
+ foreach (KeyValuePair ptr in Float_XYZs)
+ {
+ var _data = MetaTypes.ConvertToBytes(ptr.Value);
+ Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
+ }
+
+ foreach (KeyValuePair ptr in Hashes)
+ {
+ var _data = MetaTypes.ConvertToBytes(ptr.Value);
+ Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
+ }
+ }
+ }
+}