示例#1
0
文件: BSA.cs 项目: clayne/BSA_Browser
        protected override void Open(string filePath)
        {
            try
            {
                this.Magic = this.BinaryReader.ReadUInt32();

                if (this.Magic == MW_HEADER_MAGIC) // Morrowind uses this as version
                {
                    var header = new BSAHeaderMW(this.BinaryReader, this.Magic);
                    this.Header         = header;
                    this.FileCount      = (int)header.FileCount;
                    this.Files.Capacity = this.FileCount;
                    uint dataOffset = BSAHeaderMW.Size + header.HashOffset + header.FileCount * 8; // 8 = filename hash size

                    // Store file sizes and offsets
                    for (int i = 0; i < header.FileCount; i++)
                    {
                        uint size   = this.BinaryReader.ReadUInt32();
                        uint offset = this.BinaryReader.ReadUInt32() + dataOffset;

                        this.Files.Add(new BSAFileEntry(this, offset.ToString(), offset, size));
                        this.Files[i].Index = i;
                    }

                    // Check if archive has name offset and name table, for example for Xbox
                    this.BinaryReader.BaseStream.Position = BSAHeaderMW.Size + header.FileCount * 8; // Skip header and entries, 8 = file entry size

                    // First name offset should be 0 if there is one, otherwise it doesn't have one
                    hasNameTableMW = this.BinaryReader.ReadUInt32() == 0;

                    if (hasNameTableMW)
                    {
                        this.BinaryReader.BaseStream.Position = BSAHeaderMW.Size + header.FileCount * 12; // Seek to name table
                    }
                    else
                    {
                        this.BinaryReader.BaseStream.Position -= 4; // Go Back
                    }
                    for (int i = 0; i < header.FileCount; i++)
                    {
                        if (hasNameTableMW)
                        {
                            this.Files[i].FullPath = this.BinaryReader.ReadStringTo('\0');
                        }
                        else
                        {
                            this.Files[i].FullPath = string.Format("{0:X}", this.BinaryReader.ReadUInt64());
                        }

                        this.Files[i].FullPathOriginal = this.Files[i].FullPath;
                    }
                }
                else if (this.Magic == BSA_HEADER_MAGIC)
                {
                    var header = new BSAHeader(this.BinaryReader);

                    this.Header         = header;
                    this.FileCount      = (int)header.FileCount;
                    this.Files.Capacity = this.FileCount;

                    int[] numfiles = new int[header.FolderCount];
                    for (int i = 0; i < header.FolderCount; i++)
                    {
                        // Skip hash
                        this.BinaryReader.BaseStream.Position += 8;
                        // Read fileCount
                        numfiles[i] = this.BinaryReader.ReadInt32();
                        // Skip Unk1 + offset (4 + 8 bytes) if SSE. Otherwise only offset (4 bytes)
                        this.BinaryReader.BaseStream.Position += header.Version == SSE_HEADER_VERSION ? 12 : 4;
                    }

                    for (int i = 0; i < header.FolderCount; i++)
                    {
                        string folder = BinaryReader.ReadString(BinaryReader.ReadByte() - 1);
                        this.BinaryReader.BaseStream.Position++;

                        for (int j = 0; j < numfiles[i]; j++)
                        {
                            // Skip hash
                            this.BinaryReader.BaseStream.Position += 8;
                            uint size   = this.BinaryReader.ReadUInt32();
                            uint offset = this.BinaryReader.ReadUInt32();
                            bool comp   = this.Compressed;

                            if ((size & (1 << 30)) != 0)
                            {
                                comp  = !comp;
                                size ^= 1 << 30;
                            }

                            this.Files.Add(new BSAFileEntry(this, comp, folder, offset, size));
                            this.Files[j].Index = j;
                        }
                    }

                    // Grab the uncompressed file size before each data block
                    if (this.RetrieveRealSize)
                    {
                        // Save the position so we can go back after to the name table
                        long pos = this.BinaryReader.BaseStream.Position;

                        for (int i = 0; i < header.FileCount; i++)
                        {
                            var entry = this.Files[i] as BSAFileEntry;

                            if (!entry.Compressed)
                            {
                                continue;
                            }

                            this.BinaryReader.BaseStream.Position = (long)entry.Offset;

                            if (this.ContainsFileNameBlobs)
                            {
                                this.BinaryReader.BaseStream.Position += this.BinaryReader.ReadByte() + 1;
                            }

                            entry.RealSize = this.BinaryReader.ReadUInt32();
                        }

                        this.BinaryReader.BaseStream.Position = pos;
                    }

                    // Read name table
                    for (int i = 0; i < header.FileCount; i++)
                    {
                        this.Files[i].FullPath = Path.Combine(
                            this.Files[i].FullPath,
                            this.BinaryReader.ReadStringTo('\0'));
                        this.Files[i].FullPathOriginal = this.Files[i].FullPath;
                    }
                }
                else
                {
                    // Assume it's a Fallout 2 DAT
                    this.BinaryReader.BaseStream.Position = this.BinaryReader.BaseStream.Length - 8;
                    uint treeSize = this.BinaryReader.ReadUInt32();
                    uint dataSize = this.BinaryReader.ReadUInt32();

                    if (dataSize != this.BinaryReader.BaseStream.Length)
                    {
                        this.BinaryReader.Close();
                        throw new ArgumentException("File is not a valid bsa archive.", nameof(filePath));
                    }

                    this.BinaryReader.BaseStream.Position = dataSize - treeSize - 8;
                    this.FileCount      = this.BinaryReader.ReadInt32();
                    this.Files.Capacity = this.FileCount;

                    for (int i = 0; i < this.FileCount; i++)
                    {
                        var entry = new DAT2FileEntry(this.BinaryReader);

                        this.Files.Add(new BSAFileEntry(this, entry));
                        this.Files[i].Index = i;
                    }
                }
            }
            catch (Exception)
            {
                this.BinaryReader?.Close();
                throw;
            }
        }
