mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
BTar files are integrated into the extractor code
This commit is contained in:
parent
30b29d890d
commit
379337d0b6
@ -69,6 +69,7 @@ internal class Program
|
||||
services.AddSingleton<IVerb, SteamDownloadFile>();
|
||||
services.AddSingleton<IVerb, UploadToNexus>();
|
||||
services.AddSingleton<IVerb, ListCreationClubContent>();
|
||||
services.AddSingleton<IVerb, Extract>();
|
||||
|
||||
services.AddSingleton<IUserInterventionHandler, UserInterventionHandler>();
|
||||
}).Build();
|
||||
|
49
Wabbajack.CLI/Verbs/Extract.cs
Normal file
49
Wabbajack.CLI/Verbs/Extract.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Downloaders;
|
||||
using Wabbajack.DTOs;
|
||||
using Wabbajack.Paths;
|
||||
using Wabbajack.Paths.IO;
|
||||
|
||||
namespace Wabbajack.CLI.Verbs;
|
||||
|
||||
public class Extract : IVerb
|
||||
{
|
||||
|
||||
private readonly ILogger<DownloadUrl> _logger;
|
||||
private readonly FileExtractor.FileExtractor _extractor;
|
||||
|
||||
public Extract(ILogger<DownloadUrl> logger, FileExtractor.FileExtractor extractor)
|
||||
{
|
||||
_logger = logger;
|
||||
_extractor = extractor;
|
||||
}
|
||||
|
||||
public Command MakeCommand()
|
||||
{
|
||||
var command = new Command("extract");
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-i", "-input"}, "Input Archive"));
|
||||
command.Add(new Option<AbsolutePath>(new[] {"-o", "-output"}, "Output folder"));
|
||||
command.Description = "Extracts the contents of an archive into a folder";
|
||||
command.Handler = CommandHandler.Create(Run);
|
||||
return command;
|
||||
}
|
||||
|
||||
private async Task<int> Run(AbsolutePath input, AbsolutePath output, CancellationToken token)
|
||||
{
|
||||
if (!output.DirectoryExists())
|
||||
output.Parent.CreateDirectory();
|
||||
|
||||
await _extractor.ExtractAll(input, output, token, f =>
|
||||
{
|
||||
Console.WriteLine($" - {f}");
|
||||
return true;
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -5,3 +5,4 @@ Relaxed RAR format,52 61 72 21,RAR
|
||||
RAR5 or newer, 52 61 72 21 1A 07 01 00,RAR_NEW
|
||||
RAR4 or older, 52 61 72 21 1A 07 00,RAR_OLD
|
||||
DDS, 44 44 53 20,DDS
|
||||
Bethesda Tar, 42 54 41 52, BTAR
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,8 @@
|
||||
<#@ template language="C#" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ import namespace="System.Collections.Generic" #>
|
||||
<#@ import namespace="System.IO" #>
|
||||
<#@ import namespace="System.Linq" #>
|
||||
|
||||
<#
|
||||
byte[] StringToByteArray(string hex)
|
||||
|
@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@ -11,8 +13,10 @@ using OMODFramework;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Common.FileSignatures;
|
||||
using Wabbajack.Compression.BSA;
|
||||
using Wabbajack.Compression.BSA.FO4Archive;
|
||||
using Wabbajack.DTOs.Streams;
|
||||
using Wabbajack.FileExtractor.ExtractedFiles;
|
||||
using Wabbajack.IO.Async;
|
||||
using Wabbajack.Paths;
|
||||
using Wabbajack.Paths.IO;
|
||||
using Wabbajack.RateLimiter;
|
||||
@ -24,6 +28,7 @@ public class FileExtractor
|
||||
public static readonly SignatureChecker ArchiveSigs = new(FileType.TES3,
|
||||
FileType.BSA,
|
||||
FileType.BA2,
|
||||
FileType.BTAR,
|
||||
FileType.ZIP,
|
||||
//FileType.EXE,
|
||||
FileType.RAR_OLD,
|
||||
@ -43,6 +48,7 @@ public class FileExtractor
|
||||
new Extension(".7zip"),
|
||||
new Extension(".rar"),
|
||||
new Extension(".zip"),
|
||||
new Extension(".btar"),
|
||||
OMODExtension,
|
||||
FOMODExtension
|
||||
};
|
||||
@ -98,6 +104,9 @@ public class FileExtractor
|
||||
|
||||
break;
|
||||
}
|
||||
case FileType.BTAR:
|
||||
results = await GatheringExtractWithBTAR(sFn, shouldExtract, mapfn, token);
|
||||
break;
|
||||
|
||||
case FileType.BSA:
|
||||
case FileType.BA2:
|
||||
@ -120,6 +129,75 @@ public class FileExtractor
|
||||
return results;
|
||||
}
|
||||
|
||||
private async Task<IDictionary<RelativePath,T>> GatheringExtractWithBTAR<T>
|
||||
(IStreamFactory sFn, Predicate<RelativePath> shouldExtract, Func<RelativePath,IExtractedFile,ValueTask<T>> mapfn, CancellationToken token)
|
||||
{
|
||||
await using var strm = await sFn.GetStream();
|
||||
var astrm = new AsyncBinaryReader(strm);
|
||||
var magic = BinaryPrimitives.ReadUInt32BigEndian(await astrm.ReadBytes(4));
|
||||
// BTAR Magic
|
||||
if (magic != 0x42544152) throw new Exception("Not a valid BTAR file");
|
||||
if (await astrm.ReadUInt16() != 1) throw new Exception("Invalid BTAR major version, should be 1");
|
||||
var minorVersion = await astrm.ReadUInt16();
|
||||
if (minorVersion is < 2 or > 4) throw new Exception("Invalid BTAR minor version");
|
||||
|
||||
var results = new Dictionary<RelativePath, T>();
|
||||
|
||||
while (astrm.Position < astrm.Length)
|
||||
{
|
||||
var nameLength = await astrm.ReadUInt16();
|
||||
var name = Encoding.UTF8.GetString(await astrm.ReadBytes(nameLength)).ToRelativePath();
|
||||
var dataLength = await astrm.ReadUInt64();
|
||||
var newPos = astrm.Position + (long)dataLength;
|
||||
if (!shouldExtract(name))
|
||||
{
|
||||
astrm.Position += (long)dataLength;
|
||||
continue;
|
||||
}
|
||||
|
||||
var result = await mapfn(name, new BTARExtractedFile(sFn, name, astrm, astrm.Position, (long) dataLength));
|
||||
results.Add(name, result);
|
||||
astrm.Position = newPos;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private class BTARExtractedFile : IExtractedFile
|
||||
{
|
||||
private readonly IStreamFactory _parent;
|
||||
private readonly AsyncBinaryReader _rdr;
|
||||
private readonly long _start;
|
||||
private readonly long _length;
|
||||
private readonly RelativePath _name;
|
||||
|
||||
public BTARExtractedFile(IStreamFactory parent, RelativePath name, AsyncBinaryReader rdr, long startingPosition, long length)
|
||||
{
|
||||
_name = name;
|
||||
_parent = parent;
|
||||
_rdr = rdr;
|
||||
_start = startingPosition;
|
||||
_length = length;
|
||||
}
|
||||
|
||||
public DateTime LastModifiedUtc => _parent.LastModifiedUtc;
|
||||
public IPath Name => _name;
|
||||
public async ValueTask<Stream> GetStream()
|
||||
{
|
||||
_rdr.Position = _start;
|
||||
var data = await _rdr.ReadBytes((int) _length);
|
||||
return new MemoryStream(data);
|
||||
}
|
||||
|
||||
public bool CanMove { get; set; } = true;
|
||||
public async ValueTask Move(AbsolutePath newPath, CancellationToken token)
|
||||
{
|
||||
await using var output = newPath.Open(FileMode.Create, FileAccess.Read, FileShare.Read);
|
||||
_rdr.Position = _start;
|
||||
await _rdr.BaseStream.CopyToLimitAsync(output, (int)_length, token);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Dictionary<RelativePath, T>> GatheringExtractWithOMOD<T>
|
||||
(Stream archive, Predicate<RelativePath> shouldExtract, Func<RelativePath, IExtractedFile, ValueTask<T>> mapfn,
|
||||
CancellationToken token)
|
||||
|
Loading…
Reference in New Issue
Block a user