mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Can download Bethesda.NET CC files
This commit is contained in:
parent
b923cac804
commit
30b29d890d
@ -4,9 +4,11 @@ using System.CommandLine.Invocation;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Wabbajack.Common;
|
||||||
using Wabbajack.Downloaders;
|
using Wabbajack.Downloaders;
|
||||||
using Wabbajack.DTOs;
|
using Wabbajack.DTOs;
|
||||||
using Wabbajack.Paths;
|
using Wabbajack.Paths;
|
||||||
|
using Wabbajack.Paths.IO;
|
||||||
|
|
||||||
namespace Wabbajack.CLI.Verbs;
|
namespace Wabbajack.CLI.Verbs;
|
||||||
|
|
||||||
@ -42,7 +44,8 @@ public class DownloadUrl : IVerb
|
|||||||
}
|
}
|
||||||
|
|
||||||
var archive = new Archive() {State = parsed, Name = output.FileName.ToString()};
|
var archive = new Archive() {State = parsed, Name = output.FileName.ToString()};
|
||||||
await _dispatcher.Download(archive, output, CancellationToken.None);
|
var hash = await _dispatcher.Download(archive, output, CancellationToken.None); ;
|
||||||
|
Console.WriteLine($"Download complete: {output.Size().ToFileSizeString()} {hash} {hash.ToHex()} {(long)hash}");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,11 +6,11 @@ namespace Wabbajack.DTOs.DownloadStates;
|
|||||||
public class Bethesda : ADownloadState
|
public class Bethesda : ADownloadState
|
||||||
{
|
{
|
||||||
public override string TypeName { get; } = "Bethesda";
|
public override string TypeName { get; } = "Bethesda";
|
||||||
public override object[] PrimaryKey => new object[] {Game, IsCCMod, ProductId, BranchID, ContentId};
|
public override object[] PrimaryKey => new object[] {Game, IsCCMod, ProductId, BranchId, ContentId};
|
||||||
public Game Game { get; set; }
|
public Game Game { get; set; }
|
||||||
public bool IsCCMod { get; set; }
|
public bool IsCCMod { get; set; }
|
||||||
public string ContentId { get; set; }
|
public string ContentId { get; set; }
|
||||||
public long ProductId { get; set; }
|
public long ProductId { get; set; }
|
||||||
public long BranchID { get; set; }
|
public long BranchId { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
}
|
}
|
@ -1,3 +1,7 @@
|
|||||||
|
using System.IO.Compression;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using CS_AES_CTR;
|
||||||
|
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Wabbajack.Common;
|
using Wabbajack.Common;
|
||||||
using Wabbajack.Downloaders.Interfaces;
|
using Wabbajack.Downloaders.Interfaces;
|
||||||
@ -38,28 +42,56 @@ public class BethesdaDownloader : ADownloader<DTOs.DownloadStates.Bethesda>, IUr
|
|||||||
|
|
||||||
var chunks = tree!.DepotList.First().FileList.First().ChunkList;
|
var chunks = tree!.DepotList.First().FileList.First().ChunkList;
|
||||||
|
|
||||||
|
using var os = destination.Open(FileMode.Create, FileAccess.ReadWrite, FileShare.Read);
|
||||||
|
|
||||||
|
var hasher = new xxHashAlgorithm(0);
|
||||||
|
Hash finalHash = default;
|
||||||
|
|
||||||
|
var aesKey = depot.ExInfoA.ToArray();
|
||||||
|
var aesIV = depot.ExInfoB.Take(16).ToArray();
|
||||||
|
|
||||||
await chunks.PMapAll(async chunk =>
|
await chunks.PMapAll(async chunk =>
|
||||||
{
|
{
|
||||||
var data = await GetChunk(state, chunk, token);
|
var data = await GetChunk(state, chunk, depot.PropertiesId, token);
|
||||||
var reported = job.Report(data.Length, token);
|
var reported = job.Report(data.Length, token);
|
||||||
|
|
||||||
// Decrypt and Decompress
|
var aesCtr = new AES_CTR(aesKey, aesIV, false);
|
||||||
|
data = aesCtr.DecryptBytes(data);
|
||||||
|
|
||||||
|
if (chunk.UncompressedSize != chunk.ChunkSize)
|
||||||
|
{
|
||||||
|
var inflater = new InflaterInputStream(new MemoryStream(data));
|
||||||
|
data = await inflater.ReadAllAsync();
|
||||||
|
}
|
||||||
|
|
||||||
await reported;
|
await reported;
|
||||||
return data;
|
return data;
|
||||||
});
|
})
|
||||||
|
.Do(async data =>
|
||||||
return default;
|
{
|
||||||
|
if (data.Length < tree.DepotList.First().BytesPerChunk)
|
||||||
|
{
|
||||||
|
hasher.HashBytes(data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
finalHash = Hash.FromULong(hasher.FinalizeHashValueInternal(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<byte[]> GetChunk(DTOs.DownloadStates.Bethesda state, Chunk chunk,
|
await os.WriteAsync(data, token);
|
||||||
|
});
|
||||||
|
|
||||||
|
return finalHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<byte[]> GetChunk(DTOs.DownloadStates.Bethesda state, Chunk chunk, long propertiesId,
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
var uri = new Uri($"https://content.cdp.bethesda.net/{state.ProductId}/{state.BranchID}/{chunk.Sha}");
|
var uri = new Uri($"https://content.cdp.bethesda.net/{state.ProductId}/{propertiesId}/{chunk.Sha}");
|
||||||
var msg = new HttpRequestMessage(HttpMethod.Get, uri);
|
var msg = new HttpRequestMessage(HttpMethod.Get, uri);
|
||||||
msg.Headers.Add("User-Agent", "bnet");
|
msg.Headers.Add("User-Agent", "bnet");
|
||||||
using var job = await _limiter.Begin("Getting chunk", chunk.ChunkSize, token);
|
using var job = await _limiter.Begin("Getting chunk", chunk.ChunkSize, token);
|
||||||
using var response = await _httpClient.GetAsync(uri, token);
|
using var response = await _httpClient.SendAsync(msg, token);
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
throw new HttpException(response);
|
throw new HttpException(response);
|
||||||
await job.Report(chunk.ChunkSize, token);
|
await job.Report(chunk.ChunkSize, token);
|
||||||
@ -128,7 +160,7 @@ public class BethesdaDownloader : ADownloader<DTOs.DownloadStates.Bethesda>, IUr
|
|||||||
Game = game.Game,
|
Game = game.Game,
|
||||||
IsCCMod = isCCMod,
|
IsCCMod = isCCMod,
|
||||||
ProductId = productId,
|
ProductId = productId,
|
||||||
BranchID = branchId,
|
BranchId = branchId,
|
||||||
ContentId = path[3]
|
ContentId = path[3]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -136,7 +168,7 @@ public class BethesdaDownloader : ADownloader<DTOs.DownloadStates.Bethesda>, IUr
|
|||||||
public Uri UnParse(IDownloadState state)
|
public Uri UnParse(IDownloadState state)
|
||||||
{
|
{
|
||||||
var cstate = (DTOs.DownloadStates.Bethesda) state;
|
var cstate = (DTOs.DownloadStates.Bethesda) state;
|
||||||
return new Uri($"bethesda://{cstate.Game}/{(cstate.IsCCMod ? "cc" : "mod")}/{cstate.ProductId}/{cstate.BranchID}/{cstate.ContentId}");
|
return new Uri($"bethesda://{cstate.Game}/{(cstate.IsCCMod ? "cc" : "mod")}/{cstate.ProductId}/{cstate.BranchId}/{cstate.ContentId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueTask<Stream> GetChunkedSeekableStream(Archive archive, CancellationToken token)
|
public ValueTask<Stream> GetChunkedSeekableStream(Archive archive, CancellationToken token)
|
||||||
|
@ -12,4 +12,9 @@
|
|||||||
<ProjectReference Include="..\Wabbajack.Networking.BethesdaNet\Wabbajack.Networking.BethesdaNet.csproj" />
|
<ProjectReference Include="..\Wabbajack.Networking.BethesdaNet\Wabbajack.Networking.BethesdaNet.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="LibAES-CTR" Version="0.9.6" />
|
||||||
|
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -135,7 +135,7 @@ public class Client
|
|||||||
ContentId = c.ContentId,
|
ContentId = c.ContentId,
|
||||||
IsCCMod = c.CcMod,
|
IsCCMod = c.CcMod,
|
||||||
ProductId = c.CdpProductId,
|
ProductId = c.CdpProductId,
|
||||||
BranchID = c.CdpBranchId
|
BranchId = c.CdpBranchId
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,7 +165,7 @@ public class Client
|
|||||||
{
|
{
|
||||||
await EnsureAuthed(token);
|
await EnsureAuthed(token);
|
||||||
var msg = MakeMessage(HttpMethod.Get,
|
var msg = MakeMessage(HttpMethod.Get,
|
||||||
new Uri($"https://api.bethesda.net/cdp-user/projects/{state.ProductId}/branches/{state.BranchID}/{type}/.json"));
|
new Uri($"https://api.bethesda.net/cdp-user/projects/{state.ProductId}/branches/{state.BranchId}/{type}/.json"));
|
||||||
msg.Headers.Add("x-src-fp", FingerprintKey);
|
msg.Headers.Add("x-src-fp", FingerprintKey);
|
||||||
msg.Headers.Add("x-cdp-app", "UGC SDK");
|
msg.Headers.Add("x-cdp-app", "UGC SDK");
|
||||||
msg.Headers.Add("x-cdp-app-ver", "0.9.11314/debug");
|
msg.Headers.Add("x-cdp-app-ver", "0.9.11314/debug");
|
||||||
|
Loading…
Reference in New Issue
Block a user