using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using HaloObjects;
using HaloPlugins;
using Global;
using System.Threading;
using HaloControls;
using System.Xml;
using System.Diagnostics;

namespace HaloMap
{
    public enum MapType : int
    {
        Singleplayer = 0,
        Multiplayer = 1,
        Mainmenu = 2,
        Shared = 3,
        SingleplayerShared = 4
    }

    public enum RawLocation : long
    {
        Internal = 0X00000000,
        Mainmenu = 0XC0000000,
        Shared = 0X80000000,
        SingleplayerShared = 0X40000000,
        Unknown
    }

    public class Map
    {
        public const int Constant = -2147086304;
        public const int FirstIdent = -512491520;
        public const ushort SaltStart = 0xE174;
        public string[] Classes;

        public FileStream fs;
        public BinaryReader br;
        public BinaryWriter bw;

        public Header Header = new Header();
        public Index Index = new Index();
        public Tags TagIndex = new Tags();
        public StringID StringId = new StringID();
        public Unicode Unicode = new Unicode();
        public Coconuts Coconuts = new Coconuts();
        public List<string> SIDs = new List<string>();
        public List<Tag> Tags = new List<Tag>();
        public Tag SelectedTag;

        public List<BSPBlock> BSPs = new List<BSPBlock>();

        public string MainMenu, Shared, SinglePlayerShared;

