示例#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
        private void OpenSSE()
        {
            // magic and version has been read, at position 8

            // read header
            BSAHeader header = BSAHeader.ReadFrom(this.BinaryReader);

            uint numFiles = header.FileCount;

            this.Compressed            = (header.ArchiveFlags & OB_BSAARCHIVE_COMPRESSFILES) > 0;
            this.ContainsFileNameBlobs = (header.ArchiveFlags & F3_BSAARCHIVE_PREFIXFULLFILENAMES) > 0;

            // Seek to start of Folders, then skip to filename table
            this.BinaryReader.BaseStream.Seek(header.FolderRecordOffset
                                              + header.FolderNameLength
                                              + header.FolderCount * (1 + BSAFolderInfo.SizeOf(Version))
                                              + header.FileCount * BSAFileInfo.SizeOf(),
                                              SeekOrigin.Begin);

            List <string> fileNames = new List <string>();

            for (int i = 0; i < header.FileCount; i++)
            {
                fileNames.Add(this.ReadStringTo(this.BinaryReader, '\0'));
            }

            this.BinaryReader.BaseStream.Seek(header.FolderRecordOffset, SeekOrigin.Begin);

            var folderInfos = new List <BSAFolderInfo>();

            for (int i = 0; i < header.FolderCount; i++)
            {
                var fi = BSAFolderInfo.ReadFrom(this.BinaryReader, this.Version);
                folderInfos.Add(fi);
            }

            int filenameIndex = 0;

            for (int i = 0; i < folderInfos.Count; i++)
            {
                int    len = this.BinaryReader.ReadByte();
                byte[] b   = this.BinaryReader.ReadBytes(len);

                folderInfos[i].FolderName = Encoding.Default.GetString(b, 0, b.Length - 1);

                List <BSAFileInfo> fileInfos = new List <BSAFileInfo>();
                while (fileInfos.Count < folderInfos[i].FileCount)
                {
                    var fi = BSAFileInfo.ReadFrom(this.BinaryReader);
                    fi.NamePrefix = this.ContainsFileNameBlobs;
                    fi.Compressed = this.Compressed;
                    fileInfos.Add(fi);
                }

                for (int j = 0; j < fileInfos.Count; j++)
                {
                    fileInfos[j].Name = fileNames[filenameIndex];
                    filenameIndex++;
                    var fe = new BSAFileEntry(this, i)
                             .Initialize(this.Compressed,
                                         folderInfos[i].FolderName,
                                         fileInfos[j].Offset,
                                         fileInfos[j].SizeFlags);
                    fe.FullPath = Path.Combine(fe.FullPath, fileInfos[j].Name);
                    fe.fileInfo = fileInfos[j];
                    this.Files.Add(fe);
                }

                folderInfos[i].Files.AddRange(fileInfos);
            }
        }
示例#3
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);
            }
        }