using CXML;
using General;
using Ionic.Zlib;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;

namespace CXMLDecompiler
{
    class CXMLBuilder
    {
        String MainDir = "";
        String XMLFilename = "";
        String MagicNumber = "";
        Int32 Version;
        FileStream InfoFile;

        MemoryStream TreeTable;
        MemoryStream StringIDTable;
        MemoryStream IntIDTable;
        MemoryStream StringTable;
        MemoryStream CharTable;
        MemoryStream HashIDTable;
        MemoryStream IntArrayTable;
        MemoryStream FloatArrayTable;
        MemoryStream FileTable;

        Boolean IsInitalized = false;
        public Boolean HashStrings = false;

        BinaryWriter bInfoFile;
        BinaryWriter bTreeTable;
        BinaryWriter bIntIDTable;
        BinaryWriter bFloatArrayTable;
        BinaryWriter bIntArrayTable;
        BinaryWriter bHashIDTable;
        BinaryWriter bStringIDTable;
        String SilicaTypingInformation = "";
        public void Init(string XMLFile, string CxmlFile)
        {
            GetSilicaTypingInformation(XMLFile);
            MagicNumber = GetTypingInformation("MAGIC");
            Version = Int32.Parse(GetTypingInformation("VERSION"), CultureInfo.InvariantCulture);

            InfoFile = File.Open(CxmlFile, FileMode.OpenOrCreate, FileAccess.ReadWrite);
            InfoFile.SetLength(0);

            TreeTable = new MemoryStream();
            StringIDTable = new MemoryStream();
            IntIDTable = new MemoryStream();
            StringTable = new MemoryStream();
            CharTable = new MemoryStream();
            HashIDTable = new MemoryStream();
            IntArrayTable = new MemoryStream();
            FloatArrayTable = new MemoryStream();
            FileTable = new MemoryStream();

            bInfoFile = new BinaryWriter(InfoFile);
            bTreeTable = new BinaryWriter(TreeTable);
            bIntIDTable = new BinaryWriter(IntIDTable);
            bFloatArrayTable = new BinaryWriter(FloatArrayTable);
            bIntArrayTable = new BinaryWriter(IntArrayTable);
            bHashIDTable = new BinaryWriter(HashIDTable);
            bStringIDTable = new BinaryWriter(StringIDTable);
            
            XMLFilename = XMLFile;
            MainDir = Path.GetDirectoryName(XMLFilename);

            IsInitalized = true;

            return;
        }
        public void GetSilicaTypingInformation(string XmlFileName)
        {
            if (XmlFileName.Contains("<!--SilicaTypingInformation{{[[")){
                Console.WriteLine("XML Does not contain SilicaTypingInformation\nABORTING!");
                Environment.Exit(0);
            }

            string SearchFor = "<!--SilicaTypingInformation{{[[";
            string SearchFor2 = "]]}}SilicaTypingInformation-->";
            string xmlData = File.ReadAllText(XmlFileName);
            int ind1 = xmlData.IndexOf(SearchFor);
            xmlData = xmlData.Substring(ind1 + SearchFor.Length);
            int ind2 = xmlData.IndexOf(SearchFor2);
            xmlData = xmlData.Substring(0, ind2);
            SilicaTypingInformation = xmlData;
        }
        public string GetTypingInformation(string Key)
        {
            string[] typeinf = SilicaTypingInformation.Split(',');
            foreach(string type in typeinf)
            {
                string[] keyValuePair = type.Split('=');
                if (keyValuePair[0] == Key)
                    return keyValuePair[1];
            }
            return "0";
        }
        public class StringTableEntry
        {
            public int offset;
            public string name;
        }
        public class IntTableEntry
        {
            public int offset;
            public int value;
        }
        public class StringIDTableEntry
        {
            public int offset;
            public string value;
            public int loopbackOffset;
        }
        public class IntIDTableEntry
        {
            public int offset;
            public int value;
            public int loopbackOffset;
        }