        public Map(string Location)
        {
            fs = new FileStream(Location, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
            br = new BinaryReader(fs);
            bw = new BinaryWriter(fs);
        }

        public Map(Project p, string Location)
        {
            // Initialize our Stream
            fs = new FileStream(Location, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
            br = new BinaryReader(fs);
            bw = new BinaryWriter(fs);

            // Read Map
            Header.Read(this);
            Index.Read(this);
            TagIndex.Read(this, null);
            StringId.Read(this);

            // Save Meta Table Size
            int OldMeta = Header.MetaTableSize;

            // Add References
            Globals.Strings.AddRange(SIDs);
            for (int i = 0; i < Tags.Count; i++)
            {
                Handler.GetPlugin(Tags[i].Class);
                Globals.Tags.Add(Tags[i].Path + "." + Handler.DefaultName);
                Globals.Idents.Add(Tags[i].Ident);
            }

            // Reverse Tags
            List<string> pTags = new List<string>();
            pTags.AddRange(p.Tags.ToArray());
            pTags.Reverse();

            int Max = 7;

            // Read Data
            br.BaseStream.Position = 0;
            byte[] Data = br.ReadBytes(Tags[Tags.Count - 1 - (Max - 1)].Offset);

            // Close
            br.Close();
            bw.Close();
            fs.Close();

            // Open New File
            fs = new FileStream(p.Directory + "\\" + p.Name + ".map", FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
            br = new BinaryReader(fs);
            bw = new BinaryWriter(fs);

            // Write
            bw.Write(Data);

            // Compile last few tags
            for (int i = Max - 1; i >= 0; i--)
            {
                if (!pTags[i].EndsWith(".scenario_structure_bsp") && !pTags[i].EndsWith(".lightmap"))
                {
                    // Compile Tag
                    FileStream ms = new FileStream(p.Directory + pTags[i], FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
                    BinaryReader mr = new BinaryReader(ms);
                    BinaryWriter mw = new BinaryWriter(ms);

                    // Read
                    Plugin tag = new Plugin(pTags[i].Substring(pTags[i].LastIndexOf(".") + 1), null);
                    tag.Layout.Read(mr);

                    // Save Info
                    bw.BaseStream.Position = bw.BaseStream.Length;
                    Tags[Tags.Count - 1 - i].Offset = (int)bw.BaseStream.Length;

                    // Compile with Padding
                    tag.Layout.Compile(bw, Index.SecondaryMagic);
                    bw.BaseStream.Position = bw.BaseStream.Length;
                    bw.Write(new byte[Padding(4)]);
                    Tags[Tags.Count - 1 - i].Size = (int)bw.BaseStream.Length - Tags[Tags.Count - 1 - i].Offset;

                    // Close
                    ms.Close();
                    mr.Close();
                    mw.Close();
                }
            }

            // Padding
            int Pad = Padding(4096);
            for (int i = 0; i < Pad; i++)
                bw.Write((byte)0xCD);

            Header.MetaTableSize = (int)bw.BaseStream.Length - (Header.IndexOffset + Header.IndexSize);

            // Rewrite Object Index
            bw.BaseStream.Position = Index.TagStart;
            for (int i = 0; i < Tags.Count; i++)
            {
                bw.Write(Microsoft.VisualBasic.Strings.StrReverse(Tags[i].Class).ToCharArray());
                bw.Write(Tags[i].Ident);
                bw.Write(Tags[i].Offset + Index.SecondaryMagic);
                bw.Write(Tags[i].Size);
            }

            // Rewrite Index
            bw.BaseStream.Position = 24;
            bw.Write(Header.MetaTableSize);
            bw.Write((Header.NonRawSize - OldMeta) + Header.MetaTableSize);

            // Sign
            Header.MapChecksum = 0;
            br.BaseStream.Position = 2048;
            int times = ((int)br.BaseStream.Length - 2048) / 4;
            for (int i = 0; i < times; i++)
            {
                Header.MapChecksum ^= br.ReadInt32();
            }

            // Finish
            bw.BaseStream.Position = 720;
            bw.Write(Header.MapChecksum);

            // Close
            fs.Close();
            br.Close();
            bw.Close();

            MessageBox.Show("Done!");
        }

        public Map(Project p, string Location, Label lblStatus, Label lblTag, DevExpress.XtraEditors.ProgressBarControl progress, DevExpress.XtraEditors.ProgressBarControl total)
        {
            // Initialize our Stream
            Classes = new string[] { "+!#$", "nec*", "pae*", "ihe*", "hgi*", "dpi*", "piq*", "aer*", "ecs*", "/**/", ">xf<", "MooB", "PCED", "RCED", "2SGM", "MTRP", "glda", "**ia", "!tna", "dpib", "mtib", "colb", "tdsb", "rahc", "*nic", "*ulc", "dwlc", "lloc", "nloc", "oloc", "tnoc", "aerc", "lrtc", "s*cd", "*ced", "aced", "ived", "oved", "*rgd", "cbod", "effe", "roge", "piqe", " gof", "toof", "hcpf", "brag", "fdlg", "foog", "ihrg", "tmlh", " tmh", "*csh", "#duh", "gduh", "meti", "cmti", "damj", "!tpj", "snel", "ifil", "hgil", "dnsl", "pmtl", "hcam", "gtam", "gldm", "rtem", "edom", "tdpm", "ylpm", "glum", "tdhn", "ejbo", "omhp", "syhp", "vomp", "yhpp", "jorp", "3trp", "psbs", "necs", "rncs", "+xfs", "dahs", "ylis", "niks", " yks", "tils", "lcns", "!dns", "edns", "xmns", "saps", "!kps", "ecss", "tlss", "mets", "lyts", "ltdt", "kart", "*grt", "gldu", "!hgu", "ihnu", "cinu", "tinu", "chev", "ihev", "xtrv", "paew", "taew", "tigw", "ztgw", "pihw", "lgiw", "dniw", "ihpw" };
            fs = new FileStream(Location, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
            br = new BinaryReader(fs);
            bw = new BinaryWriter(fs);

            lblStatus.Text = "Writing Header...";
            Application.DoEvents();
            int Pad = 0;

            // Header
            bw.Write(new byte[2048]);

            // ms = MetaStream, mr = MetaReader, mw = MetaWriter, rr = RawReader
            #region Prep Streams
            FileStream ms = new FileStream(p.Directory + p.Tags[p.Tags.Count - 1], FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
            BinaryReader mr = new BinaryReader(ms);
            BinaryWriter mw = new BinaryWriter(ms);
            BinaryReader rr = new BinaryReader(new FileStream(p.Directory + p.Tags[p.Tags.Count - 1].Replace(".sound_diagnostics", ".soundraw"), FileMode.Open, FileAccess.Read, FileShare.Read));

            Globals.Strings.Clear();
            Globals.Idents.Clear();
            #endregion

            #region Sound Raw

            // UI
            lblStatus.Text = "Writing Internal Sound Raw...";
            Application.DoEvents();

            mr.BaseStream.Position = 512 + 64;
            Reflexive r = new Reflexive();
            r.Read(mr, 0);
            progress.Properties.Maximum = r.ChunkCount;
            for (int i = 0; i < r.ChunkCount; i++)
            {
                // UI
                progress.Text = i.ToString();
                Application.DoEvents();

                // Read Chunk Data
                mr.BaseStream.Position = r.Translation + (i * 12);
                int Offset = mr.ReadInt32();
                int Size = mr.ReadInt32() & 0x7FFFFFFF;

                if ((Offset & 0xC0000000) == 0 && Size > 0)
                {
                    // Write new Tag Data
                    mw.BaseStream.Position = r.Translation + (i * 12);
                    mw.Write((int)bw.BaseStream.Length);

                    // Write Raw and Padding
                    rr.BaseStream.Position = Offset;
                    byte[] Buffer = rr.ReadBytes(Size);
                    bw.Write(Buffer);
                    Pad = Padding(512);
                    bw.Write(new byte[Pad]);
                }
            }
            progress.Text = "0";
            mr.Close();
            mw.Close();
            ms.Close();
            rr.Close();

            #endregion

            #region Model Raw
            progress.Properties.Maximum = p.Tags.Count;
            for (int i = 0; i < p.Tags.Count; i++)
            {
                // UI
                progress.Text = i.ToString();
                Application.DoEvents();

                if (p.Tags[i].EndsWith(".render_model"))
                {
                    #region Mesh Sections
                    ms = new FileStream(p.Directory + p.Tags[i], FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
                    mr = new BinaryReader(ms);
                    mw = new BinaryWriter(ms);
                    rr = new BinaryReader(new FileStream(p.Directory + p.Tags[i].Replace(".render_model", ".model1raw"), FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite));

                    // Raw
                    mr.BaseStream.Position = 512 + 36;
                    r = new Reflexive();
                    r.Read(mr, 0);
                    for (int x = 0; x < r.ChunkCount; x++)
                    {
                        // UI
                        lblStatus.Text = "Writing Model Raw 1: " + p.Tags[i];
                        Application.DoEvents();

                        // Read
                        mr.BaseStream.Position = r.Translation + (x * 92) + 56;
                        int Offset = mr.ReadInt32();
                        int Size = mr.ReadInt32();

                        if ((Offset & 0xC0000000) == 0 && Size > 0)
                        {
                            // Write new Offset
                            mw.BaseStream.Position = r.Translation + (x * 92) + 56;
                            mw.Write((int)bw.BaseStream.Length);

                            // Write Data and Padding
                            rr.BaseStream.Position = Offset;
                            byte[] Buffer = rr.ReadBytes(Size);
                            bw.Write(Buffer);
                            Pad = Padding(512);
                            bw.Write(new byte[Pad]);
                        }
                    }
                    rr.Close();
                    #endregion

                    #region Depreciated Lightmap Raw
                    // Raw
                    rr = new BinaryReader(new FileStream(p.Directory + p.Tags[i].Replace(".render_model", ".model2raw"), FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite));
                    mr.BaseStream.Position = 512 + 116;
                    r = new Reflexive();
                    r.Read(mr, 0);
                    for (int x = 0; x < r.ChunkCount; x++)
                    {
                        // UI
                        lblStatus.Text = "Writing Model Raw 2: " + p.Tags[i];
                        Application.DoEvents();

                        // Read
                        mr.BaseStream.Position = r.Translation + (x * 88) + 52;
                        int Offset = mr.ReadInt32();
                        int Size = mr.ReadInt32();

                        if ((Offset & 0xC0000000) == 0 && Size > 0)
                        {
                            // Write new Offset
                            mw.BaseStream.Position = r.Translation + (x * 88) + 52;
                            mw.Write((int)bw.BaseStream.Length);

                            // Write Data and Padding
                            rr.BaseStream.Position = Offset;
                            byte[] Buffer = rr.ReadBytes(Size);
                            bw.Write(Buffer);
                            Pad = Padding(512);
                            bw.Write(new byte[Pad]);
                        }
                    }
                    ms.Close();
                    mr.Close();
                    mw.Close();
                    rr.Close();
                    #endregion
                }
            }
            #endregion

            #region BSP and Lightmap Raw
            // Find the Scenario Tag
            string Scenario = "";
            for (int i = 0; i < p.Tags.Count; i++)
            {
                if (p.Tags[i].EndsWith(".scenario"))
                {
                    Scenario = p.Tags[i];
                    break;
                }
            }

            // Open and navigate to the Structure BSPs Reflexive
            ms = new FileStream(p.Directory + Scenario, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
            mr = new BinaryReader(ms);
            Handler.GetPlugin("scnr");
            Plugin scnr = new Plugin(Handler.DefaultName, null);
            scnr.Layout.Read(mr);
            Global.Globals.Plugin = scnr.Layout;
            ms.Close();
            mr.Close();

            // Compile all the Raw for evey BSP and Lightmap
            HaloObjects.Reflexive bsps = (HaloObjects.Reflexive)scnr.Layout.Fields[61].Clone();
            for (int i = 0; i < bsps.GetChunkCount(); i++)
            {
                // Pull out the BSP and Lightmap Tags
                bsps.ChangeIndex(i);
                HaloObjects.TagReference bsp = (HaloObjects.TagReference)((HaloObjects.MetaNode[])bsps.GetValue())[4];
                HaloObjects.TagReference lightmap = (HaloObjects.TagReference)((HaloObjects.MetaNode[])bsps.GetValue())[5];

                // Open the BSP
                ms = new FileStream(p.Directory + (string)bsp.GetValue(), FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
                mr = new BinaryReader(ms);
                mw = new BinaryWriter(ms);

                #region Detail Objects
                rr = new BinaryReader(new FileStream(p.Directory + ((string)bsp.GetValue()).Replace(".scenario_structure_bsp", ".bsp1raw"), FileMode.Open, FileAccess.Read, FileShare.Read));
                mr.BaseStream.Position = 512 + 172;
                r = new Reflexive();
                r.Read(mr, 0);

                // UI
                lblStatus.Text = "Writing BSP1: " + (string)bsp.GetValue();
                progress.Properties.Maximum = r.ChunkCount;
                progress.Text = "0";
                Application.DoEvents();

                // Data
                for (int x = 0; x < r.ChunkCount; x++)
                {
                    // UI
                    progress.Text = x.ToString();
                    Application.DoEvents();

                    // Data
                    mr.BaseStream.Position = r.Translation + (x * 176) + 40;
                    int Offset = mr.ReadInt32();
                    int Size = mr.ReadInt32();

                    if (Offset >= 0 && Size > 0)
                    {
                        // Write new Pointer
                        mw.BaseStream.Position = r.Translation + (x * 176) + 40;
                        mw.Write((int)bw.BaseStream.Length);

                        // Write Raw with Padding
                        rr.BaseStream.Position = Offset;
                        byte[] Buffer = rr.ReadBytes(Size);
                        bw.Write(Buffer);
                        Pad = Padding(512);
                        bw.Write(new byte[Pad]);
                    }
                }
                rr.Close();
                #endregion

                #region Permutations
                rr = new BinaryReader(new FileStream(p.Directory + ((string)bsp.GetValue()).Replace(".scenario_structure_bsp", ".bsp2raw"), FileMode.Open, FileAccess.Read, FileShare.Read));
                mr.BaseStream.Position = 512 + 328;
                r = new Reflexive();
                r.Read(mr, 0);

                // UI
                lblStatus.Text = "Writing BSP2: " + (string)bsp.GetValue();
                progress.Properties.Maximum = r.ChunkCount;
                progress.Text = "0";
                Application.DoEvents();

                // Data
                for (int x = 0; x < r.ChunkCount; x++)
                {
                    // UI
                    progress.Text = x.ToString();
                    Application.DoEvents();

                    // Data
                    mr.BaseStream.Position = r.Translation + (x * 200) + 40;
                    int Offset = mr.ReadInt32();
                    int Size = mr.ReadInt32();

                    if (Offset >= 0 && Size > 0)
                    {
                        // Write new Pointer
                        mw.BaseStream.Position = r.Translation + (x * 200) + 40;
                        mw.Write((int)bw.BaseStream.Length);

                        // Write Raw with Padding
                        rr.BaseStream.Position = Offset;
                        byte[] Buffer = rr.ReadBytes(Size);
                        bw.Write(Buffer);
                        Pad = Padding(512);
                        bw.Write(new byte[Pad]);
                    }
                }
                ms.Close();
                mr.Close();
                mw.Close();
                rr.Close();
                #endregion

                #region Lightmap Raw
                if (File.Exists(p.Directory + (string)lightmap.GetValue()))
                {
                    ms = new FileStream(p.Directory + (string)lightmap.GetValue(), FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
                    mr = new BinaryReader(ms);
                    mw = new BinaryWriter(ms);
                    rr = new BinaryReader(new FileStream(p.Directory + ((string)lightmap.GetValue()).Replace(".lightmap", ".lightmapraw"), FileMode.Open, FileAccess.Read, FileShare.Read));

                    #region Lightmap Groups
                    mr.BaseStream.Position = 512 + 128;
                    r = new Reflexive();
                    r.Read(mr, 0);
                    for (int x = 0; x < r.ChunkCount; x++)
                    {
                        #region Clustors
                        mr.BaseStream.Position = r.Translation + (x * 104) + 32;
                        Reflexive clustors = new Reflexive();
                        clustors.Read(mr, 0);
                        Application.DoEvents();

                        // UI
                        lblStatus.Text = "Writing Lightmap1: " + (string)lightmap.GetValue();
                        progress.Properties.Maximum = clustors.ChunkCount;
                        progress.Text = "0";

                        // Data
                        for (int z = 0; z < clustors.ChunkCount; z++)
                        {
                            // UI
                            progress.Text = z.ToString();
                            Application.DoEvents();

                            // Data
                            mr.BaseStream.Position = clustors.Translation + (z * 84) + 40;
                            int Offset = mr.ReadInt32();
                            int Size = mr.ReadInt32();

                            if (Offset >= 0 && Size > 0)
                            {
                                // Write new Pointers
                                mw.BaseStream.Position = clustors.Translation + (z * 84) + 40;
                                mw.Write((int)bw.BaseStream.Length);

                                // Write Raw and Padding
                                rr.BaseStream.Position = Offset;
                                byte[] Buffer = rr.ReadBytes(Size);
                                bw.Write(Buffer);
                                Pad = Padding(512);
                                bw.Write(new byte[Pad]);
                            }
                        }
                        #endregion

                        #region Poop Definitions
                        mr.BaseStream.Position = r.Translation + (x * 104) + 48;
                        clustors = new Reflexive();
                        clustors.Read(mr, 0);

                        // UI
                        lblStatus.Text = "Writing Lightmap2: " + (string)lightmap.GetValue();
                        progress.Properties.Maximum = clustors.ChunkCount;
                        progress.Text = "0";
                        Application.DoEvents();

                        // Data
                        for (int z = 0; z < clustors.ChunkCount; z++)
                        {
                            // UI
                            progress.Text = z.ToString();
                            Application.DoEvents();

                            // Data
                            mr.BaseStream.Position = clustors.Translation + (z * 84) + 40;
                            int Offset = mr.ReadInt32();
                            int Size = mr.ReadInt32();

                            if (Offset >= 0 && Size > 0)
                            {
                                // Write new Pointers
                                mw.BaseStream.Position = clustors.Translation + (z * 84) + 40;
                                mw.Write((int)bw.BaseStream.Length);

                                // Write Raw and Padding
                                rr.BaseStream.Position = Offset;
                                byte[] Buffer = rr.ReadBytes(Size);
                                bw.Write(Buffer);
                                Pad = Padding(512);
                                bw.Write(new byte[Pad]);
                            }
                        }
                        #endregion

                        #region Geometry Buckets
                        mr.BaseStream.Position = r.Translation + (x * 104) + 64;
                        clustors = new Reflexive();
                        clustors.Read(mr, 0);

                        // UI
                        lblStatus.Text = "Writing Lightmap3: " + (string)lightmap.GetValue();
                        progress.Properties.Maximum = clustors.ChunkCount;
                        progress.Text = "0";
                        Application.DoEvents();

                        for (int z = 0; z < clustors.ChunkCount; z++)
                        {
                            // UI
                            progress.Text = z.ToString();
                            Application.DoEvents();

                            // Data
                            mr.BaseStream.Position = clustors.Translation + (z * 56) + 12;
                            int Offset = mr.ReadInt32();
                            int Size = mr.ReadInt32();

                            if (Offset >= 0 && Size > 0)
                            {
                                // Write new Pointers
                                mw.BaseStream.Position = clustors.Translation + (z * 56) + 12;
                                mw.Write((int)bw.BaseStream.Length);

                                // Write Raw and Padding
                                rr.BaseStream.Position = Offset;
                                byte[] Buffer = rr.ReadBytes(Size);
                                bw.Write(Buffer);
                                Pad = Padding(512);
                                bw.Write(new byte[Pad]);
                            }
                        }
                        #endregion
                    }
                    #endregion

                    ms.Close();
                    mr.Close();
                    mw.Close();
                    rr.Close();
                }
                #endregion

                #region Decorators
                ms = new FileStream(p.Directory + (string)bsp.GetValue(), FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
                mr = new BinaryReader(ms);
                mw = new BinaryWriter(ms);
                rr = new BinaryReader(new FileStream(p.Directory + ((string)bsp.GetValue()).Replace(".scenario_structure_bsp", ".bsp4raw"), FileMode.Open, FileAccess.Read, FileShare.Read));

                mr.BaseStream.Position = 512 + 580;
                r = new Reflexive();
                r.Read(mr, 0);
                for (int x = 0; x < r.ChunkCount; x++)
                {
                    #region Cache Blocks
                    mr.BaseStream.Position = r.Translation + (x * 48) + 16;
                    Reflexive blocks = new Reflexive();
                    blocks.Read(mr, 0);

                    // UI
                    lblStatus.Text = "Writing BSP4: " + (string)bsp.GetValue();
                    progress.Properties.Maximum = blocks.ChunkCount;
                    progress.Text = "0";
                    Application.DoEvents();

                    // Data
                    for (int z = 0; z < blocks.ChunkCount; z++)
                    {
                        // UI
                        progress.Text = z.ToString();
                        Application.DoEvents();

                        // Data
                        mr.BaseStream.Position = blocks.Translation + (z * 44);
                        int Offset = mr.ReadInt32();
                        int Size = mr.ReadInt32();

                        if (Offset >= 0 && Size > 0)
                        {
                            // Write new Pointer
                            mw.BaseStream.Position = blocks.Translation + (z * 44);
                            mw.Write((int)bw.BaseStream.Length);

                            // Write Raw with Padding
                            rr.BaseStream.Position = Offset;
                            byte[] Buffer = rr.ReadBytes(Size);
                            bw.Write(Buffer);
                            Pad = Padding(512);
                            bw.Write(new byte[Pad]);
                        }
                    }
                    #endregion
                }
                rr.Close();
                #endregion

                #region Water Definitions
                rr = new BinaryReader(new FileStream(p.Directory + ((string)bsp.GetValue()).Replace(".scenario_structure_bsp", ".bsp3raw"), FileMode.Open, FileAccess.Read, FileShare.Read));
                mr.BaseStream.Position = 512 + 548;
                r = new Reflexive();
                r.Read(mr, 0);

                // UI
                lblStatus.Text = "Writing BSP3: " + (string)bsp.GetValue();
                progress.Properties.Maximum = r.ChunkCount;
                progress.Text = "0";
                Application.DoEvents();

                // Data
                for (int x = 0; x < r.ChunkCount; x++)
                {
                    // UI
                    progress.Text = x.ToString();
                    Application.DoEvents();

                    // Data
                    mr.BaseStream.Position = r.Translation + (x * 172) + 16;
                    int Offset = mr.ReadInt32();
                    int Size = mr.ReadInt32();

                    if (Offset >= 0 && Size > 0)
                    {
                        // Write new Pointer
                        mw.BaseStream.Position = r.Translation + (x * 172) + 16;
                        mw.Write((int)bw.BaseStream.Length);

                        // Write Raw with Padding
                        rr.BaseStream.Position = Offset;
                        byte[] Buffer = rr.ReadBytes(Size);
                        bw.Write(Buffer);
                        Pad = Padding(512);
                        bw.Write(new byte[Pad]);
                    }
                }
                ms.Close();
                mr.Close();
                mw.Close();
                rr.Close();
                #endregion
            }
            #endregion

            #region Weather Raw
            lblStatus.Text = "Writing Weather Raw";
            progress.Properties.Maximum = p.Tags.Count;
            progress.Text = "0";
            Application.DoEvents();
            for (int i = 0; i < p.Tags.Count; i++)
            {
                // UI
                progress.Text = i.ToString();
                Application.DoEvents();

                if (p.Tags[i].EndsWith(".weather"))
                {
                    // UI
                    lblStatus.Text = "Writing Weateher Raw: " + p.Tags[i];
                    Application.DoEvents();

                    ms = new FileStream(p.Directory + p.Tags[i], FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
                    mr = new BinaryReader(ms);
                    mw = new BinaryWriter(ms);
                    rr = new BinaryReader(new FileStream(p.Directory + p.Tags[i].Replace(".weather", ".weatherraw"), FileMode.Open, FileAccess.Read, FileShare.Read));

                    mr.BaseStream.Position = 512;
                    r = new Reflexive();
                    r.Read(mr, 0);
                    for (int x = 0; x < r.ChunkCount; x++)
                    {
                        mr.BaseStream.Position = r.Translation + (x * 140) + 64;
                        int Offset = mr.ReadInt32();
                        int Size = mr.ReadInt32();

                        if ((Offset & 0xC0000000) == 0 && Size > 0)
                        {
                            // Write new Pointers
                            mw.BaseStream.Position = r.Translation + (x * 140) + 64;
                            mw.Write((int)bw.BaseStream.Length);

                            // Write Raw and Padding
                            rr.BaseStream.Position = Offset;
                            byte[] Buffer = rr.ReadBytes(Size);
                            bw.Write(Buffer);
                            Pad = Padding(512);
                            bw.Write(new byte[Pad]);
                        }
                    }
                    ms.Close();
                    mr.Close();
                    mw.Close();
                    rr.Close();
                }
            }
            #endregion

            #region Decorator Raw
            lblStatus.Text = "Writing Decorator Raw";
            progress.Properties.Maximum = p.Tags.Count;
            progress.Text = "0";
            Application.DoEvents();
            for (int i = 0; i < p.Tags.Count; i++)
            {
                // UI
                progress.Text = i.ToString();
                Application.DoEvents();

                if (p.Tags[i].EndsWith(".decorator"))
                {
                    // UI
                    lblStatus.Text = "Writing Decorator Raw: " + p.Tags[i];
                    Application.DoEvents();

                    ms = new FileStream(p.Directory + p.Tags[i], FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
                    mr = new BinaryReader(ms);
                    mw = new BinaryWriter(ms);
                    rr = new BinaryReader(new FileStream(p.Directory + p.Tags[i].Replace(".decorator", ".decoratorraw"), FileMode.Open, FileAccess.Read, FileShare.Read));

                    mr.BaseStream.Position = 512 + 56;
                    int Offset = mr.ReadInt32();
                    int Size = mr.ReadInt32();

                    if ((Offset & 0xC0000000) == 0 && Size > 0)
                    {
                        // Write new Pointers
                        mw.BaseStream.Position = 512 + 56;
                        mw.Write((int)bw.BaseStream.Length);

                        // Write Raw and Padding
                        rr.BaseStream.Position = Offset;
                        byte[] Buffer = rr.ReadBytes(Size);
                        bw.Write(Buffer);
                        Pad = Padding(512);
                        bw.Write(new byte[Pad]);
                    }
                    ms.Close();
                    mr.Close();
                    mw.Close();
                    rr.Close();
                }
            }
            #endregion

            #region Particle Raw
            lblStatus.Text = "Writing Particle Raw";
            progress.Properties.Maximum = p.Tags.Count;
            progress.Text = "0";
            Application.DoEvents();
            for (int i = 0; i < p.Tags.Count; i++)
            {
                // UI
                progress.Text = i.ToString();
                Application.DoEvents();

                if (p.Tags[i].EndsWith(".particle_model"))
                {
                    // UI
                    lblStatus.Text = "Writing Particle Raw: " + p.Tags[i];
                    Application.DoEvents();

                    ms = new FileStream(p.Directory + p.Tags[i], FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
                    mr = new BinaryReader(ms);
                    mw = new BinaryWriter(ms);
                    rr = new BinaryReader(new FileStream(p.Directory + p.Tags[i].Replace(".particle_model", ".particleraw"), FileMode.Open, FileAccess.Read, FileShare.Read));

                    mr.BaseStream.Position = 512 + 160;
                    int Offset = mr.ReadInt32();
                    int Size = mr.ReadInt32();

                    if ((Offset & 0xC0000000) == 0 && Size > 0)
                    {
                        // Write new Pointers
                        mw.BaseStream.Position = 512 + 160;
                        mw.Write((int)bw.BaseStream.Length);

                        // Write Raw and Padding
                        rr.BaseStream.Position = Offset;
                        byte[] Buffer = rr.ReadBytes(Size);
                        bw.Write(Buffer);
                        Pad = Padding(512);
                        bw.Write(new byte[Pad]);
                    }
                    ms.Close();
                    mr.Close();
                    mw.Close();
                    rr.Close();
                }
            }
            #endregion

            #region Coconut Model Raw
            ms = new FileStream(p.Directory + p.Tags[p.Tags.Count - 1], FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
            mr = new BinaryReader(ms);
            mw = new BinaryWriter(ms);
            rr = new BinaryReader(new FileStream(p.Directory + p.Tags[p.Tags.Count - 1].Replace(".sound_diagnostics", ".modelraw"), FileMode.Open, FileAccess.Read, FileShare.Read));

            // UI
            lblStatus.Text = "Writing Coconut Model Raw...";
            Application.DoEvents();

            mr.BaseStream.Position = 512 + 80;
            r = new Reflexive();
            r.Read(mr, 0);
            progress.Properties.Maximum = r.ChunkCount;
            for (int i = 0; i < r.ChunkCount; i++)
            {
                // UI
                progress.Text = i.ToString();
                Application.DoEvents();

                // Read Chunk Data
                mr.BaseStream.Position = r.Translation + (i * 44) + 8;
                int Offset = mr.ReadInt32();
                int Size = mr.ReadInt32();

                if ((Offset & 0xC0000000) == 0 && Size > 0)
                {
                    // Write new Tag Data
                    mw.BaseStream.Position = r.Translation + (i * 44) + 8;
                    mw.Write((int)bw.BaseStream.Length);

                    // Write Raw and Padding
                    rr.BaseStream.Position = Offset;
                    byte[] Buffer = rr.ReadBytes(Size);
                    bw.Write(Buffer);
                    Pad = Padding(512);
                    bw.Write(new byte[Pad]);
                }
            }
            progress.Text = "0";
            mr.Close();
            mw.Close();
            ms.Close();
            rr.Close();
            #endregion

            #region Animation Raw
            lblStatus.Text = "Writing Animation Raw";
            progress.Properties.Maximum = p.Tags.Count;
            progress.Text = "0";
            Application.DoEvents();
            for (int i = 0; i < p.Tags.Count; i++)
            {
                // UI
                progress.Text = i.ToString();
                Application.DoEvents();

                if (p.Tags[i].EndsWith(".model_animation_graph"))
                {
                    // UI
                    lblStatus.Text = "Writing Animation Raw: " + p.Tags[i];
                    Application.DoEvents();

                    ms = new FileStream(p.Directory + p.Tags[i], FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
                    mr = new BinaryReader(ms);
                    mw = new BinaryWriter(ms);
                    rr = new BinaryReader(new FileStream(p.Directory + p.Tags[i].Replace(".model_animation_graph", ".animationraw"), FileMode.Open, FileAccess.Read, FileShare.Read));

                    mr.BaseStream.Position = 512 + 172;
                    r = new Reflexive();
                    r.Read(mr, 0);
                    for (int x = 0; x < r.ChunkCount; x++)
                    {
                        mr.BaseStream.Position = r.Translation + (x * 20) + 4;
                        int Size = mr.ReadInt32();
                        int Offset = mr.ReadInt32();

                        if ((Offset & 0xC0000000) == 0 && Size > 0)
                        {
                            // Write new Pointers
                            mw.BaseStream.Position = r.Translation + (x * 20) + 8;
                            mw.Write((int)bw.BaseStream.Length);

                            // Write Raw and Padding
                            rr.BaseStream.Position = Offset;
                            byte[] Buffer = rr.ReadBytes(Size);
                            bw.Write(Buffer);
                            Pad = Padding(512);
                            bw.Write(new byte[Pad]);
                        }
                    }
                    ms.Close();
                    mr.Close();
                    mw.Close();
                    rr.Close();
                }
            }
            #endregion

            #region Generate a List of StringIds and Tag Paths
            // UI
            progress.Properties.Maximum = p.Tags.Count;
            progress.Text = "0";
            lblStatus.Text = "Compiling String Id List...";
            Application.DoEvents();

            // Data
            List<string> StringIds = new List<string>();
            //---------------- DEBUG CODE ----------------
            ms = new FileStream(p.Directory + "sids.bin", FileMode.Open, FileAccess.Read, FileShare.Read);
            mr = new BinaryReader(ms);

            mr.BaseStream.Position = 0;
            string s = "";
            char cc;
            while (mr.BaseStream.Position < mr.BaseStream.Length)
            {
                while ((cc = mr.ReadChar()) != '\0')
                    s += cc;

                StringIds.Add(s);
                s = "";
            }

            mr.Close();
            ms.Close();
            //---------------- DEBUG CODE ----------------

            /*for (int i = 0; i < p.Tags.Count; i++)
            {
                // UI
                progress.Text = i.ToString();
                Application.DoEvents();

                // Open Tag and read out SID table
                ms = new FileStream(p.Directory + p.Tags[i], FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
                mr = new BinaryReader(ms);

                // Sanity Check
                if (mr.BaseStream.Length != 0)
                {
                    int TagCount = mr.ReadInt32();
                    int SIDCount = mr.ReadInt32();
                    mr.BaseStream.Position = mr.ReadInt32();

                    // Tags
                    for (int x = 0; x < TagCount; x++)
                    {
                        char c; string temp = "";
                        while ((c = mr.ReadChar()) != '\0')
                            temp += c;
                    }

                    // Strings
                    for (int x = 0; x < SIDCount; x++)
                    {
                        char c; string temp = "";
                        while ((c = mr.ReadChar()) != '\0')
                            temp += c;

                        if (StringIds.Contains(temp) == false)
                            StringIds.Add(temp);
                    }
                }

                ms.Close();
                mr.Close();
            }*/


            // Unicode Strings
            ms = new FileStream(p.Directory + "strings.unicode_string_list", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
            mr = new BinaryReader(ms);
            int StringIdCount = mr.ReadInt32();
            int[] StringCount = new int[8];
            for (int i = 0; i < 8; i++)
                StringCount[i] = mr.ReadInt32();

            int StringTable = mr.ReadInt32();
            mr.BaseStream.Position = StringTable;
            for (int i = 0; i < StringIdCount; i++)
            {
                char c; string temp = "";
                while ((c = mr.ReadChar()) != '\0')
                    temp += c;

                if (StringIds.Contains(temp) == false)
                    StringIds.Add(temp);
            }
            ms.Close();
            mr.Close();

            // Generate a list of Idents
            List<int> Idents = new List<int>();
            for (int i = 0; i < p.Tags.Count; i++)
                Idents.Add((int)CreateIdent(i)); //FirstIdent + (i * 65537));

            Globals.Idents.AddRange(Idents);
            Globals.Strings.AddRange(StringIds);
            Globals.Tags.AddRange(p.Tags);
            #endregion

            #region Compile BSP and Lightmap Meta
            List<int> BSPOffsets = new List<int>();
            List<int> BSPMagics = new List<int>();
            List<int> BSPSizes = new List<int>();
            List<int> LightmapOffsets = new List<int>();
            List<int> LightmapSizes = new List<int>();

            // UI
            progress.Properties.Maximum = bsps.GetChunkCount() * 2;
            progress.Text = "0";
            Application.DoEvents();

            for (int i = 0; i < bsps.GetChunkCount(); i++)
            {
                // Pull out the BSP and Lightmap Tags
                bsps.ChangeIndex(i);
                HaloObjects.TagReference bsp = (HaloObjects.TagReference)((HaloObjects.MetaNode[])bsps.GetValue())[4];
                HaloObjects.TagReference lightmap = (HaloObjects.TagReference)((HaloObjects.MetaNode[])bsps.GetValue())[5];

                // UI
                lblStatus.Text = "Compiling BSP: " + (string)bsp.GetValue();
                Application.DoEvents();

                // Open the BSP
                ms = new FileStream(p.Directory + (string)bsp.GetValue(), FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
                mr = new BinaryReader(ms);
                mw = new BinaryWriter(ms);

                // Calculate the BSP Magic
                int IndexSize = (1448 + (p.Tags.Count * 16));
                IndexSize += 512 - (IndexSize % 512);
                int MagicModifier = Constant - 32 + IndexSize;
                int Offset = (int)bw.BaseStream.Position;
                int Magic = MagicModifier - Offset;
                BSPOffsets.Add(Offset);
                BSPMagics.Add(Magic);

                // Compile BSP Meta
                Handler.GetPlugin("sbsp");
                Plugin sbsp = new Plugin(Handler.DefaultName, null);
                sbsp.Layout.Read(mr);
                sbsp.Layout.Compile(bw, Magic);
                ms.Close();
                mr.Close();
                mw.Close();

                // Try Padding sbsp
                bw.BaseStream.Position = bw.BaseStream.Length;
                bw.Write(new byte[Padding(4)]);

                // Get Lightmap Info
                bw.BaseStream.Position = bw.BaseStream.Length;
                LightmapOffsets.Add((int)bw.BaseStream.Length);
                Globals.Plugin = scnr.Layout;

                // UI
                lblStatus.Text = "Compiling Lightmap: " + (string)lightmap.GetValue();
                progress.Text = (Convert.ToInt32(progress.Text) + 1).ToString();
                Application.DoEvents();

                // Compile Lightmap Meta
                if (File.Exists(p.Directory + (string)lightmap.GetValue()))
                {
                    ms = new FileStream(p.Directory + (string)lightmap.GetValue(), FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
                    mr = new BinaryReader(ms);
                    mw = new BinaryWriter(ms);

                    Handler.GetPlugin("ltmp");
                    Plugin ltmp = new Plugin(Handler.DefaultName, null);
                    ltmp.Layout.Read(mr);
                    ltmp.Layout.Compile(bw, Magic);
                    ms.Close();
                    mr.Close();
                    mw.Close();
                }

                // Write BSP Header
                bw.BaseStream.Position = bw.BaseStream.Length;
                LightmapSizes.Add((int)bw.BaseStream.Length - LightmapOffsets[i]);
                bw.Write(new byte[Padding(4096)]);
                BSPSizes.Add((int)bw.BaseStream.Length - BSPOffsets[i]);
                bw.BaseStream.Position = BSPOffsets[i];
                bw.Write(BSPSizes[i]);
                bw.Write(BSPOffsets[i] + 16 + Magic);
                bw.Write(LightmapOffsets[i] + Magic);
                bw.Write("psbs".ToCharArray());
                bw.Write(0);
                bw.BaseStream.Position = bw.BaseStream.Length;

                // UI
                progress.Text = (Convert.ToInt32(progress.Text) + 1).ToString();
                Application.DoEvents();
            }
            #endregion

            #region Compile SID Tables
            // UI
            progress.Properties.Maximum = StringIds.Count * 3;
            progress.Text = "0";
            lblStatus.Text = "Writing String Id Tables...";
            Application.DoEvents();

            // String128
            Header.Script128Offset = (int)bw.BaseStream.Length;
            Header.ScriptCount = StringIds.Count;
            for (int i = 0; i < StringIds.Count; i++)
            {
                // UI
                progress.Text = i.ToString();
                Application.DoEvents();

                // Write String
                bw.Write(StringIds[i].ToCharArray());
                bw.Write(new byte[128 - StringIds[i].Length]);
            }

            // Padding
            bw.Write(new byte[Padding(512)]);

            // StringId Index
            int SidOffset = 0;
            Header.ScriptIndexOffset = (int)bw.BaseStream.Length;
            for (int i = 0; i < StringIds.Count; i++)
            {
                // UI
                progress.Text = (Convert.ToInt32(progress.Text) + 1).ToString();
                Application.DoEvents();

                // Write
                if (i == 0)
                    bw.Write(0);
                SidOffset += StringIds[i].Length + 1;
                bw.Write(SidOffset);
            }

            // Padding
            bw.Write(new byte[Padding(512)]);

            // StringId Table
            Header.ScriptTableOffset = (int)bw.BaseStream.Length;
            for (int i = 0; i < StringIds.Count; i++)
            {

                // UI
                progress.Text = (Convert.ToInt32(progress.Text) + 1).ToString();
                Application.DoEvents();

                // Write
                bw.Write(StringIds[i].ToCharArray());
                bw.Write((byte)0);
            }
            Header.ScriptTableSize = (int)bw.BaseStream.Length - Header.ScriptTableOffset;

            // Padding
            bw.Write(new byte[Padding(512)]);
            #endregion

            #region Compile File Table
            // UI
            progress.Properties.Maximum = p.Tags.Count * 2;
            progress.Text = "0";
            lblStatus.Text = "Compiling File Table...";
            Application.DoEvents();

            // File Table
            Header.FileTableOffset = (int)bw.BaseStream.Length;
            Header.TagCount = p.Tags.Count;
            for (int i = 0; i < p.Tags.Count; i++)
            {
                // UI
                progress.Text = i.ToString();
                Application.DoEvents();

                // Write
                bw.Write(p.Tags[i].Remove(p.Tags[i].LastIndexOf(".")).ToCharArray());
                bw.Write((byte)0);
            }
            Header.FileTableSize = (int)bw.BaseStream.Length - Header.FileTableOffset;

            // Header
            bw.Write(new byte[Padding(512)]);

            // File Table Index
            int TagOffset = 0;
            Header.FileTableIndexOffset = (int)bw.BaseStream.Length;
            for (int i = 0; i < p.Tags.Count; i++)
            {
                // UI
                progress.Text = (Convert.ToInt32(progress.Text) + 1).ToString();
                Application.DoEvents();

                // Write
                bw.Write(TagOffset);
                if (i != p.Tags.Count - 1)
                    TagOffset += p.Tags[i].Remove(p.Tags[i].LastIndexOf(".")).Length + 1;
            }
            
            // Padding
            bw.Write(new byte[Padding(512)]);
            #endregion

            #region Compile Unicode Tables
            ms = new FileStream(p.Directory + "strings.unicode_string_list", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
            mr = new BinaryReader(ms);
            mw = new BinaryWriter(ms);

            // UI
            lblStatus.Text = "Compiling Unicode Tables...";
            progress.Properties.Maximum = StringCount[0] * 7;
            progress.Text = "0";
            Application.DoEvents();

            // Read SIDs
            List<string> Links = new List<string>();
            mr.BaseStream.Position = StringTable;
            for (int i = 0; i < StringIdCount; i++)
            {
                char a; string str = "";
                while ((a = mr.ReadChar()) != '\0')
                    str += a;
                Links.Add(str);
            }
            mr.BaseStream.Position = 40;

            // Data
            for (int i = 0; i < 8; i++)
            {
                // Set Offsets
                Unicode.Languages[i] = new UnicLang();
                Unicode.Languages[i].IndexOffset = (int)bw.BaseStream.Length;
                Unicode.Languages[i].StringCount = StringCount[i];

                // Write Index
                for (int x = 0; x < StringCount[i]; x++)
                {
                    // Read and Relink SID
                    int SID = mr.ReadInt16();
                    mr.BaseStream.Position += 2;
                    int Offset = mr.ReadInt32();

                    // Write
                    SID = StringIds.IndexOf(Links[SID]);
                    if (SID == -1) SID = 0;
                    bw.Write((short)SID);
                    bw.Write(new byte[] { (byte)0, (byte)StringIds[SID].Length });
                    bw.Write(Offset);
                }

                // Padding
                bw.Write(new byte[Padding(512)]);

                // Write Strings
                Unicode.Languages[i].TableOffset = (int)bw.BaseStream.Length;
                for (int x = 0; x < StringCount[i]; x++)
                {
                    // UI
                    progress.Text = (Convert.ToInt32(progress.Text) + 1).ToString();
                    Application.DoEvents();

                    byte b;
                    while ((b = mr.ReadByte()) != 0)
                        bw.Write(b);
                    bw.Write((byte)0);
                }
                Unicode.Languages[i].TableSize = (int)bw.BaseStream.Length - Unicode.Languages[i].TableOffset;

                // Padding
                bw.Write(new byte[Padding(512)]);
            }
            ms.Close();
            mr.Close();
            mw.Close();
            #endregion

            #region Write Crazy Block

            ms = new FileStream(p.Directory + "crazy.bin", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
            mr = new BinaryReader(ms);
            mw = new BinaryWriter(ms);

            // UI
            lblStatus.Text = "Writing Crazy...";
            progress.Text = "0";
            Application.DoEvents();

            // Store Values
            Header.CrazyOffset = (int)bw.BaseStream.Length;
            Header.CrazySize = (int)mr.BaseStream.Length;

            // Write
            bw.Write(mr.ReadBytes((int)mr.BaseStream.Length));

            // Padding
            Pad = Padding(512);
            bw.Write(new byte[Pad]);

            // Close
            mr.Close();
            mw.Close();
            ms.Close();

            #endregion

            #region Bitmap Raw
            // UI
            //Header.CrazyOffset = (int)bw.BaseStream.Length;
            lblStatus.Text = "Writing Bitmap Raw";
            progress.Properties.Maximum = p.Tags.Count;
            progress.Text = "0";
            Application.DoEvents();

            // Data
            for (int i = 0; i < p.Tags.Count; i++)
            {
                // UI
                progress.Text = i.ToString();
                Application.DoEvents();

                // Data
                if (p.Tags[i].EndsWith(".bitmap"))
                {
                    // UI
                    lblStatus.Text = "Writing Bitmap Raw: " + p.Tags[i];
                    Application.DoEvents();

                    // Data
                    ms = new FileStream(p.Directory + p.Tags[i], FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
                    mr = new BinaryReader(ms);
                    mw = new BinaryWriter(ms);
                    rr = new BinaryReader(new FileStream(p.Directory + p.Tags[i].Replace(".bitmap", ".bitmapraw"), FileMode.Open, FileAccess.Read, FileShare.Read));

                    mr.BaseStream.Position = 512 + 68;
                    r = new Reflexive();
                    r.Read(mr, 0);
                    for (int x = 0; x < r.ChunkCount; x++)
                    {
                        int[] Offsets = new int[3];
                        int[] Sizes = new int[3];
                        mr.BaseStream.Position = r.Translation + (x * 116) + 28;
                        Offsets[0] = mr.ReadInt32();
                        Offsets[1] = mr.ReadInt32();
                        Offsets[2] = mr.ReadInt32();
                        mr.BaseStream.Position += 12;
                        Sizes[0] = mr.ReadInt32();
                        Sizes[1] = mr.ReadInt32();
                        Sizes[2] = mr.ReadInt32();

                        // Write
                        for (int z = 0; z < 3; z++)
                        {
                            if ((Offsets[z] & 0xC0000000) == 0 && Sizes[z] > 0)
                            {
                                // Write Pointers
                                mw.BaseStream.Position = r.Translation + (x * 116) + 28 + (z * 4);
                                mw.Write((int)bw.BaseStream.Length);

                                // Write Data and Padding
                                rr.BaseStream.Position = Offsets[z];
                                byte[] Buffer = rr.ReadBytes(Sizes[z]);
                                bw.Write(Buffer);
                                Pad = Padding(512);
                                bw.Write(new byte[Pad]);
                            }
                        }
                    }
                    ms.Close();
                    mr.Close();
                    mw.Close();
                    rr.Close();
                }
            }
            #endregion

            #region Compile Index
            // UI
            lblStatus.Text = "Writing Index Header...";
            Application.DoEvents();

            #region Compile Index Header
            Header.IndexOffset = (int)bw.BaseStream.Length;
            Index.PrimaryMagic = Constant - Header.IndexOffset - 32; //Constant - (Header.IndexOffset + 32)
            bw.Write(Constant);
            bw.Write(118);
            Index.TagStartModifier = (Header.IndexOffset + 32 + 1416) + Index.PrimaryMagic;
            bw.Write(Index.TagStartModifier);
            bw.Write(FirstIdent + (65537 * 3));
            bw.Write(FirstIdent);
            bw.Write(0);
            bw.Write(p.Tags.Count);
            bw.Write("sgat".ToCharArray());
            #endregion

            #region Compile Tag List
            for (int i = 0; i < Classes.Length; i++)
            {
                // Write Class
                bw.Write(Classes[i].ToCharArray());

                // Find Parents
                string Parent = FindParent(Classes[i]);
                if (Parent != "")
                {
                    bw.Write(Parent.ToCharArray());
                    Parent = FindParent(Parent);
                    if (Parent != "")
                        bw.Write(Parent.ToCharArray());
                    else
                        bw.Write(-1);
                }
                else
                {
                    bw.Write(-1);
                    bw.Write(-1);
                }
            }
            #endregion

            #region Object Index
            Index.SecondaryMagic = Index.PrimaryMagic + BSPSizes[0];
            Index.TagStart = (int)bw.BaseStream.Length;
            bw.Write(new byte[p.Tags.Count * 16]);
            Pad = Padding(512);
            bw.Write(new byte[Pad]);
            Header.IndexSize = (int)bw.BaseStream.Length - Header.IndexOffset;
            #endregion
            #endregion

            #region Compile Metas
            // UI
            lblStatus.Text = "Compiling: ";
            progress.Properties.Maximum = p.Tags.Count;
            progress.Text = "0";
            Application.DoEvents();

            // Compile all the tags
            for (int i = 0; i < p.Tags.Count; i++)
            {
                // Check
                if (p.Tags[i].EndsWith(".scenario_structure_bsp") || p.Tags[i].EndsWith(".lightmap"))
                {
                    Tag sbsp = new Tag();
                    sbsp.Class = p.Tags[i].EndsWith(".scenario_structure_bsp") ? "sbsp" : "ltmp";
                    sbsp.Offset = 0;
                    sbsp.Size = 0;
                    sbsp.Ident = Idents[i];
                    Tags.Add(sbsp);
                }
                else
                {
                    // UI
                    lblStatus.Text = "Compiling: " + p.Tags[i];
                    progress.Text = i.ToString();
                    Application.DoEvents();

                    // Compile Tag
                    ms = new FileStream(p.Directory + p.Tags[i], FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
                    mr = new BinaryReader(ms);
                    mw = new BinaryWriter(ms);

                    // Read
                    Plugin tag = new Plugin(p.Tags[i].Substring(p.Tags[i].LastIndexOf(".") + 1), null);
                    tag.Layout.Read(mr);

                    // Set Some Values before we Write
                    if (tag.Layout.CompiledClass == "matg")
                    {
                        // Write new Unicode Pointers
                        for (int x = 0; x < 9; x++)
                        {
                            // Save
                            tag.Layout.Fields[30 + (x * 6)].SetValue(x == 8 ? 0 : Unicode.Languages[x].StringCount);
                            tag.Layout.Fields[31 + (x * 6)].SetValue(x == 8 ? 0 : Unicode.Languages[x].TableSize);
                            tag.Layout.Fields[32 + (x * 6)].SetValue(x == 8 ? Header.CrazyOffset : Unicode.Languages[x].IndexOffset);
                            tag.Layout.Fields[33 + (x * 6)].SetValue(x == 8 ? Header.CrazyOffset : Unicode.Languages[x].TableOffset);
                        }
                    }
                    else if (tag.Layout.CompiledClass == "scnr")
                    {
                        for (int x = 0; x < BSPOffsets.Count; x++)
                        {
                            // Set Values
                            ((HaloObjects.Reflexive)tag.Layout.Fields[61]).ChangeIndex(x);
                            MetaNode[] Fields = (MetaNode[])((HaloObjects.Reflexive)tag.Layout.Fields[61]).GetValue();
                            Fields[0].SetValue(BSPOffsets[x]);
                            Fields[1].SetValue(BSPSizes[x]);
                            Fields[2].SetValue(Constant - 32 + Header.IndexSize); //Constant - Header.IndexSize);
                            ((HaloObjects.Reflexive)tag.Layout.Fields[61]).SetValue(Fields);
                        }
                    }

                    // Save Info
                    Tag t = new Tag();
                    t.Class = tag.Layout.CompiledClass;
                    bw.BaseStream.Position = bw.BaseStream.Length;
                    t.Offset = (int)bw.BaseStream.Length;
                    t.Ident = Idents[i];

                    // Compile with Padding
                    tag.Layout.Compile(bw, Index.SecondaryMagic);
                    bw.BaseStream.Position = bw.BaseStream.Length;
                    bw.Write(new byte[Padding(4)]);
                    t.Size = (int)bw.BaseStream.Length - t.Offset;
                    Tags.Add(t);

                    // Close
                    ms.Close();
                    mr.Close();
                    mw.Close();
                }
            }

            // Padding
            Pad = Padding(4096);
            for (int i = 0; i < Pad; i++)
                bw.Write((byte)0xCD);

            Header.MetaTableSize = (int)bw.BaseStream.Length - (Header.IndexOffset + Header.IndexSize);
            #endregion

            #region Finish Object Index
            // UI
            lblStatus.Text = "Writing Object Index...";
            progress.Text = "0";
            Application.DoEvents();

            // Write
            bw.BaseStream.Position = Index.TagStart;
            for (int i = 0; i < Tags.Count; i++)
            {
                // UI
                progress.Text = i.ToString();
                Application.DoEvents();

                // Data
                bw.Write(Microsoft.VisualBasic.Strings.StrReverse(Tags[i].Class).ToCharArray());
                bw.Write(Tags[i].Ident);
                bw.Write(Tags[i].Offset == 0 ? 0 : Tags[i].Offset + Index.SecondaryMagic);
                bw.Write(Tags[i].Size);
            }
            #endregion

            #region Write Header
            // UI
            lblStatus.Text = "Writing Header...";
            Application.DoEvents();

            // Write
            bw.BaseStream.Position = 0;
            bw.Write("daeh".ToCharArray());
            bw.Write(8);
            bw.Write((int)bw.BaseStream.Length);
            bw.BaseStream.Position += 4;
            bw.Write(Header.IndexOffset);
            bw.Write(Header.IndexSize);
            bw.Write(Header.MetaTableSize);
            int metasize = (int)(br.BaseStream.Length - (Header.IndexOffset + Header.IndexSize));
            Header.NonRawSize = metasize + Header.IndexSize + BSPSizes[0];
            bw.Write(Header.NonRawSize);
            bw.BaseStream.Position = 288;
            bw.Write("02.09.27.09809".ToCharArray());
            bw.Write(new byte[18]);
            bw.Write(1);
            bw.BaseStream.Position += 20;
            bw.Write(Header.CrazyOffset);
            bw.Write(Header.CrazySize);
            bw.Write(Header.Script128Offset);
            bw.Write(Header.ScriptCount);
            bw.Write(Header.ScriptTableSize);
            bw.Write(Header.ScriptIndexOffset);
            bw.Write(Header.ScriptTableOffset);
            bw.BaseStream.Position = 408;
            bw.Write(p.Name.ToCharArray());
            bw.Write(new byte[36 - p.Name.Length]);
            bw.Write(p.Tags[3].ToCharArray());
            bw.Write(new byte[256 - p.Tags[3].Length]);
            bw.BaseStream.Position += 4;
            bw.Write(Header.TagCount);
            bw.Write(Header.FileTableOffset);
            bw.Write(Header.FileTableSize);
            bw.Write(Header.FileTableIndexOffset);
            bw.BaseStream.Position = 2044;
            bw.Write("toof".ToCharArray());

            // Sign
            br.BaseStream.Position = 2048;
            int times = ((int)br.BaseStream.Length - 2048) / 4;
            for (int i = 0; i < times; i++)
            {
                Header.MapChecksum ^= br.ReadInt32();
            }

            // Finish
            bw.BaseStream.Position = 720;
            bw.Write(Header.MapChecksum);
            #endregion

            // Close
            fs.Close();
            br.Close();
            bw.Close();
        }

        public void Close()
        {
            br.Close();
            bw.Close();
            fs.Close();
        }

        public void Read(TreeView tv)
        {
            Header.Read(this);
            Index.Read(this);
            StringId.Read(this);
            TagIndex.Read(this, tv);
            Coconuts.Read(this);
            Unicode.Read(this);
            ReadBSP();
            ReadSettings();
        }

        public void ReadSettings()
        {
            try
            {
                XmlTextReader xr = new XmlTextReader(Application.StartupPath + "\\Settings.xml");
                while (xr.Read())
                {
                    switch (xr.Name)
                    {
                        case "MainMenu":
                            {
                                MainMenu = xr.ReadElementString();
                                Shared = xr.ReadElementString();
                                SinglePlayerShared = xr.ReadElementString();
                                break;
                            }
                    }
                }
                xr.Close();
            }
            catch { }
        }

        public void ReadBSP()
        {
            br.BaseStream.Position = Tags[3].Offset + 528;
            Reflexive r = new Reflexive();
            r.Read(this, true);
            for (int x = 0; x < r.ChunkCount; x++)
            {
                br.BaseStream.Position = r.Translation + (x * 68);
                BSPBlock b = new BSPBlock();
                b.BSPOffset = br.ReadInt32();
                b.BSPSize = br.ReadInt32();
                b.MagicBaseAddress = br.ReadInt32(); //-2146496512
                b.Magic = b.MagicBaseAddress - b.BSPOffset;
                br.BaseStream.Position += 8;
                b.BSPIdent = br.ReadInt32();
                int BspIndex = (int)(b.BSPIdent & 0xFFFF);
                Tags[BspIndex].Offset = b.BSPOffset;
                Tags[BspIndex].Size = b.BSPSize;

                br.BaseStream.Position += 4;
                b.LightmapIdent = br.ReadInt32();
                br.BaseStream.Position = b.BSPOffset + 8;
                b.LightmapOffset = br.ReadInt32() + -b.Magic;
                b.LightmapSize = b.BSPOffset + b.BSPSize - b.LightmapOffset;
                int LtmpIndex = (int)(b.LightmapIdent & 0xFFFF);
                Tags[LtmpIndex].Offset = b.LightmapOffset;
                Tags[LtmpIndex].Size = b.LightmapSize;
                BSPs.Add(b);
            }
        }

        public static RawLocation RawLocator(ref int Offset)
        {
            long Map = Offset & 0xC0000000;
            Offset &= 0x3FFFFFFF;
            switch (Map)
            {
                case 0:
                    {
                        return RawLocation.Internal;
                    }
                case 0x80000000:
                    {
                        return RawLocation.Shared;
                    }
                case 0xC0000000:
                    {
                        return RawLocation.SingleplayerShared;
                    }
                case 0x40000000:
                    {
                        return RawLocation.Mainmenu;
                    }
            }
            return RawLocation.Unknown;
        }

        public void Decompile(string Location, Label status, DevExpress.XtraEditors.ProgressBarControl bar, DevExpress.XtraEditors.ProgressBarControl bar2)
        {
            //Add to Globals
            for (int i = 0; i < Tags.Count; i++)
            {
                try
                {
                    Handler.GetPlugin(Tags[i].Class);
                    Globals.Tags.Add(Tags[i].Path + "." + Handler.DefaultName);
                    Globals.Idents.Add(Tags[i].Ident);
                }
                catch { }
            }
            Globals.Strings.AddRange(SIDs.ToArray());

            //Create our main project file
            StreamWriter Proj = new StreamWriter(Location + "\\" + Header.MapName.TrimEnd('\0') + ".h2project");

            #region Decompile Unicode Tables
            BinaryWriter uw = new BinaryWriter(new FileStream(Location + "\\strings.unicode_string_list", FileMode.Create, FileAccess.Write, FileShare.Write));

            // Compile SID List
            List<string> StringIds = new List<string>();
            for (int i = 0; i < Unicode.Languages[0].StringCount; i++)
            {
                if (!StringIds.Contains(Unicode.Languages[0].Strings[i].Link))
                {
                    StringIds.Add(Unicode.Languages[0].Strings[i].Link);
                }
            }

            // Write Header
            uw.Write(StringIds.Count);
            uw.Write(Unicode.Languages[0].StringCount);
            uw.Write(Unicode.Languages[1].StringCount);
            uw.Write(Unicode.Languages[2].StringCount);
            uw.Write(Unicode.Languages[3].StringCount);
            uw.Write(Unicode.Languages[4].StringCount);
            uw.Write(Unicode.Languages[5].StringCount);
            uw.Write(Unicode.Languages[6].StringCount);
            uw.Write(Unicode.Languages[7].StringCount);
            uw.Write(0);

            // Write Unicode Data
            for (int i = 0; i < 8; i++)
            {
                // Write Index Data
                for (int x = 0; x < Unicode.Languages[i].StringCount; x++)
                {
                    uw.Write((short)StringIds.IndexOf(Unicode.Languages[i].Strings[x].Link));
                    uw.Write((byte)0);
                    uw.Write((byte)Unicode.Languages[i].Strings[x].Link.Length);
                    uw.Write(Unicode.Languages[i].Strings[x].Offset);
                }

                // Write Table Data
                for (int x = 0; x < Unicode.Languages[i].StringCount; x++)
                {
                    uw.Write(Unicode.Languages[i].Strings[x].String);
                    uw.Write((byte)0);
                }
            }

            // Finish
            uw.BaseStream.Position = 36;
            uw.Write((int)uw.BaseStream.Length);

            // Write Sids
            uw.BaseStream.Position = uw.BaseStream.Length;
            for (int i = 0; i < StringIds.Count; i++)
            {
                uw.Write(StringIds[i].ToCharArray());
                uw.Write((byte)0);
            }

            uw.Close();
            #endregion

            #region Decompile Crazy Block

            BinaryWriter cw = new BinaryWriter(new FileStream(Location + "\\crazy.bin", FileMode.Create, FileAccess.Write, FileShare.Write));

            // Write
            br.BaseStream.Position = Header.CrazyOffset;
            cw.Write(br.ReadBytes(Header.CrazySize));

            // Close
            cw.Close();

            #endregion

            #region Write every Tag
            StreamWriter T = new StreamWriter(Location + "\\Tags.lst");
            for (int i = 0; i < Tags.Count; i++)
            {
                //Create Sub-Directorys
                int Pointer = Tags[i].Path.LastIndexOf("\\") + 1;
                string SubPath = "\\" + Tags[i].Path.Substring(0, Pointer);
                Directory.CreateDirectory(Location + SubPath);

                //Name of file
                string TempName = Tags[i].Path.Substring(Pointer, Tags[i].Path.Length - Pointer);
                string TempTagName = SubPath + TempName; // +"." + Tag.Class;
                TempTagName = TempTagName.Replace('*', '!');
                TempTagName = TempTagName.Replace("<", "_");
                TempTagName = TempTagName.Replace(">", "_");
                TempTagName = TempTagName.Replace('/', '_');

                //Update
                bar.Text = i.ToString();
                bar2.Text = i.ToString();
                status.Text = Tags[i].Path + "." + Tags[i].Class;
                Application.DoEvents();

                //Save the Tag
                Extract(Tags[i], Location + TempTagName);

                //Add to Tags list
                T.WriteLine(Tags[i].Path + "." + Handler.DefaultName);
            }
            T.Close();

            //Add to project
            Proj.WriteLine(Location + "\\Tags.lst");
            Proj.WriteLine(Location + "\\strings.unicode_string_list");
            #endregion

            // Finish up
            Proj.Close();
        }

        public void Extract(Tag tag, string Location)
        {
            //Clear
            Globals.TagIdents.Clear();
            Globals.TagStringIds.Clear();
            Globals.TagReferences.Clear();
            Globals.TagStrings.Clear();

            //Get Plugin
            Handler.GetPlugin(tag.Class);
            Plugin p = new Plugin(Handler.DefaultName, null);
            if (p.Layout == null)
                return;

            //Write Decompiled Tag
            BinaryWriter writer = new BinaryWriter(new FileStream(Location + "." + Handler.DefaultName, FileMode.Create, FileAccess.Write, FileShare.Write));

            //Tag
            if (tag.Class == "sbsp")
                p.Layout.Decompile(br, bw, writer, FindBSP(tag.Ident), tag.Class, BSPs[FindBSPIndex(tag.Ident)].BSPOffset);
            else if (tag.Class == "ltmp")
            {
                try
                {
                    p.Layout.Decompile(br, bw, writer, FindBSP(tag.Ident), tag.Class, BSPs[FindBSPIndex(tag.Ident)].LightmapOffset);
                }
                catch (ArgumentOutOfRangeException) { }
            }
            else
                p.Layout.Decompile(br, bw, writer, Index.SecondaryMagic, tag.Class, tag.Offset);

            //Finally
            writer.Close();
        }

        public int FindBSP(int Ident)
        {
            for (int i = 0; i < BSPs.Count; i++)
            {
                if (Ident == BSPs[i].BSPIdent || Ident == BSPs[i].LightmapIdent)
                {
                    return BSPs[i].Magic;
                }
            }
            return -1;
        }

        public int FindBSPIndex(int Ident)
        {
            for (int i = 0; i < BSPs.Count; i++)
            {
                if (Ident == BSPs[i].BSPIdent || Ident == BSPs[i].LightmapIdent)
                {
                    return i;
                }
            }
            return -1;
        }

        public int FindIndex(int Ident)
        {
            for (int i = 0; i < Tags.Count; i++)
            {
                if (Tags[i].Ident == Ident)
                {
                    return i;
                }
            }
            return -1;
        }

        public BinaryReader OpenMap(RawLocation Type)
        {
            string Location = "";
            if (Type == RawLocation.Shared)
            {
                Location = Shared;
            }
            else if (Type == RawLocation.Mainmenu)
            {
                Location = MainMenu;
            }
            else if (Type == RawLocation.SingleplayerShared)
            {
                Location = SinglePlayerShared;
            }
            BinaryReader nbr = new BinaryReader(new FileStream(Location, FileMode.Open, FileAccess.Read, FileShare.Read));
            return nbr;
        }

        public uint CreateIdent(int i)
        {
            uint index = (uint)i;
            uint salt = (uint)(SaltStart + (ushort)i);
            uint id = index | salt << 16;

            // Byte Flip
            byte[] bytes = BitConverter.GetBytes(id);
            byte[] newbytes = { bytes[0], bytes[1], bytes[2], bytes[3] };

            return BitConverter.ToUInt32(newbytes, 0);
        }

        public int Padding(int Size)
        {
            int pad = Size - ((int)bw.BaseStream.Position % Size);
            if (pad == Size)
                pad = 0;
            return pad;
        }

        public string FindParent(string Class)
        {
            string ret = "";
            switch (Microsoft.VisualBasic.Strings.StrReverse(Class))
            {
                case "bipd":
                case "vehi":
                    ret = "unit";
                    break;
                case "bloc":
                case "crea":
                case "devi":
                case "item":
                case "proj":
                case "scen":
                case "ssce":
                case "unit":
                    ret = "obje";
                    break;
                case "ctrl":
                case "lifi":
                case "mach":
                    ret = "devi";
                    break;
                case "eqip":
                case "garb":
                case "weap":
                    ret = "item";
                    break;
            }
            return Microsoft.VisualBasic.Strings.StrReverse(ret);
        }
    }
}
