public StringTableReader(Stream stream)
        {
            reader = new CustomBinaryReader(stream);

            uint count = reader.ReadUInt32();
            uint dataSize = reader.ReadUInt32();

            long dataPosition = 8 + count * 8;

            for (int i = 0; i < count; i++)
            {
                uint index = reader.ReadUInt32();
                uint offset = reader.ReadUInt32();
                table.Add(index, dataPosition + offset);
            }
        }
        protected override void DoOpen()
        {
            using (CustomBinaryReader reader = new CustomBinaryReader(new FileStream(ArchivePath, FileMode.Open, FileAccess.Read)))
            {
                uint signature = reader.ReadUInt32();
                if (signature != 0x58445442)
                {
                    throw new InvalidDataException("File is not BA2");
                }

                version = reader.ReadUInt32();
                if (version > 1)
                {
                    throw new InvalidDataException("Unsupported archive file version: " + version);
                }

                type = (ArchiveType)reader.ReadUInt32();
                if (type != ArchiveType.General)
                {
                    Log.Fine("Skipping archive file which is not the general purpose type.");
                    return;
                }

                long baseOffset = reader.BaseStream.Position;
                uint fileCount = reader.ReadUInt32();
                long fileNameTableOffset = reader.ReadInt64();

                FileInfo[] files = new FileInfo[fileCount];
                for (int i = 0; i < fileCount; i++)
                {
                    files[i] = new FileInfo()
                    {
                        NameHash = reader.ReadUInt32(),
                        Type = reader.ReadUInt32(),
                        DirectoryNameHash = reader.ReadUInt32(),
                        Unknown1 = reader.ReadUInt32(),
                        DataOffset = reader.ReadInt64(),
                        DataCompressedSize = reader.ReadUInt32(),
                        DataUncompressedSize = reader.ReadUInt32(),
                        Unknown2 = reader.ReadUInt32()
                    };
                }

                reader.BaseStream.Position = fileNameTableOffset;
                for (int i = 0; i < fileCount; i++)
                {
                    ushort length = reader.ReadUInt16();
                    string path = reader.ReadStringFixedLength(length).ToLower();

                    string dir = Path.GetDirectoryName(path).ToLower();
                    string filename = Path.GetFileName(path).ToLower();
                    if (!sorted.ContainsKey(dir))
                    {
                        sorted.Add(dir, new SortedDictionary<string, FileInfo>());
                    }
                    sorted[dir].Add(filename, files[i]);
                }

            }
        }
        protected override void DoOpen()
        {
            using (CustomBinaryReader reader = new CustomBinaryReader(new FileStream(ArchivePath, FileMode.Open, FileAccess.Read)))
            {
                uint signature = reader.ReadUInt32();
                if (signature != 0x415342)
                {
                    throw new InvalidDataException("File is not BSA");
                }

                uint version = reader.ReadUInt32();
                uint folderOffset = reader.ReadUInt32();
                flags = (ArchiveFlags)reader.ReadUInt32();
                uint folderCount = reader.ReadUInt32();
                uint fileCount = reader.ReadUInt32();
                uint totalFolderNameLength = reader.ReadUInt32();
                uint totalFileNameLength = reader.ReadUInt32();
                uint fileExtensions = reader.ReadUInt32();

                FolderInfo[] folders = new FolderInfo[(int)folderCount];

                // Read folders
                reader.BaseStream.Position = folderOffset;
                for (int i = 0; i < folderCount; i++)
                {
                    ulong hash = reader.ReadUInt64();
                    uint count = reader.ReadUInt32();
                    uint offset = reader.ReadUInt32() - totalFileNameLength;
                    folders[i] = new FolderInfo()
                    {
                        FileCount = count,
                        ContentOffset = offset
                    };
                }

                // Read folder content (name and files)
                foreach (var folder in folders)
                {
                    byte folderNameLength = reader.ReadByte();
                    folder.Path = Encoding.UTF8.GetString(reader.ReadBytes(folderNameLength - 1));
                    byte zero = reader.ReadByte();

                    folder.Files = new FileInfo[folder.FileCount];
                    for (int i = 0; i < folder.FileCount; i++)
                    {
                        ulong hash = reader.ReadUInt64();
                        uint size = reader.ReadUInt32();

                        bool compressed = flags.HasFlag(ArchiveFlags.DefaultCompressed);
                        if ((size & 0xf0000000) != 0)
                        {
                            size &= 0xfffffff;
                            compressed = !compressed;
                        }

                        uint offset = reader.ReadUInt32();
                        folder.Files[i] = new FileInfo()
                        {
                            Size = size,
                            DataOffset = offset,
                            IsCompressed = compressed
                        };
                    }
                }

                long total = fileCount;
                long loaded = 0;
                string filename = Path.GetFileName(ArchivePath);

                // Read file names
                foreach (var folder in folders)
                {
                    foreach (var file in folder.Files)
                    {
                        file.Filename = reader.ReadStringZeroTerminated();
                        loaded++;
                    }
                }

                // Convert to nested sorted dictionary for fast search
                for (int i = 0; i < folderCount; i++)
                {
                    var files = new SortedDictionary<string, FileInfo>();
                    for (int j = 0; j < folders[i].FileCount; j++)
                    {
                        files.Add(folders[i].Files[j].Filename, folders[i].Files[j]);
                    }
                    sorted.Add(folders[i].Path, files);
                }

                return;
            }
        }