        public class TreeTableEntry
        {
            public int offset;
            public XmlNode node;
        }
        List<TreeTableEntry> TreeTableEntries = new List<TreeTableEntry>();
        List<StringTableEntry> StringTableEntries = new List<StringTableEntry>();
        List<StringTableEntry> CharTableEntries = new List<StringTableEntry>();
        List<IntTableEntry> HashIdTableEntries = new List<IntTableEntry>();
        List<Int32> IntArrayTableEntries = new List<Int32>();
        List<Single> FloatArrayTableEntries = new List<Single>();
        List<StringTableEntry> FileTableEntries = new List<StringTableEntry>();
        List<StringIDTableEntry> StringIDTableEntries = new List<StringIDTableEntry>();
        List<IntIDTableEntry> IntIDTableEntries = new List<IntIDTableEntry>();
        public int AddGetTreeTable(XmlNode node)
        {
            if (!(node.NodeType == XmlNodeType.Element || node.NodeType == XmlNodeType.EndElement || node.NodeType == XmlNodeType.Attribute))
                return -1;
            foreach (TreeTableEntry entry in TreeTableEntries)
                if (entry.node == node)
                    return entry.offset;

            TreeTableEntry ent = new TreeTableEntry();
            ent.node = node;
            ent.offset = Convert.ToInt32(TreeTable.Position);
            TreeTableEntries.Add(ent);
            WriteElement(node);

            return ent.offset;
        }

