2020-02-19 23:50:12 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Text;
|
2020-04-17 03:52:19 +00:00
|
|
|
|
using System.Threading.Tasks;
|
2020-03-23 12:57:18 +00:00
|
|
|
|
using Wabbajack.Common;
|
2020-04-06 22:02:01 +00:00
|
|
|
|
using Wabbajack.Common.Serialization.Json;
|
2020-08-11 15:28:25 +00:00
|
|
|
|
#nullable disable
|
2020-02-19 23:50:12 +00:00
|
|
|
|
|
2020-06-27 17:04:59 +00:00
|
|
|
|
namespace Compression.BSA
|
2020-02-19 23:50:12 +00:00
|
|
|
|
{
|
|
|
|
|
public class TES3Reader : IBSAReader
|
|
|
|
|
{
|
|
|
|
|
public static string TES3_MAGIC = Encoding.ASCII.GetString(new byte[] {0, 1, 0, 0});
|
|
|
|
|
private uint _versionNumber;
|
|
|
|
|
private uint _hashTableOffset;
|
|
|
|
|
private uint _fileCount;
|
|
|
|
|
private TES3FileEntry[] _files;
|
|
|
|
|
internal long _dataOffset;
|
2020-09-05 14:01:32 +00:00
|
|
|
|
public IStreamFactory _streamFactory;
|
2020-02-19 23:50:12 +00:00
|
|
|
|
|
2020-09-05 14:01:32 +00:00
|
|
|
|
public static async ValueTask<TES3Reader> Load(IStreamFactory factory)
|
2020-02-19 23:50:12 +00:00
|
|
|
|
{
|
2020-09-05 14:01:32 +00:00
|
|
|
|
await using var fs = await factory.GetStream();
|
2020-02-19 23:50:12 +00:00
|
|
|
|
using var br = new BinaryReader(fs);
|
2020-05-25 16:24:16 +00:00
|
|
|
|
var rdr = new TES3Reader
|
|
|
|
|
{
|
2020-09-05 14:01:32 +00:00
|
|
|
|
_streamFactory = factory,
|
2020-05-25 16:24:16 +00:00
|
|
|
|
_versionNumber = br.ReadUInt32(),
|
|
|
|
|
_hashTableOffset = br.ReadUInt32(),
|
|
|
|
|
_fileCount = br.ReadUInt32()
|
|
|
|
|
};
|
2020-02-19 23:50:12 +00:00
|
|
|
|
|
2020-05-25 16:24:16 +00:00
|
|
|
|
rdr._files = new TES3FileEntry[rdr._fileCount];
|
|
|
|
|
for (int i = 0; i < rdr._fileCount; i++)
|
2020-02-19 23:50:12 +00:00
|
|
|
|
{
|
|
|
|
|
var file = new TES3FileEntry {
|
|
|
|
|
Index = i,
|
2020-05-25 16:24:16 +00:00
|
|
|
|
Archive = rdr,
|
2020-02-19 23:50:12 +00:00
|
|
|
|
Size = br.ReadUInt32(),
|
|
|
|
|
Offset = br.ReadUInt32()
|
|
|
|
|
|
|
|
|
|
};
|
2020-05-25 16:24:16 +00:00
|
|
|
|
rdr._files[i] = file;
|
2020-02-19 23:50:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-25 16:24:16 +00:00
|
|
|
|
for (int i = 0; i < rdr._fileCount; i++)
|
2020-02-19 23:50:12 +00:00
|
|
|
|
{
|
2020-05-25 16:24:16 +00:00
|
|
|
|
rdr._files[i].NameOffset = br.ReadUInt32();
|
2020-02-19 23:50:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var origPos = br.BaseStream.Position;
|
2020-05-25 16:24:16 +00:00
|
|
|
|
for (int i = 0; i < rdr._fileCount; i++)
|
2020-02-19 23:50:12 +00:00
|
|
|
|
{
|
2020-05-25 16:24:16 +00:00
|
|
|
|
br.BaseStream.Position = origPos + rdr._files[i].NameOffset;
|
|
|
|
|
rdr._files[i].Path = new RelativePath(br.ReadStringTerm(VersionType.TES3));
|
2020-02-19 23:50:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-25 16:24:16 +00:00
|
|
|
|
br.BaseStream.Position = rdr._hashTableOffset + 12;
|
|
|
|
|
for (int i = 0; i < rdr._fileCount; i++)
|
2020-02-19 23:50:12 +00:00
|
|
|
|
{
|
2020-05-25 16:24:16 +00:00
|
|
|
|
rdr._files[i].Hash1 = br.ReadUInt32();
|
|
|
|
|
rdr._files[i].Hash2 = br.ReadUInt32();
|
2020-02-19 23:50:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-25 16:24:16 +00:00
|
|
|
|
rdr._dataOffset = br.BaseStream.Position;
|
|
|
|
|
return rdr;
|
2020-02-19 23:50:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-17 03:52:19 +00:00
|
|
|
|
public async ValueTask DisposeAsync()
|
2020-02-19 23:50:12 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IEnumerable<IFile> Files => _files;
|
|
|
|
|
public ArchiveStateObject State
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return new TES3ArchiveState
|
|
|
|
|
{
|
|
|
|
|
FileCount = _fileCount,
|
|
|
|
|
DataOffset = _dataOffset,
|
|
|
|
|
HashOffset = _hashTableOffset,
|
|
|
|
|
VersionNumber = _versionNumber,
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-02 20:15:36 +00:00
|
|
|
|
|
|
|
|
|
public void Dump(Action<string> print)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
2020-02-19 23:50:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-06 22:02:01 +00:00
|
|
|
|
[JsonName("TES3Archive")]
|
2020-02-19 23:50:12 +00:00
|
|
|
|
public class TES3ArchiveState : ArchiveStateObject
|
|
|
|
|
{
|
|
|
|
|
public uint FileCount { get; set; }
|
|
|
|
|
public long DataOffset { get; set; }
|
|
|
|
|
public uint HashOffset { get; set; }
|
|
|
|
|
public uint VersionNumber { get; set; }
|
|
|
|
|
|
2020-07-20 01:19:56 +00:00
|
|
|
|
public override async Task<IBSABuilder> MakeBuilder(long size)
|
2020-02-19 23:50:12 +00:00
|
|
|
|
{
|
|
|
|
|
return new TES3Builder(this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class TES3FileEntry : IFile
|
|
|
|
|
{
|
2020-03-23 12:57:18 +00:00
|
|
|
|
public RelativePath Path { get; set; }
|
2020-02-19 23:50:12 +00:00
|
|
|
|
public uint Size { get; set; }
|
|
|
|
|
public FileStateObject State =>
|
|
|
|
|
new TES3FileState
|
|
|
|
|
{
|
|
|
|
|
Index = Index,
|
|
|
|
|
Path = Path,
|
|
|
|
|
Size = Size,
|
|
|
|
|
Offset = Offset,
|
|
|
|
|
NameOffset = NameOffset,
|
|
|
|
|
Hash1 = Hash1,
|
|
|
|
|
Hash2 = Hash2
|
|
|
|
|
};
|
|
|
|
|
|
2020-05-25 16:24:16 +00:00
|
|
|
|
public async ValueTask CopyDataTo(Stream output)
|
2020-02-19 23:50:12 +00:00
|
|
|
|
{
|
2020-09-05 14:01:32 +00:00
|
|
|
|
await using var fs = await Archive._streamFactory.GetStream();
|
2020-02-19 23:50:12 +00:00
|
|
|
|
fs.Position = Archive._dataOffset + Offset;
|
2020-05-25 16:24:16 +00:00
|
|
|
|
await fs.CopyToLimitAsync(output, (int)Size);
|
2020-02-19 23:50:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-05 14:01:32 +00:00
|
|
|
|
public async ValueTask<IStreamFactory> GetStreamFactory()
|
|
|
|
|
{
|
|
|
|
|
var ms = new MemoryStream();
|
|
|
|
|
await CopyDataTo(ms);
|
|
|
|
|
ms.Position = 0;
|
|
|
|
|
return new MemoryStreamFactory(ms);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-05-02 20:15:36 +00:00
|
|
|
|
public void Dump(Action<string> print)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-05 14:01:32 +00:00
|
|
|
|
|
2020-02-19 23:50:12 +00:00
|
|
|
|
public uint Offset { get; set; }
|
|
|
|
|
public uint NameOffset { get; set; }
|
|
|
|
|
public uint Hash1 { get; set; }
|
|
|
|
|
public uint Hash2 { get; set; }
|
|
|
|
|
public TES3Reader Archive { get; set; }
|
|
|
|
|
public int Index { get; set; }
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-04-06 22:02:01 +00:00
|
|
|
|
[JsonName("TES3FileState")]
|
2020-02-19 23:50:12 +00:00
|
|
|
|
public class TES3FileState : FileStateObject
|
|
|
|
|
{
|
|
|
|
|
public uint Offset { get; set; }
|
|
|
|
|
public uint NameOffset { get; set; }
|
|
|
|
|
public uint Hash1 { get; set; }
|
|
|
|
|
public uint Hash2 { get; set; }
|
|
|
|
|
public uint Size { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|