2020-08-11 12:15:03 +00:00
|
|
|
|
using System;
|
2020-08-11 13:41:30 +00:00
|
|
|
|
using System.Buffers.Binary;
|
2020-08-11 12:15:03 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
2020-08-11 13:41:30 +00:00
|
|
|
|
using Wabbajack.Common;
|
2020-08-11 12:15:03 +00:00
|
|
|
|
|
|
|
|
|
namespace Compression.BSA
|
|
|
|
|
{
|
2020-08-11 15:24:02 +00:00
|
|
|
|
public class FolderRecord : IFolder
|
2020-08-11 12:15:03 +00:00
|
|
|
|
{
|
2020-08-11 13:41:30 +00:00
|
|
|
|
internal readonly BSAReader BSA;
|
|
|
|
|
private readonly ReadOnlyMemorySlice<byte> _data;
|
2020-08-11 15:28:25 +00:00
|
|
|
|
internal Lazy<FileRecord[]> _files = null!;
|
2020-08-11 13:41:30 +00:00
|
|
|
|
private int _prevFileCount;
|
2020-08-11 15:28:25 +00:00
|
|
|
|
internal FileNameBlock FileNameBlock = null!;
|
2020-08-11 15:24:02 +00:00
|
|
|
|
internal int Index { get; }
|
2020-08-11 15:28:25 +00:00
|
|
|
|
public string? Name { get; private set; }
|
2020-08-11 12:15:03 +00:00
|
|
|
|
|
2020-08-11 15:24:02 +00:00
|
|
|
|
public IEnumerable<IFile> Files => _files.Value;
|
2020-08-11 12:15:03 +00:00
|
|
|
|
|
2020-08-11 13:41:30 +00:00
|
|
|
|
internal FolderRecord(BSAReader bsa, ReadOnlyMemorySlice<byte> data, int index)
|
|
|
|
|
{
|
|
|
|
|
BSA = bsa;
|
|
|
|
|
_data = data;
|
|
|
|
|
Index = index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool IsLongform => BSA.HeaderType == VersionType.SSE;
|
|
|
|
|
|
|
|
|
|
public ulong Hash => BinaryPrimitives.ReadUInt64LittleEndian(_data);
|
|
|
|
|
|
2020-08-11 15:24:02 +00:00
|
|
|
|
public int FileCount => checked((int)BinaryPrimitives.ReadUInt32LittleEndian(_data.Slice(0x8)));
|
2020-08-11 13:41:30 +00:00
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
};
|
|
|
|
|
}
|
2020-08-11 12:15:03 +00:00
|
|
|
|
|
2020-08-11 13:41:30 +00:00
|
|
|
|
internal void ProcessFileRecordHeadersBlock(BinaryReader rdr, int fileCountTally)
|
2020-08-11 12:15:03 +00:00
|
|
|
|
{
|
2020-08-11 13:41:30 +00:00
|
|
|
|
_prevFileCount = fileCountTally;
|
|
|
|
|
var totalFileLen = checked((int)(FileCount * FileRecord.HeaderLength));
|
|
|
|
|
|
|
|
|
|
ReadOnlyMemorySlice<byte> data;
|
|
|
|
|
if (BSA.HasFolderNames)
|
2020-08-11 12:15:03 +00:00
|
|
|
|
{
|
2020-08-11 13:41:30 +00:00
|
|
|
|
var len = rdr.ReadByte();
|
|
|
|
|
data = rdr.ReadBytes(len + totalFileLen);
|
2020-08-11 15:24:02 +00:00
|
|
|
|
Name = data.Slice(0, len).ReadStringTerm(BSA.HeaderType);
|
2020-08-11 13:41:30 +00:00
|
|
|
|
data = data.Slice(len);
|
2020-08-11 12:15:03 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-08-11 13:41:30 +00:00
|
|
|
|
data = rdr.ReadBytes(totalFileLen);
|
2020-08-11 12:15:03 +00:00
|
|
|
|
}
|
2020-08-11 13:41:30 +00:00
|
|
|
|
|
|
|
|
|
_files = new Lazy<FileRecord[]>(
|
|
|
|
|
isThreadSafe: true,
|
|
|
|
|
valueFactory: () => ParseFileRecords(data));
|
2020-08-11 12:15:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-11 13:41:30 +00:00
|
|
|
|
private FileRecord[] ParseFileRecords(ReadOnlyMemorySlice<byte> data)
|
2020-08-11 12:15:03 +00:00
|
|
|
|
{
|
2020-08-11 13:41:30 +00:00
|
|
|
|
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;
|
2020-08-11 12:15:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|