private Stream Decompress(FAR3Entry Entry)
        {
            m_Reader.ReadBytes(9);
            uint   CompressedSize = m_Reader.ReadUInt32();
            ushort CompressionID  = m_Reader.ReadUShort();

            if (CompressionID == 0xFB10)
            {
                byte[] Dummy            = m_Reader.ReadBytes(3);
                uint   DecompressedSize = (uint)((Dummy[0] << 0x10) | (Dummy[1] << 0x08) | +Dummy[2]);

                Decompresser Dec = new Decompresser();
                Dec.CompressedSize   = CompressedSize;
                Dec.DecompressedSize = DecompressedSize;

                byte[] DecompressedData = Dec.Decompress(m_Reader.ReadBytes((int)CompressedSize));

                MemoryStream MemStream = new MemoryStream(DecompressedData);
                MemStream.Seek(0, SeekOrigin.Begin);

                return(MemStream);
            }
            else
            {
                m_Reader.Seek(m_Reader.Position - 15);
                return(new MemoryStream(m_Reader.ReadBytes((int)Entry.DecompressedDataSize)));
            }
        }
        /// <summary>
        /// Reads all the entries in the archive into memory.
        /// </summary>
        /// <param name="ThrowException">Wether or not to throw an exception if the archive was not a FAR3. If false, function will return.</param>
        public bool ReadArchive(bool ThrowException)
        {
            m_FinishedReading.Reset();

            if (m_Reader == null)
            {
                m_Reader = new FileReader(m_Path, false);
            }

            lock (m_Reader)
            {
                ASCIIEncoding Enc         = new ASCIIEncoding();
                string        MagicNumber = Enc.GetString(m_Reader.ReadBytes(8));

                if (!MagicNumber.Equals("FAR!byAZ", StringComparison.InvariantCultureIgnoreCase))
                {
                    if (ThrowException)
                    {
                        throw new FAR3Exception("MagicNumber was wrong - FAR3Archive.cs!");
                    }
                    else
                    {
                        m_Reader.Close();
                        return(false);
                    }
                }

                m_Reader.ReadUInt32(); //Version.
                m_Reader.Seek((long)m_Reader.ReadUInt32());

                uint NumFiles = m_Reader.ReadUInt32();

                for (int i = 0; i < NumFiles; i++)
                {
                    FAR3Entry Entry = new FAR3Entry();
                    Entry.DecompressedDataSize = m_Reader.ReadUInt32();
                    byte[] Dummy = m_Reader.ReadBytes(3);
                    Entry.CompressedDataSize = (uint)((Dummy[0] << 0) | (Dummy[1] << 8) | (Dummy[2]) << 16);
                    m_Reader.ReadByte(); //Unknown.
                    Entry.DataOffset     = m_Reader.ReadUInt32();
                    Entry.Flags          = m_Reader.ReadUShort();
                    Entry.FileNameLength = m_Reader.ReadUShort();
                    Entry.TypeID         = m_Reader.ReadUInt32();
                    Entry.FileID         = m_Reader.ReadUInt32();
                    Entry.Filename       = Enc.GetString(m_Reader.ReadBytes(Entry.FileNameLength));

                    UniqueFileID ID = new UniqueFileID(Entry.TypeID, Entry.FileID);

                    if (!m_Entries.ContainsKey(ID.UniqueID))
                    {
                        m_Entries.AddOrUpdate(ID.UniqueID, Entry, (Key, ExistingValue) => ExistingValue = Entry);
                    }
                }
            }

            m_FinishedReading.Set();

            return(true);
        }
        /// <summary>
        /// Returns a FileID for a rogue file (file that isn't archived).
        /// </summary>
        /// <param name="Entry">The entry generated for the file.</param>
        /// <returns>A FileID (see RogueFileIDs enum in Database.cs)</returns>
        public static uint GetFileID(FAR3Entry Entry)
        {
            try
            {
                string[] Dirs = Path.GetDirectoryName(Entry.Filename).Split("\\".ToCharArray());
                string Filename = Dirs[1] + "_" + Path.GetFileName(Entry.Filename);
                Filename = Filename.Replace("-", "_");
                Filename = Filename.Substring(0, Filename.IndexOf("."));

                return (uint)Enum.Parse(typeof(RogueFileIDs), Filename);
            }
            catch (ArgumentException)
            {
                return (uint)Entry.GetHashCode();
            }
        }
        /// <summary>
        /// Returns an entry in this archive as a Stream instance.
        /// Throws a FAR3Exception if entry was not found.
        /// </summary>
        /// <param name="ID">ID of the entry to grab from archive.</param>
        /// <returns>The entry's data as a Stream instance.</returns>
        public Stream GrabEntry(ulong ID)
        {
            m_FinishedReading.WaitOne();

            if (!ContainsEntry(ID))
            {
                throw new FAR3Exception("Couldn't find entry - FAR3Archive.cs!");
            }

            FAR3Entry Entry = m_Entries[ID];

            lock (m_Reader)
            {
                m_Reader.Seek((long)Entry.DataOffset);

                switch (Entry.TypeID)
                {
                case 1:     //BMP
                case 2:     //TGA
                case 5:     //SKEL
                case 7:     //ANIM
                case 9:     //MESH
                case 11:    //BND
                case 12:    //APR
                case 13:    //OFT
                case 15:    //PO
                case 16:    //COL
                case 18:    //HAG
                case 20:    //JPG
                case 24:    //PNG
                    return(Decompress(Entry));

                case 14:     //PNG, uncompressed
                default:
                    MemoryStream MemStream = new MemoryStream(m_Reader.ReadBytes((int)Entry.DecompressedDataSize));
                    MemStream.Seek(0, SeekOrigin.Begin);
                    return(MemStream);
                }
            }
        }
        private Stream Decompress(FAR3Entry Entry)
        {
            m_Reader.ReadBytes(9);
            uint CompressedSize = m_Reader.ReadUInt32();
            ushort CompressionID = m_Reader.ReadUShort();

            if (CompressionID == 0xFB10)
            {
                byte[] Dummy = m_Reader.ReadBytes(3);
                uint DecompressedSize = (uint)((Dummy[0] << 0x10) | (Dummy[1] << 0x08) | +Dummy[2]);

                Decompresser Dec = new Decompresser();
                Dec.CompressedSize = CompressedSize;
                Dec.DecompressedSize = DecompressedSize;

                byte[] DecompressedData = Dec.Decompress(m_Reader.ReadBytes((int)CompressedSize));

                MemoryStream MemStream = new MemoryStream(DecompressedData);
                MemStream.Seek(0, SeekOrigin.Begin);

                return MemStream;
            }
            else
            {
                m_Reader.Seek(m_Reader.Position - 15);
                return new MemoryStream(m_Reader.ReadBytes((int)Entry.DecompressedDataSize));
            }
        }
        /// <summary>
        /// Reads all the entries in the archive into memory.
        /// </summary>
        /// <param name="ThrowException">Wether or not to throw an exception if the archive was not a FAR3. If false, function will return.</param>
        public bool ReadArchive(bool ThrowException)
        {
            m_FinishedReading.Reset();

            if (m_Reader == null)
                m_Reader = new FileReader(m_Path, false);

            lock (m_Reader)
            {
                ASCIIEncoding Enc = new ASCIIEncoding();
                string MagicNumber = Enc.GetString(m_Reader.ReadBytes(8));

                if (!MagicNumber.Equals("FAR!byAZ", StringComparison.InvariantCultureIgnoreCase))
                {
                    if (ThrowException)
                        throw new FAR3Exception("MagicNumber was wrong - FAR3Archive.cs!");
                    else
                    {
                        m_Reader.Close();
                        return false;
                    }
                }

                m_Reader.ReadUInt32(); //Version.
                m_Reader.Seek((long)m_Reader.ReadUInt32());

                uint NumFiles = m_Reader.ReadUInt32();

                for (int i = 0; i < NumFiles; i++)
                {
                    FAR3Entry Entry = new FAR3Entry();
                    Entry.DecompressedDataSize = m_Reader.ReadUInt32();
                    byte[] Dummy = m_Reader.ReadBytes(3);
                    Entry.CompressedDataSize = (uint)((Dummy[0] << 0) | (Dummy[1] << 8) | (Dummy[2]) << 16);
                    m_Reader.ReadByte(); //Unknown.
                    Entry.DataOffset = m_Reader.ReadUInt32();
                    Entry.Flags = m_Reader.ReadUShort();
                    Entry.FileNameLength = m_Reader.ReadUShort();
                    Entry.TypeID = m_Reader.ReadUInt32();
                    Entry.FileID = m_Reader.ReadUInt32();
                    Entry.Filename = Enc.GetString(m_Reader.ReadBytes(Entry.FileNameLength));

                    UniqueFileID ID = new UniqueFileID(Entry.TypeID, Entry.FileID);

                    if (!m_Entries.ContainsKey(ID.UniqueID))
                        m_Entries.AddOrUpdate(ID.UniqueID, Entry, (Key, ExistingValue) => ExistingValue = Entry);
                }
            }

            m_FinishedReading.Set();

            return true;
        }
        /// <summary>
        /// Adds files from a specified directory to a dictionary of entries.
        /// </summary>
        /// <param name="EntryDir">The directory to scan for entries.</param>
        /// <param name="Filetype">A fully qualified lowercase filetype to scan for (can be empty).</param>
        /// <param name="Entries">The Dictionary to add entries to.</param>
        private static void AddFilesFromDir(string EntryDir, string Filetype, ref Dictionary<FAR3Entry, string> Entries)
        {
            string[] Dirs = Directory.GetDirectories(EntryDir);

            foreach(string Dir in Dirs)
            {
                if (Filetype != "")
                {
                    if (Dir.Contains(Filetype))
                    {
                        string[] Files = Directory.GetFiles(Dir);
                        string[] SubDirs = Directory.GetDirectories(Dir);
                        foreach (string Fle in Files)
                        {
                            if (Fle.Contains(".dat"))
                            {
                                FAR3Archive Archive = new FAR3Archive(Fle);
                                Archive.ReadArchive(true);

                                foreach (FAR3Entry Entry in Archive.GrabAllEntries())
                                    Entries.Add(Entry, Fle.Replace(GlobalSettings.Default.StartupPath, ""));
                            }
                            else
                            {
                                FAR3Entry Entry = new FAR3Entry();
                                Entry.Filename = Fle.Replace(GlobalSettings.Default.StartupPath, "");
                                Entry.FileID = HelperFuncs.GetFileID(Entry);
                                Entry.TypeID = HelperFuncs.GetTypeID(Path.GetExtension(Fle));

                                HelperFuncs.CheckCollision(Entry.FileID, Entries);

                                //Ignore fonts to minimize the risk of ID collisions.
                                if (!Entry.Filename.Contains(".ttf"))
                                {
                                    if (!Entry.Filename.Contains(".ffn"))
                                        Entries.Add(Entry, Entry.Filename);
                                }
                            }
                        }

                        foreach (string SubDir in SubDirs)
                        {
                            Files = Directory.GetFiles(SubDir);
                            foreach (string SubFle in Files)
                            {
                                if (SubFle.Contains(".dat"))
                                {
                                    FAR3Archive Archive = new FAR3Archive(SubFle);
                                    Archive.ReadArchive(false);

                                    foreach (FAR3Entry Entry in Archive.GrabAllEntries())
                                        Entries.Add(Entry, SubFle.Replace(GlobalSettings.Default.StartupPath, ""));
                                }
                                else
                                {
                                    FAR3Entry Entry = new FAR3Entry();
                                    Entry.Filename = SubFle.Replace(GlobalSettings.Default.StartupPath, "");
                                    Entry.FileID = HelperFuncs.GetFileID(Entry);
                                    Entry.TypeID = HelperFuncs.GetTypeID(Path.GetExtension(SubFle));

                                    HelperFuncs.CheckCollision(Entry.FileID, Entries);

                                    //Ignore fonts to minimize the risk of ID collisions.
                                    if (!Entry.Filename.Contains(".ttf"))
                                    {
                                        if (!Entry.Filename.Contains(".ffn"))
                                            Entries.Add(Entry, Entry.Filename);
                                    }
                                }
                            }
                        }
                    }
                }
                else //Filetype was empty, so just add all filetypes found...
                {
                    string[] Files = Directory.GetFiles(Dir);
                    string[] SubDirs = Directory.GetDirectories(Dir);
                    foreach (string Fle in Files)
                    {
                        if (Fle.Contains(".dat"))
                        {
                            FAR3Archive Archive = new FAR3Archive(Fle);
                            Archive.ReadArchive(true);

                            foreach (FAR3Entry Entry in Archive.GrabAllEntries())
                                Entries.Add(Entry, Fle.Replace(GlobalSettings.Default.StartupPath, ""));
                        }
                        else
                        {
                            FAR3Entry Entry = new FAR3Entry();
                            Entry.Filename = Fle.Replace(GlobalSettings.Default.StartupPath, "");
                            Entry.FileID = HelperFuncs.GetFileID(Entry);
                            Entry.TypeID = HelperFuncs.GetTypeID(Path.GetExtension(Fle));

                            HelperFuncs.CheckCollision((ulong)(((ulong)Entry.FileID) << 32 | ((ulong)(Entry.TypeID >> 32))), Entries);

                            //Ignore fonts to minimize the risk of ID collisions.
                            if (!Entry.Filename.Contains(".ttf"))
                            {
                                if(!Entry.Filename.Contains(".ffn"))
                                    Entries.Add(Entry, Entry.Filename);
                            }
                        }
                    }

                    foreach (string SubDir in SubDirs)
                    {
                        Files = Directory.GetFiles(SubDir);
                        foreach (string SubFle in Files)
                        {
                            if (SubFle.Contains(".dat"))
                            {
                                FAR3Archive Archive = new FAR3Archive(SubFle);
                                Archive.ReadArchive(true);

                                foreach (FAR3Entry Entry in Archive.GrabAllEntries())
                                    Entries.Add(Entry, SubFle.Replace(GlobalSettings.Default.StartupPath, ""));
                            }
                            else
                            {
                                FAR3Entry Entry = new FAR3Entry();
                                Entry.Filename = SubFle.Replace(GlobalSettings.Default.StartupPath, "");
                                Entry.FileID = HelperFuncs.GetFileID(Entry);
                                Entry.TypeID = HelperFuncs.GetTypeID(Path.GetExtension(SubFle));

                                HelperFuncs.CheckCollision((ulong)(((ulong)Entry.FileID) << 32 | ((ulong)(Entry.TypeID >> 32))), Entries);

                                //Ignore fonts to minimize the risk of ID collisions.
                                if (!Entry.Filename.Contains(".ttf"))
                                {
                                    if (!Entry.Filename.Contains(".ffn"))
                                        Entries.Add(Entry, Entry.Filename);
                                }
                            }
                        }
                    }
                }
            }
        }