        public int AddGetIntIdTable(int value, int loopbackPtr)
        {
            foreach (IntIDTableEntry entry in IntIDTableEntries)
            {

                if (entry.loopbackOffset == loopbackPtr && loopbackPtr != -1)
                {
                    return entry.offset;
                }
                else if (entry.value == value)
                {
                    if (loopbackPtr == -1)
                        return entry.offset;
                    entry.loopbackOffset = loopbackPtr;
                    long position1 = IntIDTable.Position;
                    IntIDTable.Seek(entry.offset, SeekOrigin.Begin);
                    bIntIDTable.Write(loopbackPtr);
                    IntIDTable.Seek(position1, SeekOrigin.Begin);
                    return entry.offset;
                }
            }
            IntIDTableEntry ent = new IntIDTableEntry();
            ent.value = value;
            ent.loopbackOffset = loopbackPtr;
            ent.offset = Convert.ToInt32(IntIDTable.Position);
            IntIDTableEntries.Add(ent);

            bIntIDTable.Write((int)loopbackPtr);
            bIntIDTable.Write((int)value);

            return ent.offset;
        }
        public int AddGetStringIdTable(string value, int loopbackPtr)
        {
            foreach (StringIDTableEntry entry in StringIDTableEntries)
            {
                if (entry.loopbackOffset == loopbackPtr && loopbackPtr != -1)
                {
                    return entry.offset;
                }
                else if (entry.value == value)
                {
                    if (loopbackPtr == -1)
                        return entry.offset;
                    entry.loopbackOffset = loopbackPtr;
                    long position1 = StringIDTable.Position;
                    StringIDTable.Seek(entry.offset, SeekOrigin.Begin);
                    bStringIDTable.Write(loopbackPtr);
                    StringIDTable.Seek(position1, SeekOrigin.Begin);
                    return entry.offset;
                }
            }
            StringIDTableEntry ent = new StringIDTableEntry();
            ent.value = value;
            ent.loopbackOffset = loopbackPtr;
            ent.offset = Convert.ToInt32(StringIDTable.Position);
            StringIDTableEntries.Add(ent);
            
            bStringIDTable.Write(loopbackPtr);
            Tools.WriteStringToStream(StringIDTable, value);
            StringIDTable.WriteByte(0x00);


            if (loopbackPtr != -1)
            {
                Align(StringIDTable, 4);
            }

            return ent.offset;
        }
        public int AddGetFileTable(byte[] value, string filename)
        {
            foreach (StringTableEntry entry in FileTableEntries)
                if (entry.name == filename)
                    return entry.offset;

            StringTableEntry ent = new StringTableEntry();
            ent.name = filename;
            ent.offset = Convert.ToInt32(FileTable.Position);
            FileTableEntries.Add(ent);

            FileTable.Write(value, 0x00, value.Length);
            Align(FileTable, 0x10);

            return ent.offset;
        }
        public int AddGetFloatArrayTable(float[] value)
        {
            float[] Test = new float[value.Length];
            float[] TestAgainst = FloatArrayTableEntries.ToArray();
            for (int i = 0; i < TestAgainst.Length; i += 1)
            {
                if (i + value.Length > TestAgainst.Length)
                    break;
                Array.ConstrainedCopy(TestAgainst, i, Test, 0, value.Length);
                if(value.SequenceEqual(Test))
                    return i;
            }
            int offset = Convert.ToInt32(FloatArrayTable.Position) / 4;

            foreach (float i in value)
            {
                bFloatArrayTable.Write((float)i);
                FloatArrayTableEntries.Add(i);
            }

            return offset;
        }
        public int AddGetIntArrayTable(int[] value)
        {
            int[] Test = new int[value.Length];
            int[] TestAgainst = IntArrayTableEntries.ToArray();
            for (int i = 0; i < TestAgainst.Length; i += 1)
            {
                if (i + value.Length > TestAgainst.Length)
                    break;
                Array.ConstrainedCopy(TestAgainst, i, Test, 0, value.Length);
                if (value.SequenceEqual(Test))
                    return i;
            }
            int offset = Convert.ToInt32(IntArrayTable.Position) / 4;

            foreach (int i in value)
            {
                bIntArrayTable.Write((int)i);
                IntArrayTableEntries.Add(i);
            }

            return offset;
        }
        public int AddGetHashIdTable(int value)
        {
            foreach (IntTableEntry entry in HashIdTableEntries)
                if (entry.value == value)
                    return entry.offset/4;

            IntTableEntry ent = new IntTableEntry();
            ent.value = value;
            ent.offset = Convert.ToInt32(HashIDTable.Position);
            HashIdTableEntries.Add(ent);

            bHashIDTable.Write((int)value);

            return ent.offset/4;
        }
        public int AddGetStringTable(string elementName)
        {
            foreach(StringTableEntry entry in StringTableEntries)
                if (entry.name == elementName)
                    return entry.offset;
            
            StringTableEntry ent = new StringTableEntry();
            ent.name = elementName;
            ent.offset = Convert.ToInt32(StringTable.Position);
            StringTableEntries.Add(ent);

            Tools.WriteStringToStream(StringTable, elementName);
            StringTable.WriteByte(0x00);

            return ent.offset;
        }
        public int AddGetCharTable(string elementName)
        {
            foreach (StringTableEntry entry in CharTableEntries)
                if (entry.name == elementName)
                    return entry.offset;

            StringTableEntry ent = new StringTableEntry();
            ent.name = elementName;
            ent.offset = Convert.ToInt32(CharTable.Position)/2;
            CharTableEntries.Add(ent);

            Tools.WriteUtf16StringToStream(CharTable, elementName);
            CharTable.WriteByte(0x00);
            CharTable.WriteByte(0x00);

            return ent.offset;
        }

