/// <summary>
        ///     Reads a generic Pokémon container from a Stream.
        ///     Those containers are the ones that starts with "GR", "MM", "AD" and so on...
        /// </summary>
        /// <param name="data">Stream with container data</param>
        /// <returns></returns>
        public static OContainer load(Stream data)
        {
            BinaryReader input = new BinaryReader(data);
            OContainer output = new OContainer();

            IOUtils.readString(input, 0, 2); //Magic
            ushort sectionCount = input.ReadUInt16();
            for (int i = 0; i < sectionCount; i++)
            {
                OContainer.fileEntry entry = new OContainer.fileEntry();

                data.Seek(4 + (i * 4), SeekOrigin.Begin);
                uint startOffset = input.ReadUInt32();
                uint endOffset = input.ReadUInt32();
                uint length = endOffset - startOffset;

                data.Seek(startOffset, SeekOrigin.Begin);
                byte[] buffer = new byte[length];
                input.Read(buffer, 0, (int)length);
                entry.data = buffer;

                output.content.Add(entry);
            }

            data.Close();

            return output;
        }
        private void BtnExport_Click(object sender, System.EventArgs e)
        {
            if (FileList.SelectedIndex == -1)
            {
                return;
            }
            using (SaveFileDialog saveDlg = new SaveFileDialog())
            {
                saveDlg.Title    = "Export file";
                saveDlg.FileName = container.content[FileList.SelectedIndex].name;
                saveDlg.Filter   = "All files|*.*";
                if (saveDlg.ShowDialog() == DialogResult.OK)
                {
                    OContainer.fileEntry file = container.content[FileList.SelectedIndex];

                    if (file.loadFromDisk)
                    {
                        byte[] buffer = new byte[file.fileLength];
                        container.data.Seek(file.fileOffset, SeekOrigin.Begin);
                        container.data.Read(buffer, 0, buffer.Length);
                        File.WriteAllBytes(saveDlg.FileName, buffer);
                    }
                    else
                    {
                        File.WriteAllBytes(saveDlg.FileName, file.data);
                    }
                }
            }
        }
        /// <summary>
        ///     Reads a DARC archive.
        /// </summary>
        /// <param name="data">Stream of the data</param>
        /// <returns>The container data</returns>
        public static OContainer load(Stream data)
        {
            OContainer output = new OContainer();
            BinaryReader input = new BinaryReader(data);

            string darcMagic = IOUtils.readStringWithLength(input, 4);
            ushort endian = input.ReadUInt16();
            ushort headerLength = input.ReadUInt16();
            uint version = input.ReadUInt32();
            uint fileSize = input.ReadUInt32();
            uint tableOffset = input.ReadUInt32();
            uint tableLength = input.ReadUInt32();
            uint dataOffset = input.ReadUInt32();

            data.Seek(tableOffset, SeekOrigin.Begin);
            fileEntry root = getEntry(input);
            int baseOffset = (int)data.Position;
            int namesOffset = (int)(tableOffset + root.length * 0xc);

            string currDir = null;
            for (int i = 0; i < root.length - 1; i++)
            {
                data.Seek(baseOffset + i * 0xc, SeekOrigin.Begin);

                fileEntry entry = getEntry(input);

                if ((entry.flags & 1) > 0)
                {
                    //Folder
                    int index = i;
                    currDir = null;
                    for (;;)
                    {
                        uint parentIndex = entry.offset;
                        currDir = getName(input, entry.nameOffset + namesOffset) + "/" + currDir;
                        if (parentIndex == 0 || parentIndex == index) break;
                        data.Seek(baseOffset + parentIndex * 0xc, SeekOrigin.Begin);
                        entry = getEntry(input);
                        index = (int)parentIndex;
                    }

                    continue;
                }

                data.Seek(entry.offset, SeekOrigin.Begin);
                byte[] buffer = new byte[entry.length];
                data.Read(buffer, 0, buffer.Length);

                OContainer.fileEntry file = new OContainer.fileEntry();
                file.name = currDir + getName(input, entry.nameOffset + namesOffset);
                file.data = buffer;

                output.content.Add(file);
            }

            data.Close();
            return output;
        }
        /// <summary>
        ///     Reads a GARC archive.
        /// </summary>
        /// <param name="data">Stream of the data</param>
        /// <returns>The container data</returns>
        public static OContainer load(Stream data)
        {
            OContainer output = new OContainer();
            BinaryReader input = new BinaryReader(data);

            output.data = data;

            string garcMagic = IOUtils.readStringWithLength(input, 4);
            uint garcHeaderLength = input.ReadUInt32();
            ushort endian = input.ReadUInt16();
            input.ReadUInt16(); //0x400
            uint sectionCount = input.ReadUInt32();
            uint dataOffset = input.ReadUInt32();
            uint decompressedLength = input.ReadUInt32();
            uint compressedLength = input.ReadUInt32();

            //File Allocation Table Offsets
            string fatoMagic = IOUtils.readStringWithLength(input, 4);
            uint fatoHeaderLength = input.ReadUInt32();
            ushort fatoEntries = input.ReadUInt16();
            input.ReadUInt16(); //0xffff = Padding?
            data.Seek(fatoEntries * 4, SeekOrigin.Current); //We don't need this

            string fatbMagic = IOUtils.readStringWithLength(input, 4);
            uint fatbHeaderLength = input.ReadUInt32();
            uint entries = input.ReadUInt32();

            long baseOffset = data.Position;

            for (int i = 0; i < entries; i++)
            {
                data.Seek(baseOffset + i * 0x10, SeekOrigin.Begin);

                uint flags = input.ReadUInt32();
                uint startOffset = input.ReadUInt32();
                uint endOffset = input.ReadUInt32();
                uint length = input.ReadUInt32();

                input.BaseStream.Seek(startOffset + dataOffset, SeekOrigin.Begin);
                byte[] buffer = new byte[Math.Min(0x10, length)];
                input.Read(buffer, 0, buffer.Length);

                bool isCompressed = buffer.Length > 0 ? buffer[0] == 0x11 : false;
                string extension = FileIO.getExtension(buffer, isCompressed ? 5 : 0);
                string name = string.Format("file_{0:D5}{1}", i, extension);

                //And add the file to the container list
                OContainer.fileEntry entry = new OContainer.fileEntry();
                entry.name = name;
                entry.loadFromDisk = true;
                entry.fileOffset = startOffset + dataOffset;
                entry.fileLength = length;
                output.content.Add(entry);
            }

            return output;
        }
        /// <summary>
        ///     Reads FPT0 containers from Dragon Quest VII.
        /// </summary>
        /// <param name="data">Stream with container data</param>
        /// <returns></returns>
        public static OContainer load(Stream data)
        {
            BinaryReader input = new BinaryReader(data);
            OContainer output = new OContainer();

            data.Seek(8, SeekOrigin.Begin);
            uint entries = input.ReadUInt32();
            uint baseAddress = 0x10 + (entries * 0x20) + 0x80;
            input.ReadUInt32();

            List<sectionEntry> files = new List<sectionEntry>();
            for (int i = 0; i < entries; i++)
            {
                sectionEntry entry = new sectionEntry();

                entry.name = IOUtils.readString(input, (uint)(0x10 + (i * 0x20)));
                data.Seek(0x20 + (i * 0x20), SeekOrigin.Begin);
                input.ReadUInt32(); //Memory address?
                entry.offset = input.ReadUInt32() + baseAddress;
                entry.length = input.ReadUInt32();
                input.ReadUInt32(); //Padding?

                files.Add(entry);
            }

            foreach (sectionEntry file in files)
            {
                OContainer.fileEntry entry = new OContainer.fileEntry();

                data.Seek(file.offset, SeekOrigin.Begin);
                byte[] buffer = new byte[file.length];
                input.Read(buffer, 0, buffer.Length);
                entry.data = buffer;
                entry.name = file.name;

                output.content.Add(entry);
            }

            data.Close();

            return output;
        }
        /// <summary>
        ///     Reads a SARC archive.
        /// </summary>
        /// <param name="data">Stream of the data</param>
        /// <returns>The container data</returns>
        public static OContainer load(Stream data)
        {
            OContainer output = new OContainer();
            BinaryReader input = new BinaryReader(data);

            string sarcMagic = IOUtils.readStringWithLength(input, 4);
            ushort sarcHeaderLength = input.ReadUInt16();
            ushort endian = input.ReadUInt16();
            uint fileLength = input.ReadUInt32();
            uint dataOffset = input.ReadUInt32();
            uint dataPadding = input.ReadUInt32();

            string sfatMagic = IOUtils.readStringWithLength(input, 4);
            ushort sfatHeaderLength = input.ReadUInt16();
            ushort entries = input.ReadUInt16();
            uint hashMultiplier = input.ReadUInt32();
            int sfntOffset = 0x20 + entries * 0x10 + 8;

            for (int i = 0; i < entries; i++)
            {
                data.Seek(0x20 + i * 0x10, SeekOrigin.Begin);

                uint nameHash = input.ReadUInt32();
                uint nameOffset = (input.ReadUInt32() & 0xffffff) << 2;
                uint offset = input.ReadUInt32();
                uint length = input.ReadUInt32() - offset;

                string name = IOUtils.readString(input, (uint)(sfntOffset + nameOffset));
                data.Seek(offset + dataOffset, SeekOrigin.Begin);
                byte[] buffer = new byte[length];
                data.Read(buffer, 0, buffer.Length);
                if (name == "") name = string.Format("file_{0:D5}{1}", i, FileIO.getExtension(buffer));

                OContainer.fileEntry entry = new OContainer.fileEntry();
                entry.name = name;
                entry.data = buffer;
                output.content.Add(entry);
            }

            data.Close();
            return output;
        }
        /// <summary>
        ///     Reads the Model PACKage from Dragon Quest VII.
        /// </summary>
        /// <param name="data">Stream of the data</param>
        /// <returns></returns>
        public static OContainer load(Stream data)
        {
            BinaryReader input = new BinaryReader(data);
            OContainer output = new OContainer();

            List<sectionEntry> mainSection = getSection(input);

            //World nodes section
            data.Seek(mainSection[0].offset, SeekOrigin.Begin);
            List<node> nodes = new List<node>();
            List<sectionEntry> worldNodesSection = getSection(input);
            foreach (sectionEntry entry in worldNodesSection)
            {
                data.Seek(entry.offset, SeekOrigin.Begin);

                node n = new node();

                //Geometry node
                input.ReadUInt32(); //GNOD magic number
                input.ReadUInt32();
                input.ReadUInt32();
                n.parentId = input.ReadInt32();
                n.name = IOUtils.readString(input, (uint)data.Position);

                data.Seek(entry.offset + 0x20, SeekOrigin.Begin);
                n.transform = new RenderBase.OMatrix();
                RenderBase.OVector4 t = new RenderBase.OVector4(input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                RenderBase.OVector4 r = new RenderBase.OVector4(input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                RenderBase.OVector4 s = new RenderBase.OVector4(input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
                n.transform *= RenderBase.OMatrix.scale(new RenderBase.OVector3(s.x, s.y, s.z));
                n.transform *= RenderBase.OMatrix.rotateX(r.x);
                n.transform *= RenderBase.OMatrix.rotateY(r.y);
                n.transform *= RenderBase.OMatrix.rotateZ(r.z);
                n.transform *= RenderBase.OMatrix.translate(new RenderBase.OVector3(t.x, t.y, t.z));

                nodes.Add(n);
            }

            RenderBase.OMatrix[] nodesTransform = new RenderBase.OMatrix[nodes.Count];
            for (int i = 0; i < nodes.Count; i++)
            {
                RenderBase.OMatrix transform = new RenderBase.OMatrix();
                transformNode(nodes, i, ref transform);
                nodesTransform[i] = transform;
            }

            //Models section
            data.Seek(mainSection[1].offset, SeekOrigin.Begin);
            List<sectionEntry> modelsSection = getSection(input);
            foreach (sectionEntry entry in modelsSection)
            {
                data.Seek(entry.offset, SeekOrigin.Begin);

                //Field Data section
                /*
                 * Usually have 3 entries.
                 * 1st entry: Model CGFX
                 * 2nd entry: Unknow CGFX, possibly animations
                 * 3rd entry: Another FieldData section, possibly child object
                 */

                List<sectionEntry> fieldDataSection = getSection(input);
                data.Seek(fieldDataSection[0].offset, SeekOrigin.Begin);
                uint length = fieldDataSection[0].length;
                while ((length & 0x7f) != 0) length++; //Align
                byte[] buffer = new byte[length];
                input.Read(buffer, 0, buffer.Length);

                OContainer.fileEntry file = new OContainer.fileEntry();
                file.name = CGFX.getName(new MemoryStream(buffer)) + ".bcmdl";
                file.data = buffer;

                output.content.Add(file);
            }

            //FILE section
            data.Seek(mainSection[2].offset, SeekOrigin.Begin);
            //TODO

            //Collision section
            data.Seek(mainSection[3].offset, SeekOrigin.Begin);
            //TODO

            //PARM(???) section
            data.Seek(mainSection[4].offset, SeekOrigin.Begin);
            //TODO

            //Textures CGFX
            data.Seek(mainSection[5].offset, SeekOrigin.Begin);
            byte[] texBuffer = new byte[mainSection[5].length];
            input.Read(texBuffer, 0, texBuffer.Length);

            OContainer.fileEntry texFile = new OContainer.fileEntry();
            texFile.name = "textures.bctex";
            texFile.data = texBuffer;

            output.content.Add(texFile);

            data.Close();

            return output;
        }