示例#2
0
        protected override void Open(string filePath)
        {
            try
            {
                this.Magic = this.BinaryReader.ReadUInt32();

                if (this.Magic == MW_HEADER_MAGIC) // Morrowind uses this as version
                {
                    var header = new BSAHeaderMW(this.BinaryReader, this.Magic);
                    this.Header    = header;
                    this.FileCount = (int)header.FileCount;
                    uint dataOffset = 12 + header.HashOffset + header.FileCount * 8;

                    for (int i = 0; i < header.FileCount; i++)
                    {
                        uint size   = this.BinaryReader.ReadUInt32();
                        uint offset = this.BinaryReader.ReadUInt32() + dataOffset;

                        this.Files.Add(new BSAFileEntry(this, offset.ToString(), offset, size));
                    }

                    // Seek to name table
                    this.BinaryReader.BaseStream.Position = 12 + header.FileCount * 12;

                    for (int i = 0; i < header.FileCount; i++)
                    {
                        this.Files[i].FullPath = this.BinaryReader.ReadStringTo('\0');
                    }
                }
                else if (this.Magic == BSA_HEADER_MAGIC)
                {
                    var header = new BSAHeader(this.BinaryReader);

                    this.Header    = header;
                    this.FileCount = (int)header.FileCount;

                    int[] numfiles = new int[header.FolderCount];
                    for (int i = 0; i < header.FolderCount; i++)
                    {
                        // Skip hash
                        this.BinaryReader.BaseStream.Position += 8;
                        // Read fileCount
                        numfiles[i] = this.BinaryReader.ReadInt32();
                        // Skip Unk1 + offset (4 + 8 bytes) if SSE. Otherwise only offset (4 bytes)
                        this.BinaryReader.BaseStream.Position += header.Version == SSE_HEADER_VERSION ? 12 : 4;
                    }

                    for (int i = 0; i < header.FolderCount; i++)
                    {
                        string folder = BinaryReader.ReadString(BinaryReader.ReadByte() - 1);
                        this.BinaryReader.BaseStream.Position++;

                        for (int j = 0; j < numfiles[i]; j++)
                        {
                            // Skip hash
                            this.BinaryReader.BaseStream.Position += 8;
                            uint size   = this.BinaryReader.ReadUInt32();
                            uint offset = this.BinaryReader.ReadUInt32();
                            bool comp   = this.Compressed;

                            if ((size & (1 << 30)) != 0)
                            {
                                comp  = !comp;
                                size ^= 1 << 30;
                            }

                            this.Files.Add(new BSAFileEntry(this, comp, folder, offset, size));
                        }
                    }

                    // Read name table
                    for (int i = 0; i < header.FileCount; i++)
                    {
                        this.Files[i].FullPath = Path.Combine(
                            this.Files[i].FullPath,
                            this.BinaryReader.ReadStringTo('\0'));
                    }
                }
                else
                {
                    // Assume it's a Fallout 2 DAT
                    this.BinaryReader.BaseStream.Position = this.BinaryReader.BaseStream.Length - 8;
                    uint treeSize = this.BinaryReader.ReadUInt32();
                    uint dataSize = this.BinaryReader.ReadUInt32();

                    if (dataSize != this.BinaryReader.BaseStream.Length)
                    {
                        this.BinaryReader.Close();
                        throw new ArgumentException("File is not a valid bsa archive.", nameof(filePath));
                    }

                    this.BinaryReader.BaseStream.Position = dataSize - treeSize - 8;
                    this.FileCount = this.BinaryReader.ReadInt32();

                    for (int i = 0; i < this.FileCount; i++)
                    {
                        var entry = new DAT2FileEntry(this.BinaryReader);

                        this.Files.Add(new BSAFileEntry(this, entry));
                    }
                }
            }
            catch (Exception ex)
            {
                this.BinaryReader?.Close();

                throw new Exception("An error occured trying to open the archive.", ex);
            }
        }