Пример #1
0
        void ReadMetadata()
        {
            // Open
            Magic = _r.ReadUInt32();
            if (Magic == F4_BSAHEADER_FILEID)
            {
                Version = _r.ReadUInt32();
                if (Version != F4_BSAHEADER_VERSION)
                {
                    throw new InvalidOperationException("BAD MAGIC");
                }
                // Read the header
                var header_Type            = _r.ReadASCIIString(4); // 08 GNRL=General, DX10=Textures
                var header_NumFiles        = _r.ReadUInt32();       // 0C
                var header_NameTableOffset = _r.ReadUInt64();       // 10 - relative to start of file
                // Create file metadatas
                _r.Position = (long)header_NameTableOffset;
                _files      = new FileMetadata[header_NumFiles];
                for (var i = 0; i < header_NumFiles; i++)
                {
                    var length = _r.ReadUInt16();
                    var path   = _r.ReadASCIIString(length).Replace('\\', '/');
                    _files[i] = new FileMetadata
                    {
                        Path     = path,
                        PathHash = Tes4HashFilePath(path),
                    };
                }
                if (header_Type == "GNRL") // General BA2 Format
                {
                    _r.Position = 16 + 8;  // sizeof(header) + 8
                    for (var i = 0; i < header_NumFiles; i++)
                    {
                        var info_NameHash     = _r.ReadUInt32();       // 00
                        var info_Ext          = _r.ReadASCIIString(4); // 04 - extension
                        var info_DirHash      = _r.ReadUInt32();       // 08
                        var info_Unk0C        = _r.ReadUInt32();       // 0C - flags? 00100100
                        var info_Offset       = _r.ReadUInt64();       // 10 - relative to start of file
                        var info_PackedSize   = _r.ReadUInt32();       // 18 - packed length (zlib)
                        var info_UnpackedSize = _r.ReadUInt32();       // 1C - unpacked length
                        var info_Unk20        = _r.ReadUInt32();       // 20 - BAADF00D
                        _files[i].PackedSize   = info_PackedSize;
                        _files[i].UnpackedSize = info_UnpackedSize;
                        _files[i].Offset       = (long)info_Offset;
                    }
                }
                else if (header_Type == "DX10") // Texture BA2 Format
                {
                    _r.Position = 16 + 8;       // sizeof(header) + 8
                    for (var i = 0; i < header_NumFiles; i++)
                    {
                        var fileMetadata         = _files[i];
                        var info_NameHash        = _r.ReadUInt32();       // 00
                        var info_Ext             = _r.ReadASCIIString(4); // 04
                        var info_DirHash         = _r.ReadUInt32();       // 08
                        var info_Unk0C           = _r.ReadByte();         // 0C
                        var info_NumChunks       = _r.ReadByte();         // 0D
                        var info_ChunkHeaderSize = _r.ReadUInt16();       // 0E - size of one chunk header
                        var info_Height          = _r.ReadUInt16();       // 10
                        var info_Width           = _r.ReadUInt16();       // 12
                        var info_NumMips         = _r.ReadByte();         // 14
                        var info_Format          = _r.ReadByte();         // 15 - DXGI_FORMAT
                        var info_Unk16           = _r.ReadUInt16();       // 16 - 0800
                        // read tex-chunks
                        var texChunks = new F4TexChunk[info_NumChunks];
                        for (var j = 0; j < info_NumChunks; j++)
                        {
                            texChunks[j] = new F4TexChunk
                            {
                                Offset       = _r.ReadUInt64(),   // 00
                                PackedSize   = _r.ReadUInt32(),   // 08
                                UnpackedSize = _r.ReadUInt32(),   // 0C
                                StartMip     = _r.ReadUInt16(),   // 10
                                EndMip       = _r.ReadUInt16(),   // 12
                                Unk14        = _r.ReadUInt32(),   // 14 - BAADFOOD
                            }
                        }
                        ;
                        var firstChunk = texChunks[0];
                        _files[i].PackedSize   = firstChunk.PackedSize;
                        _files[i].UnpackedSize = firstChunk.UnpackedSize;
                        _files[i].Offset       = (long)firstChunk.Offset;
                        fileMetadata.Tex       = new F4Tex
                        {
                            Height  = info_Height,
                            Width   = info_Width,
                            NumMips = info_NumMips,
                            Format  = (DXGIFormat)info_Format,
                            Unk16   = info_Unk16,
                            Chunks  = texChunks,
                        };
                    }
                }
            }
            else if (Magic == OB_BSAHEADER_FILEID)
            {
                Version = _r.ReadUInt32();
                if (Version != OB_BSAHEADER_VERSION && Version != F3_BSAHEADER_VERSION && Version != SSE_BSAHEADER_VERSION)
                {
                    throw new InvalidOperationException("BAD MAGIC");
                }
                // Read the header
                var header_FolderRecordOffset = _r.ReadUInt32(); // Offset of beginning of folder records
                var header_ArchiveFlags       = _r.ReadUInt32(); // Archive flags
                var header_FolderCount        = _r.ReadUInt32(); // Total number of folder records (OBBSAFolderInfo)
                var header_FileCount          = _r.ReadUInt32(); // Total number of file records (OBBSAFileInfo)
                var header_FolderNameLength   = _r.ReadUInt32(); // Total length of folder names
                var header_FileNameLength     = _r.ReadUInt32(); // Total length of file names
                var header_FileFlags          = _r.ReadUInt32(); // File flags

                // Calculate some useful values
                if ((header_ArchiveFlags & OB_BSAARCHIVE_PATHNAMES) == 0 || (header_ArchiveFlags & OB_BSAARCHIVE_FILENAMES) == 0)
                {
                    throw new InvalidOperationException("HEADER FLAGS");
                }
                _compressToggle = (header_ArchiveFlags & OB_BSAARCHIVE_COMPRESSFILES) != 0;
                if (Version == F3_BSAHEADER_VERSION || Version == SSE_BSAHEADER_VERSION)
                {
                    _hasNamePrefix = (header_ArchiveFlags & F3_BSAARCHIVE_PREFIXFULLFILENAMES) != 0;
                }
                var folderSize = Version != SSE_BSAHEADER_VERSION ? 16 : 24;

                // Create file metadatas
                _files = new FileMetadata[header_FileCount];
                var filenamesSectionStartPos = _r.Position = header_FolderRecordOffset + header_FolderNameLength + header_FolderCount * (folderSize + 1) + header_FileCount * 16;
                var buf = new List <byte>(64);
                for (var i = 0; i < header_FileCount; i++)
                {
                    buf.Clear();
                    byte curCharAsByte; while ((curCharAsByte = _r.ReadByte()) != 0)
                    {
                        buf.Add(curCharAsByte);
                    }
                    var path = Encoding.ASCII.GetString(buf.ToArray()).Replace('\\', '/');
                    _files[i] = new FileMetadata
                    {
                        Path = path,
                    };
                }
                if (_r.Position != filenamesSectionStartPos + header_FileNameLength)
                {
                    throw new InvalidOperationException("HEADER FILENAMES");
                }

                // read-all folders
                _r.Position = header_FolderRecordOffset;
                var foldersFiles = new uint[header_FolderCount];
                for (var i = 0; i < header_FolderCount; i++)
                {
                    var folder_Hash = _r.ReadUInt64();      // Hash of the folder name
                    var folder_FileCount = _r.ReadUInt32(); // Number of files in folder
                    var folder_Unk = 0U; var folder_Offset = 0UL;
                    if (Version == SSE_BSAHEADER_VERSION)
                    {
                        folder_Unk = _r.ReadUInt32(); folder_Offset = _r.ReadUInt64();
                    }
                    else
                    {
                        folder_Offset = _r.ReadUInt32();
                    }
                    foldersFiles[i] = folder_FileCount;
                }

                // add file
                var fileNameIndex = 0U;
                for (var i = 0; i < header_FolderCount; i++)
                {
                    var folder_name = _r.ReadASCIIString(_r.ReadByte(), ASCIIFormat.PossiblyNullTerminated).Replace('\\', '/'); // BSAReadSizedString
                    var folderFiles = foldersFiles[i];
                    for (var j = 0; j < folderFiles; j++)
                    {
                        var file_Hash      = _r.ReadUInt64(); // Hash of the filename
                        var file_SizeFlags = _r.ReadUInt32(); // Size of the data, possibly with OB_BSAFILE_FLAG_COMPRESS set
                        var file_Offset    = _r.ReadUInt32(); // Offset to raw file data
                        var fileMetadata   = _files[fileNameIndex++];
                        fileMetadata.SizeFlags = file_SizeFlags;
                        fileMetadata.Offset    = file_Offset;
                        fileMetadata.Path      = folder_name + "/" + fileMetadata.Path;
                        fileMetadata.PathHash  = Tes4HashFilePath(fileMetadata.Path);
                    }
                }
            }
            else if (Magic == MW_BSAHEADER_FILEID)
            {
                // Read the header
                var header_HashOffset = _r.ReadUInt32(); // Offset of hash table minus header size (12)
                var header_FileCount  = _r.ReadUInt32(); // Number of files in the archive

                // Calculate some useful values
                var headerSize             = _r.Position;
                var hashTablePosition      = headerSize + header_HashOffset;
                var fileDataSectionPostion = hashTablePosition + (8 * header_FileCount);

                // Create file metadatas
                _files = new FileMetadata[header_FileCount];
                for (var i = 0; i < header_FileCount; i++)
                {
                    _files[i] = new FileMetadata
                    {
                        // Read file sizes/offsets
                        SizeFlags = _r.ReadUInt32(),
                        Offset    = fileDataSectionPostion + _r.ReadUInt32(),
                    }
                }
                ;

                // Read filename offsets
                var filenameOffsets = new uint[header_FileCount]; // relative offset in filenames section
                for (var i = 0; i < header_FileCount; i++)
                {
                    filenameOffsets[i] = _r.ReadUInt32();
                }

                // Read filenames
                var filenamesSectionStartPos = _r.Position;
                var buf = new List <byte>(64);
                for (var i = 0; i < header_FileCount; i++)
                {
                    _r.Position = filenamesSectionStartPos + filenameOffsets[i];
                    buf.Clear();
                    byte curCharAsByte; while ((curCharAsByte = _r.ReadByte()) != 0)
                    {
                        buf.Add(curCharAsByte);
                    }
                    _files[i].Path = Encoding.ASCII.GetString(buf.ToArray()).Replace('\\', '/');
                }

                // Read filename hashes
                _r.Position = hashTablePosition;
                for (var i = 0; i < header_FileCount; i++)
                {
                    //_files[i].PathHash = _r.ReadUInt64();
                    _files[i].PathHash = Tes3HashFilePath(_files[i].Path);
                }
            }
            else
            {
                throw new InvalidOperationException("BAD MAGIC");
            }

            // Create the file metadata hash table
            _filesByHash = _files.ToLookup(x => x.PathHash);
        }

        ulong HashFilePath(string filePath)
        {
            if (Magic == MW_BSAHEADER_FILEID)
            {
                return(Tes3HashFilePath(filePath));
            }
            else
            {
                return(Tes4HashFilePath(filePath));
            }
        }