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)); } }