2021-09-27 12:42:46 +00:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.IO;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Text;
|
|
|
|
using System.Threading;
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
using Wabbajack.Compression.BSA.Interfaces;
|
|
|
|
using Wabbajack.DTOs.BSA.ArchiveStates;
|
|
|
|
using Wabbajack.DTOs.BSA.FileStates;
|
|
|
|
using Wabbajack.Paths.IO;
|
|
|
|
|
2024-06-17 18:10:53 +00:00
|
|
|
namespace Wabbajack.Compression.BSA.BA2Archive;
|
2021-10-23 16:51:17 +00:00
|
|
|
|
|
|
|
public class Builder : IBuilder
|
2021-09-27 12:42:46 +00:00
|
|
|
{
|
2021-10-23 16:51:17 +00:00
|
|
|
private List<IFileBuilder> _entries = new();
|
|
|
|
private DiskSlabAllocator _slab;
|
|
|
|
private BA2State _state;
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
public async ValueTask AddFile(AFile state, Stream src, CancellationToken token)
|
|
|
|
{
|
2023-01-28 21:42:23 +00:00
|
|
|
try
|
2021-09-27 12:42:46 +00:00
|
|
|
{
|
2023-01-28 21:42:23 +00:00
|
|
|
switch (_state.Type)
|
|
|
|
{
|
|
|
|
case BA2EntryType.GNRL:
|
|
|
|
var result = await FileEntryBuilder.Create((BA2File)state, src, _slab, token);
|
|
|
|
lock (_entries)
|
|
|
|
{
|
|
|
|
_entries.Add(result);
|
|
|
|
}
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2023-01-28 21:42:23 +00:00
|
|
|
break;
|
|
|
|
case BA2EntryType.DX10:
|
2024-06-17 18:10:53 +00:00
|
|
|
var resultdx10 = await DX10FileEntryBuilder.Create((BA2DX10File)state, src, _slab, _state.Compression == 3, token);
|
2023-01-28 21:42:23 +00:00
|
|
|
lock (_entries)
|
|
|
|
{
|
|
|
|
_entries.Add(resultdx10);
|
|
|
|
}
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2023-01-28 21:42:23 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
|
|
|
throw new InvalidDataException($"Error adding file {state.Path} to archive: {ex.Message}", ex);
|
2021-09-27 12:42:46 +00:00
|
|
|
}
|
2021-10-23 16:51:17 +00:00
|
|
|
}
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
public async ValueTask Build(Stream fs, CancellationToken token)
|
|
|
|
{
|
|
|
|
SortEntries();
|
|
|
|
await using var bw = new BinaryWriter(fs, Encoding.Default, true);
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
bw.Write(Encoding.ASCII.GetBytes(_state.HeaderMagic));
|
|
|
|
bw.Write(_state.Version);
|
|
|
|
bw.Write(Encoding.ASCII.GetBytes(Enum.GetName(_state.Type)!));
|
|
|
|
bw.Write((uint) _entries.Count);
|
|
|
|
var tableOffsetLoc = bw.BaseStream.Position;
|
|
|
|
bw.Write((ulong) 0);
|
2024-06-17 18:10:53 +00:00
|
|
|
if(_state.Version == 2 || _state.Version == 3)
|
|
|
|
{
|
|
|
|
bw.Write(_state.Unknown1);
|
|
|
|
bw.Write(_state.Unknown2);
|
|
|
|
if (_state.Version == 3)
|
|
|
|
bw.Write(_state.Compression);
|
|
|
|
}
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
foreach (var entry in _entries) entry.WriteHeader(bw, token);
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
foreach (var entry in _entries) await entry.WriteData(bw, token);
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
if (!_state.HasNameTable) return;
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
var pos = bw.BaseStream.Position;
|
|
|
|
bw.BaseStream.Seek(tableOffsetLoc, SeekOrigin.Begin);
|
|
|
|
bw.Write((ulong) pos);
|
|
|
|
bw.BaseStream.Seek(pos, SeekOrigin.Begin);
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
foreach (var entry in _entries)
|
2021-09-27 12:42:46 +00:00
|
|
|
{
|
2021-10-23 16:51:17 +00:00
|
|
|
var bytes = Encoding.UTF8.GetBytes(entry.FullName);
|
|
|
|
bw.Write((ushort) bytes.Length);
|
|
|
|
await bw.BaseStream.WriteAsync(bytes, token);
|
2021-09-27 12:42:46 +00:00
|
|
|
}
|
2021-10-23 16:51:17 +00:00
|
|
|
}
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
public static Builder Create(BA2State state, TemporaryFileManager manager)
|
|
|
|
{
|
|
|
|
var self = new Builder {_state = state, _slab = new DiskSlabAllocator(manager)};
|
|
|
|
return self;
|
|
|
|
}
|
2021-09-27 12:42:46 +00:00
|
|
|
|
2021-10-23 16:51:17 +00:00
|
|
|
public async ValueTask DisposeAsync()
|
|
|
|
{
|
|
|
|
await _slab.DisposeAsync();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void SortEntries()
|
|
|
|
{
|
|
|
|
_entries = _entries.OrderBy(e => e.Index).ToList();
|
2021-09-27 12:42:46 +00:00
|
|
|
}
|
|
|
|
}
|