using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; using Wabbajack.Common; using Wabbajack.Compression.BSA.Interfaces; using Wabbajack.DTOs.BSA.ArchiveStates; using Wabbajack.DTOs.BSA.FileStates; namespace Wabbajack.Compression.BSA.TES3Archive; public class Builder : IBuilder { private readonly (TES3File state, Stream data)[] _files; private readonly TES3State _state; public Builder(TES3State state) { _state = state; _files = new (TES3File state, Stream data)[_state.FileCount]; } public ValueTask AddFile(AFile state, Stream src, CancellationToken token) { var tesState = (TES3File) state; _files[state.Index] = (tesState, src); return ValueTask.CompletedTask; } public async ValueTask Build(Stream file, CancellationToken token) { await using var bw = new BinaryWriter(file, Encoding.Default, true); bw.Write(_state.VersionNumber); bw.Write(_state.HashOffset); bw.Write(_state.FileCount); foreach (var (state, _) in _files) { bw.Write(state.Size); bw.Write(state.Offset); } foreach (var (state, _) in _files) bw.Write(state.NameOffset); var orgPos = bw.BaseStream.Position; foreach (var (state, _) in _files) { if (bw.BaseStream.Position != orgPos + state.NameOffset) throw new BSAException("Offsets don't match when writing TES3 BSA"); bw.Write(Encoding.ASCII.GetBytes((string) state.Path)); bw.Write((byte) 0); } bw.BaseStream.Position = _state.HashOffset + 12; foreach (var (state, _) in _files) { bw.Write(state.Hash1); bw.Write(state.Hash2); } if (bw.BaseStream.Position != _state.DataOffset) throw new InvalidDataException("Data offset doesn't match when writing TES3 BSA"); foreach (var (state, data) in _files) { bw.BaseStream.Position = _state.DataOffset + state.Offset; await data.CopyToWithStatusAsync(data.Length, bw.BaseStream, token); await data.DisposeAsync(); } } public ValueTask DisposeAsync() { return ValueTask.CompletedTask; } }