        public AttributeType DetermineType(string ElementName, string AttributeName) 
        {
            return (AttributeType)Int32.Parse(GetTypingInformation(ElementName + ":" + AttributeName), CultureInfo.InvariantCulture);
        }
        public void WriteAttribute(XmlAttribute attribute, MemoryStream WorkRam, BinaryWriter bWorkRam, string ElementName)
        {
            int AttributePtr = AddGetStringTable(attribute.Name);
            AttributeType Type = DetermineType(ElementName, attribute.Name);
            bWorkRam.Write(AttributePtr);
            bWorkRam.Write((int)Type);
            Console.WriteLine("AttributeType: " + Type.ToString());
            switch (Type)
            {
                case AttributeType.TYPE_NONE:
                    Console.WriteLine("UNSUPPORTED TYPE @ " + TreeTable.Position);
                    Console.ReadKey();
                    break;
                case AttributeType.TYPE_INT:
                    int intWrite = Int32.Parse(attribute.Value, CultureInfo.InvariantCulture);
                    bWorkRam.Write((int)intWrite);
                    bWorkRam.Write((int)0x00);
                    Console.WriteLine("Int - Value: " + intWrite.ToString());
                    break;
                case AttributeType.TYPE_FLOAT:
                    float FloatValue = Single.Parse(attribute.Value.Substring(0, attribute.Value.Length - 1), CultureInfo.InvariantCulture);
                    bWorkRam.Write((float)FloatValue);
                    bWorkRam.Write((int)0x00);
                    Console.WriteLine("Float - Value: " + FloatValue.ToString());
                    break;
                case AttributeType.TYPE_STRING:
                    int StringOffset = AddGetStringTable(attribute.Value);
                    int StringLen = Encoding.UTF8.GetBytes(attribute.Value).Length;
                    bWorkRam.Write(StringOffset);
                    bWorkRam.Write(StringLen);
                    Console.WriteLine("String: " + attribute.Value);
                    break;
                case AttributeType.TYPE_CHAR:
                    int CharOffset = AddGetCharTable(attribute.Value);
                    int CharLen = Encoding.Unicode.GetBytes(attribute.Value).Length;
                    bWorkRam.Write(CharOffset);
                    bWorkRam.Write(CharLen);
                    Console.WriteLine("Char: " + attribute.Value);
                    break;
                case AttributeType.TYPE_HASH_ID:
                    int hashId = Int32.Parse(attribute.Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
                    
                    if (HashStrings)
                        hashId = Int32.Parse(Tools.GenerateShortHash(Encoding.UTF8.GetBytes(attribute.Value)), NumberStyles.HexNumber, CultureInfo.InvariantCulture);

                    int HashTableOffset = AddGetHashIdTable(hashId);
                    int HashTableSize = 4;
                    bWorkRam.Write(HashTableOffset);
                    bWorkRam.Write(HashTableSize);
                    Console.WriteLine("Hash ID Offset:" + HashTableOffset.ToString() + " size: " + HashTableSize);
                    break;
                case AttributeType.TYPE_INTEGER_ARRAY:
                    string[] arr = attribute.Value.Replace("[", "").Replace("]", "").Replace(" ", "").Split(',');
                    int[] intArr = new int[arr.Length];
                    for (int i = 0; i < intArr.Length; i++)
                        intArr[i] = Int32.Parse(arr[i], CultureInfo.InvariantCulture);

                    int IntArrayOffset = AddGetIntArrayTable(intArr);
                    int IntArraySize = intArr.Length;
                    bWorkRam.Write(IntArrayOffset);
                    bWorkRam.Write(IntArraySize);

                    Console.WriteLine("Int Array: " + attribute.Value);
                    break;
                case AttributeType.TYPE_FLOAT_ARRAY:
                    arr = attribute.Value.Replace("[", "").Replace("]", "").Replace(" ", "").Replace("f", "").Split(',');
                    float[] floatArr = new float[arr.Length];
                    for (int i = 0; i < floatArr.Length; i++)
                        floatArr[i] = Single.Parse(arr[i], CultureInfo.InvariantCulture);

                    int FloatArrayOffset = AddGetFloatArrayTable(floatArr);
                    int FloatArraySize = floatArr.Length;
                    bWorkRam.Write(FloatArrayOffset);
                    bWorkRam.Write(FloatArraySize);

                    Console.WriteLine("Float Array: " + attribute.Value);
                    break;
                case AttributeType.TYPE_FILE:
                    string fPath = Path.Combine(MainDir, attribute.Value);
                    byte[] data = File.ReadAllBytes(fPath);
                    int FilePtr = AddGetFileTable(data, Path.Combine(Environment.CurrentDirectory, fPath));
                    int FileSz = data.Length;
                    bWorkRam.Write(FilePtr);
                    bWorkRam.Write(FileSz);
                    Console.WriteLine("Reading File: " + fPath);
                    break;
                case AttributeType.TYPE_ID_STRING_LOOPBACK:
                    int StringIdTableOffset = AddGetStringIdTable(attribute.Value, Convert.ToInt32(TreeTable.Position));
                    bWorkRam.Write(StringIdTableOffset);
                    bWorkRam.Write((int)0x00);
                    Console.WriteLine("String Loopback: " + ElementName + " " + TreeTable.Position.ToString("X") + " (" + TreeTable.Position.ToString("X") + ")");
                    Console.WriteLine("Loopback ID String: " + StringIdTableOffset + " sz: 0");
                    break;
                case AttributeType.TYPE_ID_STRING:
                    StringIdTableOffset = AddGetStringIdTable(attribute.Value, -1);
                    bWorkRam.Write(StringIdTableOffset);
                    bWorkRam.Write((int)0x00);
                    Console.WriteLine("ID String: " + StringIdTableOffset + " sz: 0");
                    break;
                case AttributeType.TYPE_ID_INT_LOOPBACK:
                    int hash = Int32.Parse(attribute.Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
                    if (HashStrings)
                        hash = Int32.Parse(Tools.GenerateShortHash(Encoding.UTF8.GetBytes(attribute.Value)), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
                    int IntIdTableOffset = AddGetIntIdTable(hash, Convert.ToInt32(TreeTable.Position));
                    bWorkRam.Write(IntIdTableOffset);
                    bWorkRam.Write((int)0x00);
                    Console.WriteLine("Int Loopback: " + ElementName + " " + TreeTable.Position.ToString("X") + " (" + TreeTable.Position.ToString("X") + ")");
                    Console.WriteLine("Loopback ID Int: " + IntIdTableOffset + " sz: 0");
                    break;
                case AttributeType.TYPE_ID_INT:
                    hash = Int32.Parse(attribute.Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
                    if (HashStrings)
                        hash = Int32.Parse(Tools.GenerateShortHash(Encoding.UTF8.GetBytes(attribute.Value)), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
                    IntIdTableOffset = AddGetIntIdTable(hash, -1);

                    bWorkRam.Write(IntIdTableOffset);
                    bWorkRam.Write((int)0x00);
                    Console.WriteLine("Int Id: " + IntIdTableOffset + " sz: 0");
                    break;
                default:
                    Console.WriteLine("UNKNOWN TYPE @ " + TreeTable.Position);
                    Console.ReadKey();
                    break;
            }
            Console.WriteLine(attribute.Name + "=" + attribute.Value);
        }
        public void WriteElement(XmlNode node)
        {
            MemoryStream WorkRam = new MemoryStream();
            BinaryWriter bWorkRam = new BinaryWriter(WorkRam);
            int ElementPtr = AddGetStringTable(node.Name);
            int NumAttributes = 0;
            int ParentPtr = -1;
            int PrevSibling = -1;
            int NextSibling = -1;

            int FirstChild = -1;
            int LastChild = -1;
            long Position = TreeTable.Position;
            bWorkRam.Write(ElementPtr);
            bWorkRam.Write(NumAttributes);
            bWorkRam.Write(ParentPtr);
            bWorkRam.Write(PrevSibling);
            bWorkRam.Write(NextSibling);
            bWorkRam.Write(FirstChild);
            bWorkRam.Write(LastChild);
            
            foreach (XmlAttribute attribute in node.Attributes)
            {
                WriteAttribute(attribute, WorkRam, bWorkRam, node.Name);
            }
            

            WorkRam.Seek(0x00, SeekOrigin.Begin);
            WorkRam.CopyTo(TreeTable);
            
            foreach (XmlNode childNode in node.ChildNodes)
            {
                if(childNode != null)
                    AddGetTreeTable(childNode);
            }
            // Rewrite header now

            if (node.Attributes != null)
                NumAttributes = node.Attributes.Count;
            if (node.ParentNode != null)
                ParentPtr = AddGetTreeTable(node.ParentNode);
            if (node.PreviousSibling != null)
                PrevSibling = AddGetTreeTable(node.PreviousSibling);
            if (node.NextSibling != null)
                NextSibling = AddGetTreeTable(node.NextSibling);
            if (node.FirstChild != null)
                FirstChild = AddGetTreeTable(node.FirstChild);
            if (node.LastChild != null)
                LastChild = AddGetTreeTable(node.LastChild);

            long Position2 = TreeTable.Position;
            TreeTable.Seek(Position, SeekOrigin.Begin);
            bTreeTable.Write(ElementPtr);
            bTreeTable.Write(NumAttributes);
            bTreeTable.Write(ParentPtr);
            bTreeTable.Write(PrevSibling);
            bTreeTable.Write(NextSibling);
            bTreeTable.Write(FirstChild);
            bTreeTable.Write(LastChild);
            TreeTable.Seek(Position2, SeekOrigin.Begin);
        }

        public void Align(Stream s, int align)
        {

            int size = Convert.ToInt32(s.Length);

            if (size % align != 0)
            {
                int totalAlign = (align - (size % align));
                for (int i = 0; i < totalAlign; i++)
                {
                    s.WriteByte(0x00);
                }
            }
        }

        public void Term()
        {
            InfoFile.Close();
            TreeTable.Close();
            StringIDTable.Close();
            IntIDTable.Close();
            StringTable.Close();
            CharTable.Close();
            HashIDTable.Close();
            IntArrayTable.Close();
            FloatArrayTable.Close();
            FileTable.Close();

            bTreeTable.Close();
            bIntIDTable.Close();
            bFloatArrayTable.Close();
            bIntArrayTable.Close();
            bHashIDTable.Close();

            TreeTableEntries.Clear();
            StringTableEntries.Clear();
            CharTableEntries.Clear();
            HashIdTableEntries.Clear();
            IntArrayTableEntries.Clear();
            FloatArrayTableEntries.Clear();
            FileTableEntries.Clear();
            StringIDTableEntries.Clear();
            IntIDTableEntries.Clear();

            IsInitalized = false;
        }

        public void BuildCXML(string XmlFile, string CxmlFile)
        {
            if (!IsInitalized)
                Init(XmlFile, CxmlFile);
            Console.WriteLine("Magic Number: " + MagicNumber);
            Console.WriteLine("Version: " + Version.ToString("X"));

            XmlDocument XMLFile = new XmlDocument();
            XMLFile.Load(XmlFile);

            Tools.WriteStringToStream(InfoFile, MagicNumber);
            bInfoFile.Write((Int32)Version);
            byte[] headerPlaceholder = new byte[0x48];
            InfoFile.Write(headerPlaceholder, 0x00, headerPlaceholder.Length);

            foreach(XmlNode childNode in XMLFile.ChildNodes)
            {
                AddGetTreeTable(childNode);
            }

            int TreeTableOffset = Convert.ToInt32(InfoFile.Position);
            int TreeTableSize = Convert.ToInt32(TreeTable.Length);
            Align(TreeTable, 0x10);
            TreeTable.Seek(0x00, SeekOrigin.Begin);
            TreeTable.CopyTo(InfoFile);
            

            int StringIdOffset = Convert.ToInt32(InfoFile.Position);
            int StringIdSize = Convert.ToInt32(StringIDTable.Length);
            Align(StringIDTable, 0x10);
            StringIDTable.Seek(0x00, SeekOrigin.Begin);
            StringIDTable.CopyTo(InfoFile);

            int IntIdOffset = Convert.ToInt32(InfoFile.Position);
            int IntIdSize = Convert.ToInt32(IntIDTable.Length);
            Align(IntIDTable, 0x10);
            IntIDTable.Seek(0x00, SeekOrigin.Begin);
            IntIDTable.CopyTo(InfoFile);

            int StringTableOffset = Convert.ToInt32(InfoFile.Position);
            int StringTableSize = Convert.ToInt32(StringTable.Length);
            Align(StringTable, 0x10);
            StringTable.Seek(0x00, SeekOrigin.Begin);
            StringTable.CopyTo(InfoFile);

            int CharTableOffset = Convert.ToInt32(InfoFile.Position);
            int CharTableSize = Convert.ToInt32(CharTable.Length);
            Align(CharTable, 0x10);
            CharTable.Seek(0x00, SeekOrigin.Begin);
            CharTable.CopyTo(InfoFile);

            int HashIdTableOffset = Convert.ToInt32(InfoFile.Position);
            int HashIdTableSize = Convert.ToInt32(HashIDTable.Length);
            Align(HashIDTable, 0x10);
            HashIDTable.Seek(0x00, SeekOrigin.Begin);
            HashIDTable.CopyTo(InfoFile);

            int IntArrayTableOffset = Convert.ToInt32(InfoFile.Position);
            int IntArrayTableSize = Convert.ToInt32(IntArrayTable.Length);
            Align(IntArrayTable, 0x10);
            IntArrayTable.Seek(0x00, SeekOrigin.Begin);
            IntArrayTable.CopyTo(InfoFile);

            int FloatArrayTableOffset = Convert.ToInt32(InfoFile.Position);
            int FloatArrayTableSize = Convert.ToInt32(FloatArrayTable.Length);
            Align(FloatArrayTable, 0x10);
            FloatArrayTable.Seek(0x00, SeekOrigin.Begin);
            FloatArrayTable.CopyTo(InfoFile);

            int FileTableOffset = Convert.ToInt32(InfoFile.Position);
            int FileTableSize = Convert.ToInt32(FileTable.Length);
            Align(FileTable, 0x10);
            FileTable.Seek(0x00, SeekOrigin.Begin);
            FileTable.CopyTo(InfoFile);

            string excessData = GetTypingInformation("EXCESS");
            if (excessData != "0")
            {
                Console.WriteLine("Excess Data Found, Writing to end.");
                byte[] ExcessData = Convert.FromBase64String(excessData.Replace("*", "="));
                InfoFile.Read(ExcessData, 0x00, ExcessData.Length);
                byte[] UnCompressed = ZlibStream.UncompressBuffer(ExcessData);
                InfoFile.Write(UnCompressed, 0x00, UnCompressed.Length);
            }

            InfoFile.Seek(0x8, SeekOrigin.Begin);
            bInfoFile.Write(TreeTableOffset);
            bInfoFile.Write(TreeTableSize);
            bInfoFile.Write(StringIdOffset);
            bInfoFile.Write(StringIdSize);
            bInfoFile.Write(IntIdOffset);
            bInfoFile.Write(IntIdSize);
            bInfoFile.Write(StringTableOffset);
            bInfoFile.Write(StringTableSize);
            bInfoFile.Write(CharTableOffset);
            bInfoFile.Write(CharTableSize);
            bInfoFile.Write(HashIdTableOffset);
            bInfoFile.Write(HashIdTableSize);
            bInfoFile.Write(IntArrayTableOffset);
            bInfoFile.Write(IntArrayTableSize);
            bInfoFile.Write(FloatArrayTableOffset);
            bInfoFile.Write(FloatArrayTableSize);
            bInfoFile.Write(FileTableOffset);
            bInfoFile.Write(FileTableSize);

            Term();
        }
    }
}
