using System; using System.Buffers.Binary; using System.Collections.Generic; using System.IO; using System.Text; using NativeImport; using Wabbajack.Common; using File = Alphaleonis.Win32.Filesystem.File; namespace Compression.BSA { public class FolderRecord { internal readonly BSAReader BSA; private readonly ReadOnlyMemorySlice _data; internal Lazy _files; private ReadOnlyMemorySlice? _nameData; private int _prevFileCount; internal FileNameBlock FileNameBlock; private readonly Lazy _name; public int Index { get; } public string Name => _name.Value; internal FolderRecord(BSAReader bsa, ReadOnlyMemorySlice data, int index) { BSA = bsa; _data = data; Index = index; _name = new Lazy( () => _nameData.HasValue ? _nameData.Value.ReadStringTerm(BSA.HeaderType) : string.Empty, isThreadSafe: true); } private bool IsLongform => BSA.HeaderType == VersionType.SSE; public ulong Hash => BinaryPrimitives.ReadUInt64LittleEndian(_data); public uint FileCount => BinaryPrimitives.ReadUInt32LittleEndian(_data.Slice(0x8)); public uint Unknown => IsLongform ? BinaryPrimitives.ReadUInt32LittleEndian(_data.Slice(0xC)) : 0; public ulong Offset => IsLongform ? BinaryPrimitives.ReadUInt64LittleEndian(_data.Slice(0x10)) : BinaryPrimitives.ReadUInt32LittleEndian(_data.Slice(0xC)); public static int HeaderLength(VersionType version) { return version switch { VersionType.SSE => 0x18, _ => 0x10, }; } internal void ProcessFileRecordHeadersBlock(BinaryReader rdr, int fileCountTally) { _prevFileCount = fileCountTally; var totalFileLen = checked((int)(FileCount * FileRecord.HeaderLength)); ReadOnlyMemorySlice data; if (BSA.HasFolderNames) { var len = rdr.ReadByte(); data = rdr.ReadBytes(len + totalFileLen); _nameData = data.Slice(0, len); data = data.Slice(len); } else { data = rdr.ReadBytes(totalFileLen); } _files = new Lazy( isThreadSafe: true, valueFactory: () => ParseFileRecords(data)); } private FileRecord[] ParseFileRecords(ReadOnlyMemorySlice data) { var fileCount = FileCount; var ret = new FileRecord[fileCount]; for (var idx = 0; idx < fileCount; idx += 1) { var fileData = data.Slice(idx * FileRecord.HeaderLength, FileRecord.HeaderLength); ret[idx] = new FileRecord(this, fileData, idx, idx + _prevFileCount, FileNameBlock); } return ret; } } }