mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Merge pull request #701 from Noggog/wabbajack-lib-nullability
Wabbajack.Lib Nullability Enabled
This commit is contained in:
@ -184,4 +184,10 @@ dotnet_diagnostic.CS8609.severity = error
|
|||||||
dotnet_diagnostic.CS8714.severity = error
|
dotnet_diagnostic.CS8714.severity = error
|
||||||
|
|
||||||
# CS8605: Unboxing a possibly null value.
|
# CS8605: Unboxing a possibly null value.
|
||||||
dotnet_diagnostic.CS8605.severity = error
|
dotnet_diagnostic.CS8605.severity = error
|
||||||
|
|
||||||
|
# CS8613: Nullability of reference types in return type doesn't match implicitly implemented member.
|
||||||
|
dotnet_diagnostic.CS8613.severity = error
|
||||||
|
|
||||||
|
# CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
|
||||||
|
dotnet_diagnostic.CS8632.severity = error
|
@ -53,14 +53,14 @@ namespace Wabbajack.BuildServer.Test
|
|||||||
public async Task CanNotifyOfInis()
|
public async Task CanNotifyOfInis()
|
||||||
{
|
{
|
||||||
var archive =
|
var archive =
|
||||||
new Archive
|
new Archive(
|
||||||
{
|
new NexusDownloader.State
|
||||||
State = new NexusDownloader.State
|
|
||||||
{
|
{
|
||||||
Game = Game.SkyrimSpecialEdition,
|
Game = Game.SkyrimSpecialEdition,
|
||||||
ModID = long.MaxValue >> 3,
|
ModID = long.MaxValue >> 3,
|
||||||
FileID = long.MaxValue >> 3,
|
FileID = long.MaxValue >> 3,
|
||||||
},
|
})
|
||||||
|
{
|
||||||
Name = Guid.NewGuid().ToString()
|
Name = Guid.NewGuid().ToString()
|
||||||
};
|
};
|
||||||
Assert.True(await AuthorAPI.UploadPackagedInis(new[] {archive}));
|
Assert.True(await AuthorAPI.UploadPackagedInis(new[] {archive}));
|
||||||
|
@ -122,19 +122,14 @@ namespace Wabbajack.BuildServer.Test
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
var modListData = new ModList
|
var modListData = new ModList();
|
||||||
{
|
modListData.Archives.Add(
|
||||||
Archives = new List<Archive>
|
new Archive(new HTTPDownloader.State(MakeURL("test_archive.txt")))
|
||||||
{
|
{
|
||||||
new Archive
|
Hash = await test_archive_path.FileHashAsync(),
|
||||||
{
|
Name = "test_archive",
|
||||||
Hash = await test_archive_path.FileHashAsync(),
|
Size = test_archive_path.Size,
|
||||||
Name = "test_archive",
|
});
|
||||||
Size = test_archive_path.Size,
|
|
||||||
State = new HTTPDownloader.State {Url = MakeURL("test_archive.txt")}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var modListPath = "test_modlist.wabbajack".RelativeTo(Fixture.ServerPublicFolder);
|
var modListPath = "test_modlist.wabbajack".RelativeTo(Fixture.ServerPublicFolder);
|
||||||
|
|
||||||
|
@ -42,13 +42,9 @@ namespace Wabbajack.BuildServer.Test
|
|||||||
{
|
{
|
||||||
Payload = new IndexJob
|
Payload = new IndexJob
|
||||||
{
|
{
|
||||||
Archive = new Archive
|
Archive = new Archive(new HTTPDownloader.State(MakeURL("old_file_data.random")))
|
||||||
{
|
{
|
||||||
Name = "Oldfile",
|
Name = "Oldfile",
|
||||||
State = new HTTPDownloader.State
|
|
||||||
{
|
|
||||||
Url = MakeURL("old_file_data.random"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -57,13 +53,9 @@ namespace Wabbajack.BuildServer.Test
|
|||||||
{
|
{
|
||||||
Payload = new IndexJob
|
Payload = new IndexJob
|
||||||
{
|
{
|
||||||
Archive = new Archive
|
Archive = new Archive(new HTTPDownloader.State(MakeURL("new_file_data.random")))
|
||||||
{
|
{
|
||||||
Name = "Newfile",
|
Name = "Newfile",
|
||||||
State = new HTTPDownloader.State
|
|
||||||
{
|
|
||||||
Url = MakeURL("new_file_data.random"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -126,6 +118,5 @@ namespace Wabbajack.BuildServer.Test
|
|||||||
Assert.True($"{oldDataHash.ToHex()}_{newDataHash.ToHex()}".RelativeTo(Fixture.ServerUpdatesFolder).IsFile);
|
Assert.True($"{oldDataHash.ToHex()}_{newDataHash.ToHex()}".RelativeTo(Fixture.ServerUpdatesFolder).IsFile);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,10 +102,9 @@ namespace Wabbajack.BuildServer.Controllers
|
|||||||
Priority = Job.JobPriority.Low,
|
Priority = Job.JobPriority.Low,
|
||||||
Payload = new IndexJob
|
Payload = new IndexJob
|
||||||
{
|
{
|
||||||
Archive = new Archive
|
Archive = new Archive(data)
|
||||||
{
|
{
|
||||||
Name = entry.Name,
|
Name = entry.Name,
|
||||||
State = data
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -22,7 +22,7 @@ namespace Wabbajack.BuildServer.Controllers
|
|||||||
public async Task<long> EnqueueJob(string JobName)
|
public async Task<long> EnqueueJob(string JobName)
|
||||||
{
|
{
|
||||||
var jobtype = AJobPayload.NameToType[JobName];
|
var jobtype = AJobPayload.NameToType[JobName];
|
||||||
var job = new Job{Priority = Job.JobPriority.High, Payload = (AJobPayload)jobtype.GetConstructor(new Type[0]).Invoke(new object?[0])};
|
var job = new Job{Priority = Job.JobPriority.High, Payload = (AJobPayload)jobtype.GetConstructor(new Type[0]).Invoke(new object[0])};
|
||||||
await SQL.EnqueueJob(job);
|
await SQL.EnqueueJob(job);
|
||||||
return job.Id;
|
return job.Id;
|
||||||
}
|
}
|
||||||
|
@ -175,15 +175,19 @@ namespace Wabbajack.BuildServer.Controllers
|
|||||||
var allMods = await api.GetModFiles(state.Game, state.ModID);
|
var allMods = await api.GetModFiles(state.Game, state.ModID);
|
||||||
var archive = allMods.files.Where(m => !string.IsNullOrEmpty(m.category_name))
|
var archive = allMods.files.Where(m => !string.IsNullOrEmpty(m.category_name))
|
||||||
.OrderBy(s => Math.Abs((long)s.size - origSize))
|
.OrderBy(s => Math.Abs((long)s.size - origSize))
|
||||||
.Select(s => new Archive {
|
.Select(s =>
|
||||||
Name = s.file_name,
|
new Archive(
|
||||||
Size = (long)s.size,
|
new NexusDownloader.State
|
||||||
State = new NexusDownloader.State
|
{
|
||||||
|
Game = state.Game,
|
||||||
|
ModID = state.ModID,
|
||||||
|
FileID = s.file_id
|
||||||
|
})
|
||||||
{
|
{
|
||||||
Game = state.Game,
|
Name = s.file_name,
|
||||||
ModID = state.ModID,
|
Size = (long)s.size,
|
||||||
FileID = s.file_id
|
})
|
||||||
}}).FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
if (archive == null)
|
if (archive == null)
|
||||||
{
|
{
|
||||||
|
@ -21,13 +21,12 @@ namespace Wabbajack.BuildServer.Models.Jobs
|
|||||||
{
|
{
|
||||||
Utils.Log($"Indexing game files");
|
Utils.Log($"Indexing game files");
|
||||||
var states = GameRegistry.Games.Values
|
var states = GameRegistry.Games.Values
|
||||||
.Where(game => game.GameLocation() != null && game.MainExecutable != null)
|
.Where(game => game.TryGetGameLocation() != null && game.MainExecutable != null)
|
||||||
.SelectMany(game => game.GameLocation().Value.EnumerateFiles()
|
.SelectMany(game => game.GameLocation().EnumerateFiles()
|
||||||
.Select(file => new GameFileSourceDownloader.State
|
.Select(file => new GameFileSourceDownloader.State(game.InstalledVersion)
|
||||||
{
|
{
|
||||||
Game = game.Game,
|
Game = game.Game,
|
||||||
GameVersion = game.InstalledVersion,
|
GameFile = file.RelativeTo(game.GameLocation()),
|
||||||
GameFile = file.RelativeTo(game.GameLocation().Value),
|
|
||||||
}))
|
}))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
@ -41,7 +40,7 @@ namespace Wabbajack.BuildServer.Models.Jobs
|
|||||||
|
|
||||||
await states.PMap(queue, async state =>
|
await states.PMap(queue, async state =>
|
||||||
{
|
{
|
||||||
var path = state.Game.MetaData().GameLocation().Value.Combine(state.GameFile);
|
var path = state.Game.MetaData().GameLocation().Combine(state.GameFile);
|
||||||
Utils.Log($"Hashing Game file {path}");
|
Utils.Log($"Hashing Game file {path}");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -55,16 +54,15 @@ namespace Wabbajack.BuildServer.Models.Jobs
|
|||||||
|
|
||||||
var with_hash = states.Where(state => state.Hash != default).ToList();
|
var with_hash = states.Where(state => state.Hash != default).ToList();
|
||||||
Utils.Log($"Inserting {with_hash.Count} jobs.");
|
Utils.Log($"Inserting {with_hash.Count} jobs.");
|
||||||
var jobs = states.Select(state => new IndexJob {Archive = new Archive {Name = state.GameFile.FileName.ToString(), State = state}})
|
var jobs = states.Select(state => new IndexJob {Archive = new Archive(state) { Name = state.GameFile.FileName.ToString()}})
|
||||||
.Select(j => new Job {Payload = j, RequiresNexus = j.UsesNexus})
|
.Select(j => new Job {Payload = j, RequiresNexus = j.UsesNexus})
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
foreach (var job in jobs)
|
foreach (var job in jobs)
|
||||||
await sql.EnqueueJob(job);
|
await sql.EnqueueJob(job);
|
||||||
|
|
||||||
return JobResult.Success();
|
return JobResult.Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IEnumerable<object> PrimaryKey => new object[0];
|
protected override IEnumerable<object> PrimaryKey => new object[0];
|
||||||
|
@ -39,13 +39,9 @@ namespace Wabbajack.BuildServer.Models.Jobs
|
|||||||
{
|
{
|
||||||
Payload = new IndexJob
|
Payload = new IndexJob
|
||||||
{
|
{
|
||||||
Archive = new Archive
|
Archive = new Archive(new MegaDownloader.State(url.ToString()))
|
||||||
{
|
{
|
||||||
Name = Guid.NewGuid() + ".7z",
|
Name = Guid.NewGuid() + ".7z",
|
||||||
State = new MegaDownloader.State
|
|
||||||
{
|
|
||||||
Url = url.ToString()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -57,15 +57,11 @@ namespace Wabbajack.BuildServer.Models.Jobs
|
|||||||
Priority = Job.JobPriority.High,
|
Priority = Job.JobPriority.High,
|
||||||
Payload = new IndexJob
|
Payload = new IndexJob
|
||||||
{
|
{
|
||||||
Archive = new Archive
|
Archive = new Archive(new HTTPDownloader.State(file.Uri))
|
||||||
{
|
{
|
||||||
Name = file.MungedName,
|
Name = file.MungedName,
|
||||||
Size = file.Size,
|
Size = file.Size,
|
||||||
Hash = file.Hash,
|
Hash = file.Hash,
|
||||||
State = new HTTPDownloader.State
|
|
||||||
{
|
|
||||||
Url = file.Uri
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -524,9 +524,8 @@ namespace Wabbajack.BuildServer.Model.Models
|
|||||||
var result = await conn.QueryFirstOrDefaultAsync<string>(@"SELECT JsonState FROM dbo.DownloadStates
|
var result = await conn.QueryFirstOrDefaultAsync<string>(@"SELECT JsonState FROM dbo.DownloadStates
|
||||||
WHERE Hash = @hash AND PrimaryKey like 'NexusDownloader+State|%'",
|
WHERE Hash = @hash AND PrimaryKey like 'NexusDownloader+State|%'",
|
||||||
new {Hash = (long)startingHash});
|
new {Hash = (long)startingHash});
|
||||||
return result == null ? null : new Archive
|
return result == null ? null : new Archive(result.FromJsonString<AbstractDownloadState>())
|
||||||
{
|
{
|
||||||
State = result.FromJsonString<AbstractDownloadState>(),
|
|
||||||
Hash = startingHash
|
Hash = startingHash
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -536,9 +535,8 @@ namespace Wabbajack.BuildServer.Model.Models
|
|||||||
await using var conn = await Open();
|
await using var conn = await Open();
|
||||||
var result = await conn.QueryFirstOrDefaultAsync<(long Hash, string State)>(@"SELECT Hash, JsonState FROM dbo.DownloadStates WHERE PrimaryKey = @PrimaryKey",
|
var result = await conn.QueryFirstOrDefaultAsync<(long Hash, string State)>(@"SELECT Hash, JsonState FROM dbo.DownloadStates WHERE PrimaryKey = @PrimaryKey",
|
||||||
new {PrimaryKey = primaryKey});
|
new {PrimaryKey = primaryKey});
|
||||||
return result == default ? null : new Archive
|
return result == default ? null : new Archive(result.State.FromJsonString<AbstractDownloadState>())
|
||||||
{
|
{
|
||||||
State = result.State.FromJsonString<AbstractDownloadState>(),
|
|
||||||
Hash = Hash.FromLong(result.Hash)
|
Hash = Hash.FromLong(result.Hash)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -318,7 +318,7 @@ namespace Wabbajack.CLI.Verbs
|
|||||||
|
|
||||||
private static async Task<string> GetTextFileFromModlist(AbsolutePath archive, ModList modlist, RelativePath sourceID)
|
private static async Task<string> GetTextFileFromModlist(AbsolutePath archive, ModList modlist, RelativePath sourceID)
|
||||||
{
|
{
|
||||||
var installer = new MO2Installer(archive, modlist, default, default, null);
|
var installer = new MO2Installer(archive, modlist, default, default, parameters: null!);
|
||||||
byte[] bytes = await installer.LoadBytesFromPath(sourceID);
|
byte[] bytes = await installer.LoadBytesFromPath(sourceID);
|
||||||
return Encoding.Default.GetString(bytes);
|
return Encoding.Default.GetString(bytes);
|
||||||
}
|
}
|
||||||
@ -328,9 +328,9 @@ namespace Wabbajack.CLI.Verbs
|
|||||||
return header.Trim().Replace(" ", "").Replace(".", "");
|
return header.Trim().Replace(" ", "").Replace(".", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetModName(Archive a)
|
private static string? GetModName(Archive a)
|
||||||
{
|
{
|
||||||
var result = a.Name;
|
string? result = a.Name;
|
||||||
|
|
||||||
if (a.State is IMetaState metaState)
|
if (a.State is IMetaState metaState)
|
||||||
{
|
{
|
||||||
|
@ -9,7 +9,7 @@ namespace Wabbajack.CLI.Verbs
|
|||||||
public class DeleteFile : AVerb
|
public class DeleteFile : AVerb
|
||||||
{
|
{
|
||||||
[Option('n', "name", Required = true, HelpText = @"Full name (as returned by my-files) of the file")]
|
[Option('n', "name", Required = true, HelpText = @"Full name (as returned by my-files) of the file")]
|
||||||
public string? Name { get; set; }
|
public string Name { get; set; } = null!;
|
||||||
|
|
||||||
protected override async Task<ExitCode> Run()
|
protected override async Task<ExitCode> Run()
|
||||||
{
|
{
|
||||||
|
@ -35,7 +35,7 @@ namespace Wabbajack.CLI.Verbs
|
|||||||
new[] {state}
|
new[] {state}
|
||||||
.PMap(queue, async s =>
|
.PMap(queue, async s =>
|
||||||
{
|
{
|
||||||
await s.Download(new Archive {Name = Path.GetFileName(Output)}, (AbsolutePath)Output);
|
await s.Download(new Archive(state: null!) {Name = Path.GetFileName(Output)}, (AbsolutePath)Output);
|
||||||
}).Wait();
|
}).Wait();
|
||||||
|
|
||||||
File.WriteAllLines(Output + ".meta", state.GetMetaIni());
|
File.WriteAllLines(Output + ".meta", state.GetMetaIni());
|
||||||
|
@ -21,6 +21,9 @@ namespace Wabbajack
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the given values to the dictionary. If a key already exists, it will throw an exception
|
||||||
|
/// </summary>
|
||||||
public static void Add<K, V>(this IDictionary<K, V> dict, IEnumerable<KeyValuePair<K, V>> vals)
|
public static void Add<K, V>(this IDictionary<K, V> dict, IEnumerable<KeyValuePair<K, V>> vals)
|
||||||
where K : notnull
|
where K : notnull
|
||||||
{
|
{
|
||||||
@ -30,6 +33,9 @@ namespace Wabbajack
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the given values to the dictionary. If a key already exists, it will be replaced
|
||||||
|
/// </summary>
|
||||||
public static void Set<K, V>(this IDictionary<K, V> dict, IEnumerable<KeyValuePair<K, V>> vals)
|
public static void Set<K, V>(this IDictionary<K, V> dict, IEnumerable<KeyValuePair<K, V>> vals)
|
||||||
where K : notnull
|
where K : notnull
|
||||||
{
|
{
|
||||||
@ -38,5 +44,15 @@ namespace Wabbajack
|
|||||||
dict[val.Key] = val.Value;
|
dict[val.Key] = val.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears the dictionary and adds the given values
|
||||||
|
/// </summary>
|
||||||
|
public static void SetTo<K, V>(this IDictionary<K, V> dict, IEnumerable<KeyValuePair<K, V>> vals)
|
||||||
|
where K : notnull
|
||||||
|
{
|
||||||
|
dict.Clear();
|
||||||
|
dict.Set(vals);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,5 +33,32 @@ namespace Wabbajack
|
|||||||
yield return next;
|
yield return next;
|
||||||
foreach (var itm in coll) yield return itm;
|
foreach (var itm in coll) yield return itm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts and filters a nullable enumerable to a non-nullable enumerable
|
||||||
|
/// </summary>
|
||||||
|
public static IEnumerable<T> NotNull<T>(this IEnumerable<T?> e)
|
||||||
|
where T : class
|
||||||
|
{
|
||||||
|
// Filter out nulls
|
||||||
|
return e.Where(e => e != null)
|
||||||
|
// Cast to non nullable type
|
||||||
|
.Select(e => e!);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Selects items that are castable to the desired type
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of the original enumerable to cast from</typeparam>
|
||||||
|
/// <typeparam name="R">Type to attempt casting to</typeparam>
|
||||||
|
/// <param name="e">Enumerable to process</param>
|
||||||
|
/// <returns>Enumerable with only objects that were castable</returns>
|
||||||
|
public static IEnumerable<R> WhereCastable<T, R>(this IEnumerable<T> e)
|
||||||
|
where T : class
|
||||||
|
where R : T
|
||||||
|
{
|
||||||
|
return e.Where(e => e is R)
|
||||||
|
.Select(e => (R)e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
15
Wabbajack.Common/Extensions/ListExt.cs
Normal file
15
Wabbajack.Common/Extensions/ListExt.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Wabbajack.Common
|
||||||
|
{
|
||||||
|
public static class ListExt
|
||||||
|
{
|
||||||
|
public static void SetTo<T>(this List<T> list, IEnumerable<T> rhs)
|
||||||
|
{
|
||||||
|
list.Clear();
|
||||||
|
list.AddRange(rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Alphaleonis.Win32.Filesystem;
|
using Alphaleonis.Win32.Filesystem;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
@ -85,23 +86,44 @@ namespace Wabbajack.Common
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
AbsolutePath? gameLoc = GameLocation();
|
if (!TryGetGameLocation(out var gameLoc))
|
||||||
if (gameLoc == null)
|
|
||||||
throw new GameNotInstalledException(this);
|
throw new GameNotInstalledException(this);
|
||||||
if (MainExecutable == null)
|
if (MainExecutable == null)
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
|
|
||||||
return FileVersionInfo.GetVersionInfo((string)gameLoc.Value.Combine(MainExecutable)).ProductVersion;
|
return FileVersionInfo.GetVersionInfo((string)gameLoc.Combine(MainExecutable)).ProductVersion;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsInstalled => GameLocation() != null;
|
public bool IsInstalled => TryGetGameLocation() != null;
|
||||||
|
|
||||||
public string? MainExecutable { get; internal set; }
|
public string? MainExecutable { get; internal set; }
|
||||||
|
|
||||||
public AbsolutePath? GameLocation()
|
public AbsolutePath? TryGetGameLocation()
|
||||||
{
|
{
|
||||||
return Consts.TestMode ? AbsolutePath.GetCurrentDirectory() : StoreHandler.Instance.GetGamePath(Game);
|
return Consts.TestMode ? AbsolutePath.GetCurrentDirectory() : StoreHandler.Instance.TryGetGamePath(Game);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetGameLocation(out AbsolutePath path)
|
||||||
|
{
|
||||||
|
var ret = TryGetGameLocation();
|
||||||
|
if (ret != null)
|
||||||
|
{
|
||||||
|
path = ret.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
path = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AbsolutePath GameLocation()
|
||||||
|
{
|
||||||
|
var ret = TryGetGameLocation();
|
||||||
|
if (ret == null) throw new ArgumentNullException();
|
||||||
|
return ret.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,16 +178,24 @@ namespace Wabbajack.Common
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to parse game data from an arbitrary string. Tries first via parsing as a game Enum, then by Nexus name,
|
/// Parse game data from an arbitrary string. Tries first via parsing as a game Enum, then by Nexus name.
|
||||||
/// <param nambe="someName"></param>
|
/// <param nambe="someName">Name to query</param>
|
||||||
/// <returns></returns>
|
/// <returns>GameMetaData found</returns>
|
||||||
public static GameMetaData? GetByFuzzyName(string someName)
|
/// <exception cref="ArgumentNullException">If string could not be translated to a game</exception>
|
||||||
|
public static GameMetaData GetByFuzzyName(string someName)
|
||||||
|
{
|
||||||
|
return TryGetByFuzzyName(someName) ?? throw new ArgumentNullException($"{someName} could not be translated to a game");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to parse game data from an arbitrary string. Tries first via parsing as a game Enum, then by Nexus name.
|
||||||
|
/// <param nambe="someName">Name to query</param>
|
||||||
|
/// <returns>GameMetaData if found, otherwise null</returns>
|
||||||
|
public static GameMetaData? TryGetByFuzzyName(string someName)
|
||||||
{
|
{
|
||||||
if (Enum.TryParse(typeof(Game), someName, true, out var metadata)) return ((Game)metadata!).MetaData();
|
if (Enum.TryParse(typeof(Game), someName, true, out var metadata)) return ((Game)metadata!).MetaData();
|
||||||
|
|
||||||
GameMetaData? result = null;
|
GameMetaData? result = GetByNexusName(someName);
|
||||||
|
|
||||||
result = GetByNexusName(someName);
|
|
||||||
if (result != null) return result;
|
if (result != null) return result;
|
||||||
|
|
||||||
result = GetByMO2ArchiveName(someName);
|
result = GetByMO2ArchiveName(someName);
|
||||||
@ -174,6 +204,12 @@ namespace Wabbajack.Common
|
|||||||
return int.TryParse(someName, out int id) ? GetBySteamID(id) : null;
|
return int.TryParse(someName, out int id) ? GetBySteamID(id) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool TryGetByFuzzyName(string someName, [MaybeNullWhen(false)] out GameMetaData gameMetaData)
|
||||||
|
{
|
||||||
|
gameMetaData = TryGetByFuzzyName(someName);
|
||||||
|
return gameMetaData != null;
|
||||||
|
}
|
||||||
|
|
||||||
public static IReadOnlyDictionary<Game, GameMetaData> Games = new Dictionary<Game, GameMetaData>
|
public static IReadOnlyDictionary<Game, GameMetaData> Games = new Dictionary<Game, GameMetaData>
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
|
@ -11,7 +11,7 @@ namespace Wabbajack.Common.Http
|
|||||||
{
|
{
|
||||||
public class Client
|
public class Client
|
||||||
{
|
{
|
||||||
public List<(string, string)> Headers = new List<(string, string)>();
|
public List<(string, string?)> Headers = new List<(string, string?)>();
|
||||||
public List<Cookie> Cookies = new List<Cookie>();
|
public List<Cookie> Cookies = new List<Cookie>();
|
||||||
public async Task<HttpResponseMessage> GetAsync(string url, HttpCompletionOption responseHeadersRead = HttpCompletionOption.ResponseHeadersRead)
|
public async Task<HttpResponseMessage> GetAsync(string url, HttpCompletionOption responseHeadersRead = HttpCompletionOption.ResponseHeadersRead)
|
||||||
{
|
{
|
||||||
|
@ -62,16 +62,12 @@ namespace Wabbajack.Common
|
|||||||
return JsonConvert.SerializeObject(obj, JsonSettings);
|
return JsonConvert.SerializeObject(obj, JsonSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T FromJson<T>(this AbsolutePath filename,
|
public static T FromJson<T>(this AbsolutePath filename)
|
||||||
TypeNameHandling handling = TypeNameHandling.All,
|
|
||||||
TypeNameAssemblyFormatHandling format = TypeNameAssemblyFormatHandling.Full)
|
|
||||||
{
|
{
|
||||||
return JsonConvert.DeserializeObject<T>(filename.ReadAllText(), JsonSettings)!;
|
return JsonConvert.DeserializeObject<T>(filename.ReadAllText(), JsonSettings)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T FromJsonString<T>(this string data,
|
public static T FromJsonString<T>(this string data)
|
||||||
TypeNameHandling handling = TypeNameHandling.Objects,
|
|
||||||
TypeNameAssemblyFormatHandling format = TypeNameAssemblyFormatHandling.Full)
|
|
||||||
{
|
{
|
||||||
return JsonConvert.DeserializeObject<T>(data, JsonSettings)!;
|
return JsonConvert.DeserializeObject<T>(data, JsonSettings)!;
|
||||||
}
|
}
|
||||||
@ -230,8 +226,7 @@ namespace Wabbajack.Common
|
|||||||
return (Game)i;
|
return (Game)i;
|
||||||
}
|
}
|
||||||
|
|
||||||
GameMetaData? game = GameRegistry.GetByFuzzyName(str);
|
if (!GameRegistry.TryGetByFuzzyName(str, out var game))
|
||||||
if (game == null)
|
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"Could not convert {str} to a Game type.");
|
throw new ArgumentException($"Could not convert {str} to a Game type.");
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using System.Security;
|
using System.Security;
|
||||||
using DynamicData;
|
using DynamicData;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
namespace Wabbajack.Common.StoreHandlers
|
namespace Wabbajack.Common.StoreHandlers
|
||||||
{
|
{
|
||||||
@ -21,9 +22,14 @@ namespace Wabbajack.Common.StoreHandlers
|
|||||||
|
|
||||||
public class SteamWorkshopItem
|
public class SteamWorkshopItem
|
||||||
{
|
{
|
||||||
public SteamGame? Game;
|
public readonly SteamGame Game;
|
||||||
public int ItemID;
|
public int ItemID;
|
||||||
public int Size;
|
public int Size;
|
||||||
|
|
||||||
|
public SteamWorkshopItem(SteamGame game)
|
||||||
|
{
|
||||||
|
Game = game;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SteamHandler : AStoreHandler
|
public class SteamHandler : AStoreHandler
|
||||||
@ -202,14 +208,14 @@ namespace Wabbajack.Common.StoreHandlers
|
|||||||
var bracketStart = 0;
|
var bracketStart = 0;
|
||||||
var bracketEnd = 0;
|
var bracketEnd = 0;
|
||||||
|
|
||||||
SteamWorkshopItem? currentItem = new SteamWorkshopItem();
|
SteamWorkshopItem? currentItem = new SteamWorkshopItem(game);
|
||||||
|
|
||||||
lines.Do(l =>
|
lines.Do(l =>
|
||||||
{
|
{
|
||||||
if (end)
|
if (end)
|
||||||
return;
|
return;
|
||||||
if (currentItem == null)
|
if (currentItem == null)
|
||||||
currentItem = new SteamWorkshopItem();
|
currentItem = new SteamWorkshopItem(game);
|
||||||
|
|
||||||
var currentLine = lines.IndexOf(l);
|
var currentLine = lines.IndexOf(l);
|
||||||
if (l.ContainsCaseInsensitive("\"appid\"") && !foundAppID)
|
if (l.ContainsCaseInsensitive("\"appid\"") && !foundAppID)
|
||||||
@ -271,7 +277,6 @@ namespace Wabbajack.Common.StoreHandlers
|
|||||||
|
|
||||||
bracketStart = 0;
|
bracketStart = 0;
|
||||||
bracketEnd = 0;
|
bracketEnd = 0;
|
||||||
currentItem.Game = game;
|
|
||||||
game.WorkshopItems.Add(currentItem);
|
game.WorkshopItems.Add(currentItem);
|
||||||
|
|
||||||
Utils.Log($"Found Steam Workshop item {currentItem.ItemID}");
|
Utils.Log($"Found Steam Workshop item {currentItem.ItemID}");
|
||||||
|
@ -52,7 +52,7 @@ namespace Wabbajack.Common.StoreHandlers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbsolutePath? GetGamePath(Game game)
|
public AbsolutePath? TryGetGamePath(Game game)
|
||||||
{
|
{
|
||||||
return StoreGames.FirstOrDefault(g => g.Game == game)?.Path;
|
return StoreGames.FirstOrDefault(g => g.Game == game)?.Path;
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ namespace Wabbajack.Common
|
|||||||
private readonly BehaviorSubject<(int DesiredCPUs, int CurrentCPUs)> _cpuCountSubj = new BehaviorSubject<(int DesiredCPUs, int CurrentCPUs)>((0, 0));
|
private readonly BehaviorSubject<(int DesiredCPUs, int CurrentCPUs)> _cpuCountSubj = new BehaviorSubject<(int DesiredCPUs, int CurrentCPUs)>((0, 0));
|
||||||
public IObservable<(int CurrentCPUs, int DesiredCPUs)> CurrentCpuCount => _cpuCountSubj;
|
public IObservable<(int CurrentCPUs, int DesiredCPUs)> CurrentCpuCount => _cpuCountSubj;
|
||||||
|
|
||||||
private readonly Subject<IObservable<int>> _activeNumThreadsObservable = new Subject<IObservable<int>>();
|
private readonly Subject<IObservable<int>?> _activeNumThreadsObservable = new Subject<IObservable<int>?>();
|
||||||
|
|
||||||
public static TimeSpan PollMS = TimeSpan.FromMilliseconds(200);
|
public static TimeSpan PollMS = TimeSpan.FromMilliseconds(200);
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ namespace Wabbajack.Common
|
|||||||
/// Creates a WorkQueue whos number of threads is determined by the given observable
|
/// Creates a WorkQueue whos number of threads is determined by the given observable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="numThreads">Driving observable that determines how many threads should be actively pulling jobs from the queue</param>
|
/// <param name="numThreads">Driving observable that determines how many threads should be actively pulling jobs from the queue</param>
|
||||||
public WorkQueue(IObservable<int> numThreads)
|
public WorkQueue(IObservable<int>? numThreads)
|
||||||
{
|
{
|
||||||
// Hook onto the number of active threads subject, and subscribe to it for changes
|
// Hook onto the number of active threads subject, and subscribe to it for changes
|
||||||
_activeNumThreadsObservable
|
_activeNumThreadsObservable
|
||||||
@ -86,7 +86,7 @@ namespace Wabbajack.Common
|
|||||||
/// Sets the driving observable that determines how many threads should be actively pulling jobs from the queue
|
/// Sets the driving observable that determines how many threads should be actively pulling jobs from the queue
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="numThreads">Driving observable that determines how many threads should be actively pulling jobs from the queue</param>
|
/// <param name="numThreads">Driving observable that determines how many threads should be actively pulling jobs from the queue</param>
|
||||||
public void SetActiveThreadsObservable(IObservable<int> numThreads)
|
public void SetActiveThreadsObservable(IObservable<int>? numThreads)
|
||||||
{
|
{
|
||||||
_activeNumThreadsObservable.OnNext(numThreads);
|
_activeNumThreadsObservable.OnNext(numThreads);
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,9 @@ namespace Wabbajack.Lib
|
|||||||
{
|
{
|
||||||
public WorkQueue Queue { get; } = new WorkQueue();
|
public WorkQueue Queue { get; } = new WorkQueue();
|
||||||
|
|
||||||
public Context VFS { get; private set; }
|
public Context VFS { get; }
|
||||||
|
|
||||||
protected StatusUpdateTracker UpdateTracker { get; private set; }
|
protected StatusUpdateTracker UpdateTracker { get; }
|
||||||
|
|
||||||
private Subject<Percent> _percentCompleted { get; } = new Subject<Percent>();
|
private Subject<Percent> _percentCompleted { get; } = new Subject<Percent>();
|
||||||
|
|
||||||
@ -42,7 +42,6 @@ namespace Wabbajack.Lib
|
|||||||
private Subject<bool> _isRunning { get; } = new Subject<bool>();
|
private Subject<bool> _isRunning { get; } = new Subject<bool>();
|
||||||
public IObservable<bool> IsRunning => _isRunning;
|
public IObservable<bool> IsRunning => _isRunning;
|
||||||
|
|
||||||
private int _configured;
|
|
||||||
private int _started;
|
private int _started;
|
||||||
private readonly CancellationTokenSource _cancel = new CancellationTokenSource();
|
private readonly CancellationTokenSource _cancel = new CancellationTokenSource();
|
||||||
|
|
||||||
@ -53,21 +52,16 @@ namespace Wabbajack.Lib
|
|||||||
public BehaviorSubject<byte> MaxCores = new BehaviorSubject<byte>(byte.MaxValue);
|
public BehaviorSubject<byte> MaxCores = new BehaviorSubject<byte>(byte.MaxValue);
|
||||||
public BehaviorSubject<Percent> TargetUsagePercent = new BehaviorSubject<Percent>(Percent.One);
|
public BehaviorSubject<Percent> TargetUsagePercent = new BehaviorSubject<Percent>(Percent.One);
|
||||||
|
|
||||||
protected void ConfigureProcessor(int steps, IObservable<int> numThreads = null)
|
public ABatchProcessor(int steps)
|
||||||
{
|
{
|
||||||
if (1 == Interlocked.CompareExchange(ref _configured, 1, 1))
|
|
||||||
{
|
|
||||||
throw new InvalidDataException("Can't configure a processor twice");
|
|
||||||
}
|
|
||||||
Queue.SetActiveThreadsObservable(numThreads);
|
|
||||||
UpdateTracker = new StatusUpdateTracker(steps);
|
UpdateTracker = new StatusUpdateTracker(steps);
|
||||||
|
VFS = new Context(Queue) { UpdateTracker = UpdateTracker };
|
||||||
Queue.Status.Subscribe(_queueStatus)
|
Queue.Status.Subscribe(_queueStatus)
|
||||||
.DisposeWith(_subs);
|
.DisposeWith(_subs);
|
||||||
Queue.LogMessages.Subscribe(_logMessages)
|
Queue.LogMessages.Subscribe(_logMessages)
|
||||||
.DisposeWith(_subs);
|
.DisposeWith(_subs);
|
||||||
UpdateTracker.Progress.Subscribe(_percentCompleted);
|
UpdateTracker.Progress.Subscribe(_percentCompleted);
|
||||||
UpdateTracker.StepName.Subscribe(_textStatus);
|
UpdateTracker.StepName.Subscribe(_textStatus);
|
||||||
VFS = new Context(Queue) { UpdateTracker = UpdateTracker };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
@ -19,10 +19,10 @@ namespace Wabbajack.Lib
|
|||||||
{
|
{
|
||||||
public abstract class ACompiler : ABatchProcessor
|
public abstract class ACompiler : ABatchProcessor
|
||||||
{
|
{
|
||||||
public string ModListName, ModListAuthor, ModListDescription, ModListWebsite;
|
public string? ModListName, ModListAuthor, ModListDescription, ModListWebsite;
|
||||||
public AbsolutePath ModListImage, ModListReadme;
|
public AbsolutePath ModListImage, ModListReadme;
|
||||||
public bool ReadmeIsWebsite;
|
public bool ReadmeIsWebsite;
|
||||||
protected Version WabbajackVersion;
|
protected Version? WabbajackVersion;
|
||||||
|
|
||||||
public abstract AbsolutePath VFSCacheName { get; }
|
public abstract AbsolutePath VFSCacheName { get; }
|
||||||
//protected string VFSCacheName => Path.Combine(Consts.LocalAppDataPath, $"vfs_compile_cache.bin");
|
//protected string VFSCacheName => Path.Combine(Consts.LocalAppDataPath, $"vfs_compile_cache.bin");
|
||||||
@ -42,14 +42,19 @@ namespace Wabbajack.Lib
|
|||||||
|
|
||||||
public bool IgnoreMissingFiles { get; set; }
|
public bool IgnoreMissingFiles { get; set; }
|
||||||
|
|
||||||
public ICollection<Archive> SelectedArchives = new List<Archive>();
|
public List<Archive> SelectedArchives { get; protected set; } = new List<Archive>();
|
||||||
public List<Directive> InstallDirectives = new List<Directive>();
|
public List<Directive> InstallDirectives { get; protected set; } = new List<Directive>();
|
||||||
public List<RawSourceFile> AllFiles = new List<RawSourceFile>();
|
public List<RawSourceFile> AllFiles { get; protected set; } = new List<RawSourceFile>();
|
||||||
public ModList ModList = new ModList();
|
public ModList ModList = new ModList();
|
||||||
|
|
||||||
public List<IndexedArchive> IndexedArchives = new List<IndexedArchive>();
|
public List<IndexedArchive> IndexedArchives = new List<IndexedArchive>();
|
||||||
public Dictionary<Hash, IEnumerable<VirtualFile>> IndexedFiles = new Dictionary<Hash, IEnumerable<VirtualFile>>();
|
public Dictionary<Hash, IEnumerable<VirtualFile>> IndexedFiles = new Dictionary<Hash, IEnumerable<VirtualFile>>();
|
||||||
|
|
||||||
|
public ACompiler(int steps)
|
||||||
|
: base(steps)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public static void Info(string msg)
|
public static void Info(string msg)
|
||||||
{
|
{
|
||||||
Utils.Log(msg);
|
Utils.Log(msg);
|
||||||
@ -216,7 +221,7 @@ namespace Wabbajack.Lib
|
|||||||
.GroupBy(f => f.File.Hash)
|
.GroupBy(f => f.File.Hash)
|
||||||
.ToDictionary(f => f.Key, f => f.First());
|
.ToDictionary(f => f.Key, f => f.First());
|
||||||
|
|
||||||
SelectedArchives = await hashes.PMap(Queue, hash => ResolveArchive(hash, archives));
|
SelectedArchives.SetTo(await hashes.PMap(Queue, hash => ResolveArchive(hash, archives)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Archive> ResolveArchive(Hash hash, IDictionary<Hash, IndexedArchive> archives)
|
public async Task<Archive> ResolveArchive(Hash hash, IDictionary<Hash, IndexedArchive> archives)
|
||||||
@ -226,8 +231,7 @@ namespace Wabbajack.Lib
|
|||||||
return await ResolveArchive(found);
|
return await ResolveArchive(found);
|
||||||
}
|
}
|
||||||
|
|
||||||
Error($"No match found for Archive sha: {hash.ToBase64()} this shouldn't happen");
|
throw new ArgumentException($"No match found for Archive sha: {hash.ToBase64()} this shouldn't happen");
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Archive> ResolveArchive(IndexedArchive archive)
|
public async Task<Archive> ResolveArchive(IndexedArchive archive)
|
||||||
@ -238,10 +242,7 @@ namespace Wabbajack.Lib
|
|||||||
Error(
|
Error(
|
||||||
$"No download metadata found for {archive.Name}, please use MO2 to query info or add a .meta file and try again.");
|
$"No download metadata found for {archive.Name}, please use MO2 to query info or add a .meta file and try again.");
|
||||||
|
|
||||||
var result = new Archive
|
var result = new Archive(await DownloadDispatcher.ResolveArchive(archive.IniData));
|
||||||
{
|
|
||||||
State = await DownloadDispatcher.ResolveArchive(archive.IniData)
|
|
||||||
};
|
|
||||||
|
|
||||||
if (result.State == null)
|
if (result.State == null)
|
||||||
Error($"{archive.Name} could not be handled by any of the downloaders");
|
Error($"{archive.Name} could not be handled by any of the downloaders");
|
||||||
|
@ -27,11 +27,12 @@ namespace Wabbajack.Lib
|
|||||||
|
|
||||||
public AbsolutePath ModListArchive { get; private set; }
|
public AbsolutePath ModListArchive { get; private set; }
|
||||||
public ModList ModList { get; private set; }
|
public ModList ModList { get; private set; }
|
||||||
public Dictionary<Hash, AbsolutePath> HashedArchives { get; set; }
|
public Dictionary<Hash, AbsolutePath> HashedArchives { get; } = new Dictionary<Hash, AbsolutePath>();
|
||||||
|
|
||||||
public SystemParameters SystemParameters { get; set; }
|
public SystemParameters? SystemParameters { get; set; }
|
||||||
|
|
||||||
public AInstaller(AbsolutePath archive, ModList modList, AbsolutePath outputFolder, AbsolutePath downloadFolder, SystemParameters parameters)
|
public AInstaller(AbsolutePath archive, ModList modList, AbsolutePath outputFolder, AbsolutePath downloadFolder, SystemParameters? parameters, int steps)
|
||||||
|
: base(steps)
|
||||||
{
|
{
|
||||||
ModList = modList;
|
ModList = modList;
|
||||||
ModListArchive = archive;
|
ModListArchive = archive;
|
||||||
@ -166,6 +167,10 @@ namespace Wabbajack.Lib
|
|||||||
.PDoIndexed(queue, async (idx, group) =>
|
.PDoIndexed(queue, async (idx, group) =>
|
||||||
{
|
{
|
||||||
Utils.Status("Installing files", Percent.FactoryPutInRange(idx, vFiles.Count));
|
Utils.Status("Installing files", Percent.FactoryPutInRange(idx, vFiles.Count));
|
||||||
|
if (group.Key == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("FromFile was null");
|
||||||
|
}
|
||||||
var firstDest = OutputFolder.Combine(group.First().To);
|
var firstDest = OutputFolder.Combine(group.First().To);
|
||||||
await CopyFile(group.Key.StagedPath, firstDest, true);
|
await CopyFile(group.Key.StagedPath, firstDest, true);
|
||||||
|
|
||||||
@ -173,7 +178,6 @@ namespace Wabbajack.Lib
|
|||||||
{
|
{
|
||||||
await CopyFile(firstDest, OutputFolder.Combine(copy.To), false);
|
await CopyFile(firstDest, OutputFolder.Combine(copy.To), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Status("Unstaging files");
|
Status("Unstaging files");
|
||||||
@ -278,11 +282,11 @@ namespace Wabbajack.Lib
|
|||||||
var hashResults = await DownloadFolder.EnumerateFiles()
|
var hashResults = await DownloadFolder.EnumerateFiles()
|
||||||
.Where(e => e.Extension != Consts.HashFileExtension)
|
.Where(e => e.Extension != Consts.HashFileExtension)
|
||||||
.PMap(Queue, async e => (await e.FileHashCachedAsync(), e));
|
.PMap(Queue, async e => (await e.FileHashCachedAsync(), e));
|
||||||
HashedArchives = hashResults
|
HashedArchives.SetTo(hashResults
|
||||||
.OrderByDescending(e => e.Item2.LastModified)
|
.OrderByDescending(e => e.Item2.LastModified)
|
||||||
.GroupBy(e => e.Item1)
|
.GroupBy(e => e.Item1)
|
||||||
.Select(e => e.First())
|
.Select(e => e.First())
|
||||||
.ToDictionary(e => e.Item1, e => e.Item2);
|
.Select(e => new KeyValuePair<Hash, AbsolutePath>(e.Item1, e.Item2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -391,8 +395,13 @@ namespace Wabbajack.Lib
|
|||||||
|
|
||||||
return await path.FileHashAsync() == d.Hash ? d : null;
|
return await path.FileHashAsync() == d.Hash ? d : null;
|
||||||
}))
|
}))
|
||||||
.Where(d => d != null)
|
.Do(d =>
|
||||||
.Do(d => indexed.Remove(d.To));
|
{
|
||||||
|
if (d != null)
|
||||||
|
{
|
||||||
|
indexed.Remove(d.To);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
UpdateTracker.NextStep("Updating ModList");
|
UpdateTracker.NextStep("Updating ModList");
|
||||||
Utils.Log($"Optimized {ModList.Directives.Count} directives to {indexed.Count} required");
|
Utils.Log($"Optimized {ModList.Directives.Count} directives to {indexed.Count} required");
|
||||||
|
@ -14,7 +14,7 @@ namespace Wabbajack.Lib
|
|||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<Archive> GetModUpgrade(Hash hash)
|
public static async Task<Archive?> GetModUpgrade(Hash hash)
|
||||||
{
|
{
|
||||||
using var response = await GetClient()
|
using var response = await GetClient()
|
||||||
.GetAsync($"{Consts.WabbajackBuildServerUri}alternative/{hash.ToHex()}");
|
.GetAsync($"{Consts.WabbajackBuildServerUri}alternative/{hash.ToHex()}");
|
||||||
@ -33,7 +33,7 @@ namespace Wabbajack.Lib
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="hash"></param>
|
/// <param name="hash"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static async Task<string> GetModIni(Hash hash)
|
public static async Task<string?> GetModIni(Hash hash)
|
||||||
{
|
{
|
||||||
var client = new Common.Http.Client();
|
var client = new Common.Http.Client();
|
||||||
try
|
try
|
||||||
|
@ -11,7 +11,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
_compiler = compiler;
|
_compiler = compiler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract ValueTask<Directive> Run(RawSourceFile source);
|
public abstract ValueTask<Directive?> Run(RawSourceFile source);
|
||||||
public abstract IState GetState();
|
public abstract IState GetState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
return new State();
|
return new State();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
if (!Consts.SupportedBSAs.Contains(source.Path.Extension)) return null;
|
if (!Consts.SupportedBSAs.Contains(source.Path.Extension)) return null;
|
||||||
|
|
||||||
@ -79,12 +79,12 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
CreateBSA directive;
|
CreateBSA directive;
|
||||||
using (var bsa = BSADispatch.OpenRead(source.AbsolutePath))
|
using (var bsa = BSADispatch.OpenRead(source.AbsolutePath))
|
||||||
{
|
{
|
||||||
directive = new CreateBSA
|
directive = new CreateBSA(
|
||||||
|
state: bsa.State,
|
||||||
|
items: bsa.Files.Select(f => f.State).ToList())
|
||||||
{
|
{
|
||||||
To = source.Path,
|
To = source.Path,
|
||||||
TempID = (RelativePath)id,
|
TempID = (RelativePath)id,
|
||||||
State = bsa.State,
|
|
||||||
FileStates = bsa.Files.Select(f => f.State).ToList()
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
if (!_compiler.IndexedFiles.TryGetValue(source.Hash, out var found)) return null;
|
if (!_compiler.IndexedFiles.TryGetValue(source.Hash, out var found)) return null;
|
||||||
var result = source.EvolveTo<FromArchive>();
|
var result = source.EvolveTo<FromArchive>();
|
||||||
|
@ -10,7 +10,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
var result = source.EvolveTo<NoMatch>();
|
var result = source.EvolveTo<NoMatch>();
|
||||||
result.Reason = "No Match in Stack";
|
result.Reason = "No Match in Stack";
|
||||||
|
@ -4,7 +4,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
{
|
{
|
||||||
public interface ICompilationStep
|
public interface ICompilationStep
|
||||||
{
|
{
|
||||||
ValueTask<Directive> Run(RawSourceFile source);
|
ValueTask<Directive?> Run(RawSourceFile source);
|
||||||
IState GetState();
|
IState GetState();
|
||||||
}
|
}
|
||||||
|
|
@ -25,7 +25,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
if (!source.AbsolutePath.InFolder(_mo2Compiler.MO2ModsFolder)) return null;
|
if (!source.AbsolutePath.InFolder(_mo2Compiler.MO2ModsFolder)) return null;
|
||||||
if (_allEnabledMods.Any(mod => source.AbsolutePath.InFolder(mod)))
|
if (_allEnabledMods.Any(mod => source.AbsolutePath.InFolder(mod)))
|
||||||
|
@ -14,7 +14,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
_reason = $"Ignored because path ends with {postfix}";
|
_reason = $"Ignored because path ends with {postfix}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
if (!((string)source.Path).EndsWith(_postfix)) return null;
|
if (!((string)source.Path).EndsWith(_postfix)) return null;
|
||||||
var result = source.EvolveTo<IgnoredDirectly>();
|
var result = source.EvolveTo<IgnoredDirectly>();
|
||||||
@ -30,17 +30,13 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
[JsonObject("IgnoreEndsWith")]
|
[JsonObject("IgnoreEndsWith")]
|
||||||
public class State : IState
|
public class State : IState
|
||||||
{
|
{
|
||||||
|
public string Postfix { get; set; }
|
||||||
|
|
||||||
public State(string postfix)
|
public State(string postfix)
|
||||||
{
|
{
|
||||||
Postfix = postfix;
|
Postfix = postfix;
|
||||||
}
|
}
|
||||||
|
|
||||||
public State()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Postfix { get; set; }
|
|
||||||
|
|
||||||
public ICompilationStep CreateStep(ACompiler compiler)
|
public ICompilationStep CreateStep(ACompiler compiler)
|
||||||
{
|
{
|
||||||
return new IgnoreEndsWith(compiler, Postfix);
|
return new IgnoreEndsWith(compiler, Postfix);
|
||||||
|
@ -13,7 +13,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
_startDir = Consts.GameFolderFilesDir + "\\";
|
_startDir = Consts.GameFolderFilesDir + "\\";
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
if (!((string)source.Path).StartsWith(_startDir)) return null;
|
if (!((string)source.Path).StartsWith(_startDir)) return null;
|
||||||
var i = source.EvolveTo<IgnoredDirectly>();
|
var i = source.EvolveTo<IgnoredDirectly>();
|
||||||
|
@ -15,7 +15,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
_gameFolder = compiler.GamePath;
|
_gameFolder = compiler.GamePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
if (_gameFolderFilesExists)
|
if (_gameFolderFilesExists)
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
_reason = $"Ignored because path contains {_pattern}";
|
_reason = $"Ignored because path contains {_pattern}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
if (!((string)source.Path).Contains(_pattern)) return null;
|
if (!((string)source.Path).Contains(_pattern)) return null;
|
||||||
var result = source.EvolveTo<IgnoredDirectly>();
|
var result = source.EvolveTo<IgnoredDirectly>();
|
||||||
@ -30,17 +30,13 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
[JsonObject("IgnorePathContains")]
|
[JsonObject("IgnorePathContains")]
|
||||||
public class State : IState
|
public class State : IState
|
||||||
{
|
{
|
||||||
public State()
|
public string Pattern { get; set; }
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public State(string pattern)
|
public State(string pattern)
|
||||||
{
|
{
|
||||||
Pattern = pattern;
|
Pattern = pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Pattern { get; set; }
|
|
||||||
|
|
||||||
public ICompilationStep CreateStep(ACompiler compiler)
|
public ICompilationStep CreateStep(ACompiler compiler)
|
||||||
{
|
{
|
||||||
return new IgnorePathContains(compiler, Pattern);
|
return new IgnorePathContains(compiler, Pattern);
|
||||||
|
@ -17,7 +17,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
_regex = new Regex(pattern);
|
_regex = new Regex(pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
if (!_regex.IsMatch((string)source.Path)) return null;
|
if (!_regex.IsMatch((string)source.Path)) return null;
|
||||||
var result = source.EvolveTo<IgnoredDirectly>();
|
var result = source.EvolveTo<IgnoredDirectly>();
|
||||||
@ -33,17 +33,13 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
[JsonObject("IgnorePattern")]
|
[JsonObject("IgnorePattern")]
|
||||||
public class State : IState
|
public class State : IState
|
||||||
{
|
{
|
||||||
public State()
|
public string Pattern { get; set; }
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public State(string pattern)
|
public State(string pattern)
|
||||||
{
|
{
|
||||||
Pattern = pattern;
|
Pattern = pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Pattern { get; set; }
|
|
||||||
|
|
||||||
public ICompilationStep CreateStep(ACompiler compiler)
|
public ICompilationStep CreateStep(ACompiler compiler)
|
||||||
{
|
{
|
||||||
return new IgnoreRegex(compiler, Pattern);
|
return new IgnoreRegex(compiler, Pattern);
|
||||||
|
@ -14,7 +14,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
_reason = string.Format("Ignored because path starts with {0}", _prefix);
|
_reason = string.Format("Ignored because path starts with {0}", _prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
if (!((string)source.Path).StartsWith(_prefix))
|
if (!((string)source.Path).StartsWith(_prefix))
|
||||||
{
|
{
|
||||||
@ -35,17 +35,13 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
[JsonObject("IgnoreStartsWith")]
|
[JsonObject("IgnoreStartsWith")]
|
||||||
public class State : IState
|
public class State : IState
|
||||||
{
|
{
|
||||||
public State()
|
public string Prefix { get; set; }
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public State(string prefix)
|
public State(string prefix)
|
||||||
{
|
{
|
||||||
Prefix = prefix;
|
Prefix = prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Prefix { get; set; }
|
|
||||||
|
|
||||||
public ICompilationStep CreateStep(ACompiler compiler)
|
public ICompilationStep CreateStep(ACompiler compiler)
|
||||||
{
|
{
|
||||||
return new IgnoreStartsWith(compiler, Prefix);
|
return new IgnoreStartsWith(compiler, Prefix);
|
||||||
|
@ -19,7 +19,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
if (!_cruftFiles.Any(f => source.Path.StartsWith(f))) return null;
|
if (!_cruftFiles.Any(f => source.Path.StartsWith(f))) return null;
|
||||||
var result = source.EvolveTo<IgnoredDirectly>();
|
var result = source.EvolveTo<IgnoredDirectly>();
|
||||||
|
@ -10,7 +10,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
var inline = source.EvolveTo<InlineFile>();
|
var inline = source.EvolveTo<InlineFile>();
|
||||||
inline.SourceDataID = await _compiler.IncludeFile(await source.AbsolutePath.ReadAllBytesAsync());
|
inline.SourceDataID = await _compiler.IncludeFile(await source.AbsolutePath.ReadAllBytesAsync());
|
||||||
|
@ -11,7 +11,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
if (!Consts.ConfigFileExtensions.Contains(source.Path.Extension)) return null;
|
if (!Consts.ConfigFileExtensions.Contains(source.Path.Extension)) return null;
|
||||||
var result = source.EvolveTo<InlineFile>();
|
var result = source.EvolveTo<InlineFile>();
|
||||||
|
@ -11,7 +11,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
if (source.AbsolutePath.Extension != Consts.ESP &&
|
if (source.AbsolutePath.Extension != Consts.ESP &&
|
||||||
source.AbsolutePath.Extension != Consts.ESM) return null;
|
source.AbsolutePath.Extension != Consts.ESM) return null;
|
||||||
|
@ -14,7 +14,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
_prefix = Consts.LOOTFolderFilesDir + "\\";
|
_prefix = Consts.LOOTFolderFilesDir + "\\";
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
if (!source.Path.StartsWith(_prefix)) return null;
|
if (!source.Path.StartsWith(_prefix)) return null;
|
||||||
var result = source.EvolveTo<InlineFile>();
|
var result = source.EvolveTo<InlineFile>();
|
||||||
|
@ -11,7 +11,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
if (!source.Path.StartsWith("mods\\") || source.Path.FileName != Consts.MetaIni) return null;
|
if (!source.Path.StartsWith("mods\\") || source.Path.FileName != Consts.MetaIni) return null;
|
||||||
var e = source.EvolveTo<InlineFile>();
|
var e = source.EvolveTo<InlineFile>();
|
||||||
|
@ -23,7 +23,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
if (!source.AbsolutePath.InFolder(_modProfilesFolder)) return null;
|
if (!source.AbsolutePath.InFolder(_modProfilesFolder)) return null;
|
||||||
if (_profiles.Any(profile => source.AbsolutePath.InFolder(profile))) return null;
|
if (_profiles.Any(profile => source.AbsolutePath.InFolder(profile))) return null;
|
||||||
|
@ -12,11 +12,11 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
public class IncludePatches : ACompilationStep
|
public class IncludePatches : ACompilationStep
|
||||||
{
|
{
|
||||||
private readonly Dictionary<RelativePath, IGrouping<RelativePath, VirtualFile>> _indexed;
|
private readonly Dictionary<RelativePath, IGrouping<RelativePath, VirtualFile>> _indexed;
|
||||||
private VirtualFile _bsa;
|
private VirtualFile? _bsa;
|
||||||
private Dictionary<RelativePath, VirtualFile> _indexedByName;
|
private Dictionary<RelativePath, VirtualFile> _indexedByName;
|
||||||
private MO2Compiler _mo2Compiler;
|
private MO2Compiler _mo2Compiler;
|
||||||
|
|
||||||
public IncludePatches(ACompiler compiler, VirtualFile constructingFromBSA = null) : base(compiler)
|
public IncludePatches(ACompiler compiler, VirtualFile? constructingFromBSA = null) : base(compiler)
|
||||||
{
|
{
|
||||||
_bsa = constructingFromBSA;
|
_bsa = constructingFromBSA;
|
||||||
_mo2Compiler = (MO2Compiler)compiler;
|
_mo2Compiler = (MO2Compiler)compiler;
|
||||||
@ -30,9 +30,8 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
.ToDictionary(f => f.FullPath.FileName);
|
.ToDictionary(f => f.FullPath.FileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
|
|
||||||
var name = source.File.Name.FileName;
|
var name = source.File.Name.FileName;
|
||||||
RelativePath nameWithoutExt = name;
|
RelativePath nameWithoutExt = name;
|
||||||
if (name.Extension == Consts.MOHIDDEN)
|
if (name.Extension == Consts.MOHIDDEN)
|
||||||
@ -41,7 +40,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
if (!_indexed.TryGetValue(name, out var choices))
|
if (!_indexed.TryGetValue(name, out var choices))
|
||||||
_indexed.TryGetValue(nameWithoutExt, out choices);
|
_indexed.TryGetValue(nameWithoutExt, out choices);
|
||||||
|
|
||||||
dynamic modIni = null;
|
dynamic? modIni = null;
|
||||||
if (source.AbsolutePath.InFolder(_mo2Compiler.MO2ModsFolder))
|
if (source.AbsolutePath.InFolder(_mo2Compiler.MO2ModsFolder))
|
||||||
{
|
{
|
||||||
if (_bsa == null)
|
if (_bsa == null)
|
||||||
@ -55,7 +54,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
|
|
||||||
var installationFile = (RelativePath)modIni?.General?.installationFile;
|
var installationFile = (RelativePath)modIni?.General?.installationFile;
|
||||||
|
|
||||||
VirtualFile found = null;
|
VirtualFile? found = null;
|
||||||
|
|
||||||
// Find based on exact file name + ext
|
// Find based on exact file name + ext
|
||||||
if (choices != null)
|
if (choices != null)
|
||||||
|
@ -14,7 +14,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
var files = new HashSet<AbsolutePath>
|
var files = new HashSet<AbsolutePath>
|
||||||
{
|
{
|
||||||
|
@ -16,7 +16,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
_regex = new Regex(pattern);
|
_regex = new Regex(pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
if (!_regex.IsMatch((string)source.Path)) return null;
|
if (!_regex.IsMatch((string)source.Path)) return null;
|
||||||
|
|
||||||
@ -33,17 +33,13 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
[JsonObject("IncludeRegex")]
|
[JsonObject("IncludeRegex")]
|
||||||
public class State : IState
|
public class State : IState
|
||||||
{
|
{
|
||||||
public State()
|
public string Pattern { get; set; }
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public State(string pattern)
|
public State(string pattern)
|
||||||
{
|
{
|
||||||
Pattern = pattern;
|
Pattern = pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Pattern { get; set; }
|
|
||||||
|
|
||||||
public ICompilationStep CreateStep(ACompiler compiler)
|
public ICompilationStep CreateStep(ACompiler compiler)
|
||||||
{
|
{
|
||||||
return new IncludeRegex(compiler, Pattern);
|
return new IncludeRegex(compiler, Pattern);
|
||||||
|
@ -18,7 +18,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
_game = steamGame;
|
_game = steamGame;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
if (!_regex.IsMatch((string)source.Path))
|
if (!_regex.IsMatch((string)source.Path))
|
||||||
return null;
|
return null;
|
||||||
@ -31,7 +31,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
if (id == 0)
|
if (id == 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
SteamWorkshopItem item = null;
|
SteamWorkshopItem? item = null;
|
||||||
_game.WorkshopItems.Where(i => i.ItemID == id).Do(i => item = i);
|
_game.WorkshopItems.Where(i => i.ItemID == id).Do(i => item = i);
|
||||||
if (item == null)
|
if (item == null)
|
||||||
return null;
|
return null;
|
||||||
|
@ -15,7 +15,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
_mo2Compiler = (MO2Compiler) compiler;
|
_mo2Compiler = (MO2Compiler) compiler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
return Consts.ConfigFileExtensions.Contains(source.Path.Extension) ? await RemapFile(source) : null;
|
return Consts.ConfigFileExtensions.Contains(source.Path.Extension) ? await RemapFile(source) : null;
|
||||||
}
|
}
|
||||||
@ -25,7 +25,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
return new State();
|
return new State();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Directive> RemapFile(RawSourceFile source)
|
private async Task<Directive?> RemapFile(RawSourceFile source)
|
||||||
{
|
{
|
||||||
var data = await source.AbsolutePath.ReadAllTextAsync();
|
var data = await source.AbsolutePath.ReadAllTextAsync();
|
||||||
var originalData = data;
|
var originalData = data;
|
||||||
|
@ -26,7 +26,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
}).Select(kv => $"mods\\{kv.Key}\\");
|
}).Select(kv => $"mods\\{kv.Key}\\");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
if (!source.Path.StartsWith(Consts.MO2ModFolderName)) return null;
|
if (!source.Path.StartsWith(Consts.MO2ModFolderName)) return null;
|
||||||
foreach (var modpath in _includeDirectly)
|
foreach (var modpath in _includeDirectly)
|
||||||
@ -48,17 +48,13 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
[JsonObject("IncludeTaggedMods")]
|
[JsonObject("IncludeTaggedMods")]
|
||||||
public class State : IState
|
public class State : IState
|
||||||
{
|
{
|
||||||
public State()
|
public string Tag { get; set; }
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public State(string tag)
|
public State(string tag)
|
||||||
{
|
{
|
||||||
Tag = tag;
|
Tag = tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Tag { get; set; }
|
|
||||||
|
|
||||||
public ICompilationStep CreateStep(ACompiler compiler)
|
public ICompilationStep CreateStep(ACompiler compiler)
|
||||||
{
|
{
|
||||||
return new IncludeTaggedMods(compiler, Tag);
|
return new IncludeTaggedMods(compiler, Tag);
|
||||||
|
@ -19,7 +19,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
_correctProfiles = _mo2Compiler.SelectedProfiles.Select(p => _mo2Compiler.MO2ProfileDir.Parent.Combine(p)).ToList();
|
_correctProfiles = _mo2Compiler.SelectedProfiles.Select(p => _mo2Compiler.MO2ProfileDir.Parent.Combine(p)).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
if (!_correctProfiles.Any(p => source.AbsolutePath.InFolder(p)))
|
if (!_correctProfiles.Any(p => source.AbsolutePath.InFolder(p)))
|
||||||
return null;
|
return null;
|
||||||
|
@ -12,7 +12,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
// * TODO I don't know what this does
|
// * TODO I don't know what this does
|
||||||
/*
|
/*
|
||||||
|
@ -16,7 +16,7 @@ namespace Wabbajack.Lib.CompilationSteps
|
|||||||
_mo2Compiler = (MO2Compiler) compiler;
|
_mo2Compiler = (MO2Compiler) compiler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<Directive> Run(RawSourceFile source)
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
{
|
{
|
||||||
var filename = source.Path.FileName;
|
var filename = source.Path.FileName;
|
||||||
var gameFile = _mo2Compiler.GamePath.Combine((RelativePath)"Data", filename);
|
var gameFile = _mo2Compiler.GamePath.Combine((RelativePath)"Data", filename);
|
||||||
|
@ -43,22 +43,22 @@ namespace Wabbajack.Lib
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Archives required by this modlist
|
/// Archives required by this modlist
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<Archive> Archives;
|
public List<Archive> Archives = new List<Archive>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Author of the ModList
|
/// Author of the ModList
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Author;
|
public string Author = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Description of the ModList
|
/// Description of the ModList
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Description;
|
public string Description = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Install directives
|
/// Install directives
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<Directive> Directives;
|
public List<Directive> Directives = new List<Directive>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The game variant to which this game applies
|
/// The game variant to which this game applies
|
||||||
@ -78,12 +78,12 @@ namespace Wabbajack.Lib
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Name of the ModList
|
/// Name of the ModList
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name;
|
public string Name = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// readme path or website
|
/// readme path or website
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Readme;
|
public string Readme = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether readme is a website
|
/// Whether readme is a website
|
||||||
@ -93,12 +93,12 @@ namespace Wabbajack.Lib
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The build version of Wabbajack used when compiling the Modlist
|
/// The build version of Wabbajack used when compiling the Modlist
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Version WabbajackVersion;
|
public Version? WabbajackVersion;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Website of the ModList
|
/// Website of the ModList
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Uri Website;
|
public Uri? Website;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The size of all the archives once they're downloaded
|
/// The size of all the archives once they're downloaded
|
||||||
@ -134,7 +134,7 @@ namespace Wabbajack.Lib
|
|||||||
|
|
||||||
public class IgnoredDirectly : Directive
|
public class IgnoredDirectly : Directive
|
||||||
{
|
{
|
||||||
public string Reason;
|
public string Reason = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class NoMatch : IgnoredDirectly
|
public class NoMatch : IgnoredDirectly
|
||||||
@ -190,12 +190,12 @@ namespace Wabbajack.Lib
|
|||||||
[JsonName("FromArchive")]
|
[JsonName("FromArchive")]
|
||||||
public class FromArchive : Directive
|
public class FromArchive : Directive
|
||||||
{
|
{
|
||||||
private string _fullPath;
|
private string? _fullPath;
|
||||||
|
|
||||||
public HashRelativePath ArchiveHashPath { get; set; }
|
public HashRelativePath ArchiveHashPath { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public VirtualFile FromFile { get; set; }
|
public VirtualFile? FromFile { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public string FullPath => _fullPath ??= string.Join("|", ArchiveHashPath);
|
public string FullPath => _fullPath ??= string.Join("|", ArchiveHashPath);
|
||||||
@ -205,8 +205,17 @@ namespace Wabbajack.Lib
|
|||||||
public class CreateBSA : Directive
|
public class CreateBSA : Directive
|
||||||
{
|
{
|
||||||
public RelativePath TempID { get; set; }
|
public RelativePath TempID { get; set; }
|
||||||
public ArchiveStateObject State { get; set; }
|
public ArchiveStateObject State { get; }
|
||||||
public List<FileStateObject> FileStates { get; set; }
|
public List<FileStateObject> FileStates { get; set; } = new List<FileStateObject>();
|
||||||
|
|
||||||
|
public CreateBSA(ArchiveStateObject state, IEnumerable<FileStateObject>? items = null)
|
||||||
|
{
|
||||||
|
State = state;
|
||||||
|
if (items != null)
|
||||||
|
{
|
||||||
|
FileStates.AddRange(items);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonName("PatchedFromArchive")]
|
[JsonName("PatchedFromArchive")]
|
||||||
@ -231,7 +240,7 @@ namespace Wabbajack.Lib
|
|||||||
public class MergedPatch : Directive
|
public class MergedPatch : Directive
|
||||||
{
|
{
|
||||||
public RelativePath PatchID { get; set; }
|
public RelativePath PatchID { get; set; }
|
||||||
public List<SourcePatch> Sources { get; set; }
|
public List<SourcePatch> Sources { get; set; } = new List<SourcePatch>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonName("Archive")]
|
[JsonName("Archive")]
|
||||||
@ -245,49 +254,33 @@ namespace Wabbajack.Lib
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Meta INI for the downloaded archive
|
/// Meta INI for the downloaded archive
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Meta { get; set; }
|
public string? Meta { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Human friendly name of this archive
|
/// Human friendly name of this archive
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name { get; set; }
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
public long Size { get; set; }
|
public long Size { get; set; }
|
||||||
|
|
||||||
public AbstractDownloadState State { get; set; }
|
public AbstractDownloadState State { get; }
|
||||||
|
|
||||||
|
public Archive(AbstractDownloadState state)
|
||||||
|
{
|
||||||
|
State = state;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class IndexedArchive
|
public class IndexedArchive
|
||||||
{
|
{
|
||||||
public dynamic IniData;
|
public dynamic? IniData;
|
||||||
public string Meta;
|
public string Meta = string.Empty;
|
||||||
public string Name;
|
public string Name = string.Empty;
|
||||||
public VirtualFile File { get; internal set; }
|
public VirtualFile File { get; }
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
public IndexedArchive(VirtualFile file)
|
||||||
/// A archive entry
|
{
|
||||||
/// </summary>
|
File = file;
|
||||||
public class IndexedEntry
|
}
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// MurMur3 hash of this file
|
|
||||||
/// </summary>
|
|
||||||
public string Hash;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Path in the archive to this file
|
|
||||||
/// </summary>
|
|
||||||
public string Path;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Size of the file (uncompressed)
|
|
||||||
/// </summary>
|
|
||||||
public long Size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class IndexedArchiveEntry : IndexedEntry
|
|
||||||
{
|
|
||||||
public string[] HashPath;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,19 +11,18 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
public interface IMetaState
|
public interface IMetaState
|
||||||
{
|
{
|
||||||
Uri URL { get; }
|
Uri URL { get; }
|
||||||
string Name { get; set; }
|
string? Name { get; set; }
|
||||||
string Author { get; set; }
|
string? Author { get; set; }
|
||||||
string Version { get; set; }
|
string? Version { get; set; }
|
||||||
string ImageURL { get; set; }
|
string? ImageURL { get; set; }
|
||||||
bool IsNSFW { get; set; }
|
bool IsNSFW { get; set; }
|
||||||
string Description { get; set; }
|
string? Description { get; set; }
|
||||||
|
|
||||||
Task<bool> LoadMetaData();
|
Task<bool> LoadMetaData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class AbstractDownloadState
|
public abstract class AbstractDownloadState
|
||||||
{
|
{
|
||||||
|
|
||||||
public static List<Type> KnownSubTypes = new List<Type>
|
public static List<Type> KnownSubTypes = new List<Type>
|
||||||
{
|
{
|
||||||
typeof(HTTPDownloader.State),
|
typeof(HTTPDownloader.State),
|
||||||
@ -48,7 +47,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
|
|
||||||
static AbstractDownloadState()
|
static AbstractDownloadState()
|
||||||
{
|
{
|
||||||
NameToType = KnownSubTypes.ToDictionary(t => t.FullName.Substring(t.Namespace.Length + 1), t => t);
|
NameToType = KnownSubTypes.ToDictionary(t => t.FullName!.Substring(t.Namespace!.Length + 1), t => t);
|
||||||
TypeToName = NameToType.ToDictionary(k => k.Value, k => k.Key);
|
TypeToName = NameToType.ToDictionary(k => k.Value, k => k.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +67,6 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if this file is allowed to be downloaded via whitelist
|
/// Returns true if this file is allowed to be downloaded via whitelist
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -85,7 +83,9 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
public async Task<bool> Download(AbsolutePath destination)
|
public async Task<bool> Download(AbsolutePath destination)
|
||||||
{
|
{
|
||||||
destination.Parent.CreateDirectory();
|
destination.Parent.CreateDirectory();
|
||||||
return await Download(new Archive {Name = (string)destination.FileName}, destination);
|
// ToDo
|
||||||
|
// Is this null override needed? Why is state allowed to be null here?
|
||||||
|
return await Download(new Archive(state: null!) {Name = (string)destination.FileName}, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -96,7 +96,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
|
|
||||||
public abstract IDownloader GetDownloader();
|
public abstract IDownloader GetDownloader();
|
||||||
|
|
||||||
public abstract string GetManifestURL(Archive a);
|
public abstract string? GetManifestURL(Archive a);
|
||||||
public abstract string[] GetMetaIni();
|
public abstract string[] GetMetaIni();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,12 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
where TState : AbstractIPS4Downloader<TDownloader, TState>.State<TDownloader>, new()
|
where TState : AbstractIPS4Downloader<TDownloader, TState>.State<TDownloader>, new()
|
||||||
where TDownloader : IDownloader
|
where TDownloader : IDownloader
|
||||||
{
|
{
|
||||||
public override string SiteName { get; }
|
protected AbstractIPS4Downloader(Uri loginUri, string encryptedKeyName, string cookieDomain)
|
||||||
public override Uri SiteURL { get; }
|
: base(loginUri, encryptedKeyName, cookieDomain, "ips4_member_id")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<AbstractDownloadState> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
public async Task<AbstractDownloadState?> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
||||||
{
|
{
|
||||||
Uri url = DownloaderUtils.GetDirectURL(archiveINI);
|
Uri url = DownloaderUtils.GetDirectURL(archiveINI);
|
||||||
|
|
||||||
@ -70,23 +72,23 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class State<TStateDownloader> : AbstractDownloadState, IMetaState
|
||||||
public class State<TStateDownloader> : AbstractDownloadState, IMetaState where TStateDownloader : IDownloader
|
where TStateDownloader : IDownloader
|
||||||
{
|
{
|
||||||
public string FullURL { get; set; }
|
public string FullURL { get; set; } = string.Empty;
|
||||||
public bool IsAttachment { get; set; }
|
public bool IsAttachment { get; set; }
|
||||||
public string FileID { get; set; }
|
public string FileID { get; set; } = string.Empty;
|
||||||
|
|
||||||
public string FileName { get; set; }
|
public string FileName { get; set; } = string.Empty;
|
||||||
|
|
||||||
// from IMetaState
|
// from IMetaState
|
||||||
public Uri URL => new Uri($"{Site}/files/file/{FileName}");
|
public Uri URL => new Uri($"{Site}/files/file/{FileName}");
|
||||||
public string Name { get; set; }
|
public string? Name { get; set; }
|
||||||
public string Author { get; set; }
|
public string? Author { get; set; }
|
||||||
public string Version { get; set; }
|
public string? Version { get; set; }
|
||||||
public string ImageURL { get; set; }
|
public string? ImageURL { get; set; }
|
||||||
public virtual bool IsNSFW { get; set; }
|
public virtual bool IsNSFW { get; set; }
|
||||||
public string Description { get; set; }
|
public string? Description { get; set; }
|
||||||
|
|
||||||
private static bool IsHTTPS => Downloader.SiteURL.AbsolutePath.StartsWith("https://");
|
private static bool IsHTTPS => Downloader.SiteURL.AbsolutePath.StartsWith("https://");
|
||||||
private static string URLPrefix => IsHTTPS ? "https://" : "http://";
|
private static string URLPrefix => IsHTTPS ? "https://" : "http://";
|
||||||
@ -119,6 +121,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
public override async Task<bool> Download(Archive a, AbsolutePath destination)
|
public override async Task<bool> Download(Archive a, AbsolutePath destination)
|
||||||
{
|
{
|
||||||
await using var stream = await ResolveDownloadStream();
|
await using var stream = await ResolveDownloadStream();
|
||||||
|
if (stream == null) return false;
|
||||||
await using (var file = destination.Create())
|
await using (var file = destination.Create())
|
||||||
{
|
{
|
||||||
await stream.CopyToAsync(file);
|
await stream.CopyToAsync(file);
|
||||||
@ -126,7 +129,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Stream> ResolveDownloadStream()
|
private async Task<Stream?> ResolveDownloadStream()
|
||||||
{
|
{
|
||||||
TOP:
|
TOP:
|
||||||
string url;
|
string url;
|
||||||
@ -236,12 +239,5 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AbstractIPS4Downloader(Uri loginUri, string encryptedKeyName, string cookieDomain) :
|
|
||||||
base(loginUri, encryptedKeyName, cookieDomain, "ips4_member_id")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,17 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
private readonly string _encryptedKeyName;
|
private readonly string _encryptedKeyName;
|
||||||
private readonly string _cookieDomain;
|
private readonly string _cookieDomain;
|
||||||
private readonly string _cookieName;
|
private readonly string _cookieName;
|
||||||
internal Common.Http.Client AuthedClient;
|
// ToDo
|
||||||
|
// Remove null assignment. Either add nullability to type, or figure way to prepare it safely
|
||||||
|
public Common.Http.Client AuthedClient { get; private set; } = null!;
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> TriggerLogin { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> ClearLogin { get; }
|
||||||
|
public IObservable<bool> IsLoggedIn => Utils.HaveEncryptedJsonObservable(_encryptedKeyName);
|
||||||
|
public abstract string SiteName { get; }
|
||||||
|
public virtual IObservable<string>? MetaInfo { get; }
|
||||||
|
public abstract Uri SiteURL { get; }
|
||||||
|
public virtual Uri? IconUri { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets up all the login facilites needed for a INeedsLogin downloader based on having the user log
|
/// Sets up all the login facilites needed for a INeedsLogin downloader based on having the user log
|
||||||
@ -48,14 +58,6 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
execute: () => Utils.CatchAndLog(() => Utils.DeleteEncryptedJson(_encryptedKeyName)),
|
execute: () => Utils.CatchAndLog(() => Utils.DeleteEncryptedJson(_encryptedKeyName)),
|
||||||
canExecute: IsLoggedIn.ObserveOnGuiThread());
|
canExecute: IsLoggedIn.ObserveOnGuiThread());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> TriggerLogin { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> ClearLogin { get; }
|
|
||||||
public IObservable<bool> IsLoggedIn => Utils.HaveEncryptedJsonObservable(_encryptedKeyName);
|
|
||||||
public abstract string SiteName { get; }
|
|
||||||
public virtual IObservable<string> MetaInfo { get; }
|
|
||||||
public abstract Uri SiteURL { get; }
|
|
||||||
public virtual Uri IconUri { get; }
|
|
||||||
|
|
||||||
protected virtual async Task WhileWaiting(IWebDriver browser)
|
protected virtual async Task WhileWaiting(IWebDriver browser)
|
||||||
{
|
{
|
||||||
@ -120,7 +122,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
Downloader = downloader;
|
Downloader = downloader;
|
||||||
}
|
}
|
||||||
public override string ShortDescription => $"Getting {Downloader.SiteName} Login";
|
public override string ShortDescription => $"Getting {Downloader.SiteName} Login";
|
||||||
public override string ExtendedDescription { get; }
|
public override string ExtendedDescription { get; } = string.Empty;
|
||||||
|
|
||||||
private readonly TaskCompletionSource<Helpers.Cookie[]> _source = new TaskCompletionSource<Helpers.Cookie[]>();
|
private readonly TaskCompletionSource<Helpers.Cookie[]> _source = new TaskCompletionSource<Helpers.Cookie[]>();
|
||||||
public Task<Helpers.Cookie[]> Task => _source.Task;
|
public Task<Helpers.Cookie[]> Task => _source.Task;
|
||||||
@ -138,6 +140,4 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,15 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
public class BethesdaNetDownloader : IUrlDownloader, INeedsLogin
|
public class BethesdaNetDownloader : IUrlDownloader, INeedsLogin
|
||||||
{
|
{
|
||||||
public const string DataName = "bethesda-net-data";
|
public const string DataName = "bethesda-net-data";
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> TriggerLogin { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> ClearLogin { get; }
|
||||||
|
public IObservable<bool> IsLoggedIn => Utils.HaveEncryptedJsonObservable(DataName);
|
||||||
|
public string SiteName => "Bethesda.NET";
|
||||||
|
public IObservable<string> MetaInfo => Observable.Return(""); //"Wabbajack will start the game, then exit once you enter the Mods page";
|
||||||
|
public Uri SiteURL => new Uri("https://bethesda.net");
|
||||||
|
public Uri? IconUri { get; }
|
||||||
|
|
||||||
public BethesdaNetDownloader()
|
public BethesdaNetDownloader()
|
||||||
{
|
{
|
||||||
TriggerLogin = ReactiveCommand.CreateFromTask(() => Utils.CatchAndLog(RequestLoginAndCache), IsLoggedIn.Select(b => !b).ObserveOn(RxApp.MainThreadScheduler));
|
TriggerLogin = ReactiveCommand.CreateFromTask(() => Utils.CatchAndLog(RequestLoginAndCache), IsLoggedIn.Select(b => !b).ObserveOn(RxApp.MainThreadScheduler));
|
||||||
@ -38,23 +47,23 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
|
|
||||||
private static async Task RequestLoginAndCache()
|
private static async Task RequestLoginAndCache()
|
||||||
{
|
{
|
||||||
var result = await Utils.Log(new RequestBethesdaNetLogin()).Task;
|
await Utils.Log(new RequestBethesdaNetLogin()).Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<AbstractDownloadState> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
public async Task<AbstractDownloadState?> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
||||||
{
|
{
|
||||||
var url = (Uri)DownloaderUtils.GetDirectURL(archiveINI);
|
var url = (Uri)DownloaderUtils.GetDirectURL(archiveINI);
|
||||||
return StateFromUrl(url);
|
return StateFromUrl(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static AbstractDownloadState StateFromUrl(Uri url)
|
internal static AbstractDownloadState? StateFromUrl(Uri url)
|
||||||
{
|
{
|
||||||
if (url != null && url.Host == "bethesda.net" && url.AbsolutePath.StartsWith("/en/mods/"))
|
if (url != null && url.Host == "bethesda.net" && url.AbsolutePath.StartsWith("/en/mods/"))
|
||||||
{
|
{
|
||||||
var split = url.AbsolutePath.Split('/');
|
var split = url.AbsolutePath.Split('/');
|
||||||
var game = split[3];
|
var game = split[3];
|
||||||
var modId = split[5];
|
var modId = split[5];
|
||||||
return new State {GameName = game, ContentId = modId};
|
return new State(gameName: game, contentId: modId);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -65,10 +74,11 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
await Utils.Log(new RequestBethesdaNetLogin()).Task;
|
await Utils.Log(new RequestBethesdaNetLogin()).Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<BethesdaNetData> Login(Game game)
|
public static async Task<BethesdaNetData?> Login(Game game)
|
||||||
{
|
{
|
||||||
var metadata = game.MetaData();
|
var metadata = game.MetaData();
|
||||||
var gamePath = metadata.GameLocation()?.Combine(metadata.MainExecutable);
|
if (metadata.MainExecutable == null) throw new NotImplementedException();
|
||||||
|
var gamePath = metadata.GameLocation().Combine(metadata.MainExecutable);
|
||||||
var info = new ProcessStartInfo
|
var info = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = @"Downloaders\BethesdaNet\bethnetlogin.exe",
|
FileName = @"Downloaders\BethesdaNet\bethnetlogin.exe",
|
||||||
@ -103,28 +113,25 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractDownloadState GetDownloaderState(string url)
|
public AbstractDownloadState? GetDownloaderState(string url)
|
||||||
{
|
{
|
||||||
return StateFromUrl(new Uri(url));
|
return StateFromUrl(new Uri(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> TriggerLogin { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> ClearLogin { get; }
|
|
||||||
public IObservable<bool> IsLoggedIn => Utils.HaveEncryptedJsonObservable(DataName);
|
|
||||||
public string SiteName => "Bethesda.NET";
|
|
||||||
public IObservable<string> MetaInfo => Observable.Return(""); //"Wabbajack will start the game, then exit once you enter the Mods page";
|
|
||||||
public Uri SiteURL => new Uri("https://bethesda.net");
|
|
||||||
public Uri IconUri { get; }
|
|
||||||
|
|
||||||
|
|
||||||
[JsonName("BethesdaNetDownloader")]
|
[JsonName("BethesdaNetDownloader")]
|
||||||
public class State : AbstractDownloadState
|
public class State : AbstractDownloadState
|
||||||
{
|
{
|
||||||
public string GameName { get; set; }
|
public string GameName { get; }
|
||||||
public string ContentId { get; set; }
|
public string ContentId { get; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override object[] PrimaryKey => new object[] {GameName, ContentId};
|
public override object[] PrimaryKey => new object[] { GameName, ContentId };
|
||||||
|
|
||||||
|
public State(string gameName, string contentId)
|
||||||
|
{
|
||||||
|
GameName = gameName;
|
||||||
|
ContentId = contentId;
|
||||||
|
}
|
||||||
|
|
||||||
public override bool IsWhitelisted(ServerWhitelist whitelist)
|
public override bool IsWhitelisted(ServerWhitelist whitelist)
|
||||||
{
|
{
|
||||||
@ -143,8 +150,8 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
using var got = await client.GetAsync(
|
using var got = await client.GetAsync(
|
||||||
$"https://content.cdp.bethesda.net/{collected.CDPProductId}/{collected.CDPPropertiesId}/{chunk.sha}");
|
$"https://content.cdp.bethesda.net/{collected.CDPProductId}/{collected.CDPPropertiesId}/{chunk.sha}");
|
||||||
var data = await got.Content.ReadAsByteArrayAsync();
|
var data = await got.Content.ReadAsByteArrayAsync();
|
||||||
if (collected.AESKey != null)
|
if (collected.AESKey != null)
|
||||||
AESCTRDecrypt(collected.AESKey, collected.AESIV, data);
|
AESCTRDecrypt(collected.AESKey, collected.AESIV!, data);
|
||||||
|
|
||||||
if (chunk.uncompressed_size == chunk.chunk_size)
|
if (chunk.uncompressed_size == chunk.chunk_size)
|
||||||
await file.WriteAsync(data, 0, data.Length);
|
await file.WriteAsync(data, 0, data.Length);
|
||||||
@ -198,7 +205,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
|
|
||||||
public override async Task<bool> Verify(Archive archive)
|
public override async Task<bool> Verify(Archive archive)
|
||||||
{
|
{
|
||||||
var info = await ResolveDownloadInfo();
|
await ResolveDownloadInfo();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,7 +229,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
client.Headers.Add(("x-cdp-app", "UGC SDK"));
|
client.Headers.Add(("x-cdp-app", "UGC SDK"));
|
||||||
client.Headers.Add(("x-cdp-app-ver", "0.9.11314/debug"));
|
client.Headers.Add(("x-cdp-app-ver", "0.9.11314/debug"));
|
||||||
client.Headers.Add(("x-cdp-lib-ver", "0.9.11314/debug"));
|
client.Headers.Add(("x-cdp-lib-ver", "0.9.11314/debug"));
|
||||||
client.Headers.Add(("x-cdp-platform","Win/32"));
|
client.Headers.Add(("x-cdp-platform", "Win/32"));
|
||||||
|
|
||||||
posted = await client.PostAsync("https://api.bethesda.net/cdp-user/auth",
|
posted = await client.PostAsync("https://api.bethesda.net/cdp-user/auth",
|
||||||
new StringContent("{\"access_token\": \"" + info.AccessToken + "\"}", Encoding.UTF8,
|
new StringContent("{\"access_token\": \"" + info.AccessToken + "\"}", Encoding.UTF8,
|
||||||
@ -233,10 +240,10 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
var got = await client.GetAsync($"https://api.bethesda.net/mods/ugc-workshop/content/get?content_id={ContentId}");
|
var got = await client.GetAsync($"https://api.bethesda.net/mods/ugc-workshop/content/get?content_id={ContentId}");
|
||||||
JObject data = JObject.Parse(await got.Content.ReadAsStringAsync());
|
JObject data = JObject.Parse(await got.Content.ReadAsStringAsync());
|
||||||
|
|
||||||
var content = data["platform"]["response"]["content"];
|
var content = data["platform"]!["response"]!["content"]!;
|
||||||
|
|
||||||
info.CDPBranchId = (int)content["cdp_branch_id"];
|
info.CDPBranchId = (int)content["cdp_branch_id"]!;
|
||||||
info.CDPProductId = (int)content["cdp_product_id"];
|
info.CDPProductId = (int)content["cdp_product_id"]!;
|
||||||
|
|
||||||
client.Headers.Add(("Authorization", $"Token {info.CDPToken}"));
|
client.Headers.Add(("Authorization", $"Token {info.CDPToken}"));
|
||||||
client.Headers.Add(("Accept", "application/json"));
|
client.Headers.Add(("Accept", "application/json"));
|
||||||
@ -246,7 +253,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
$"https://api.bethesda.net/cdp-user/projects/{info.CDPProductId}/branches/{info.CDPBranchId}/tree/.json");
|
$"https://api.bethesda.net/cdp-user/projects/{info.CDPProductId}/branches/{info.CDPBranchId}/tree/.json");
|
||||||
|
|
||||||
var tree = (await got.Content.ReadAsStringAsync()).FromJsonString<CDPTree>();
|
var tree = (await got.Content.ReadAsStringAsync()).FromJsonString<CDPTree>();
|
||||||
|
|
||||||
got.Dispose();
|
got.Dispose();
|
||||||
got = await client.PostAsync($"https://api.bethesda.net/mods/ugc-content/add-subscription", new StringContent($"{{\"content_id\": \"{ContentId}\"}}", Encoding.UTF8, "application/json"));
|
got = await client.PostAsync($"https://api.bethesda.net/mods/ugc-content/add-subscription", new StringContent($"{{\"content_id\": \"{ContentId}\"}}", Encoding.UTF8, "application/json"));
|
||||||
|
|
||||||
@ -255,14 +262,14 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
$"https://api.bethesda.net/cdp-user/projects/{info.CDPProductId}/branches/{info.CDPBranchId}/depots/.json");
|
$"https://api.bethesda.net/cdp-user/projects/{info.CDPProductId}/branches/{info.CDPBranchId}/depots/.json");
|
||||||
|
|
||||||
var props_obj = JObject.Parse(await got.Content.ReadAsStringAsync()).Properties().First();
|
var props_obj = JObject.Parse(await got.Content.ReadAsStringAsync()).Properties().First();
|
||||||
info.CDPPropertiesId = (int)props_obj.Value["properties_id"];
|
info.CDPPropertiesId = (int)props_obj.Value["properties_id"]!;
|
||||||
|
|
||||||
info.AESKey = props_obj.Value["ex_info_A"].Select(e => (byte)e).ToArray();
|
info.AESKey = props_obj.Value["ex_info_A"].Select(e => (byte)e).ToArray();
|
||||||
info.AESIV = props_obj.Value["ex_info_B"].Select(e => (byte)e).Take(16).ToArray();
|
info.AESIV = props_obj.Value["ex_info_B"].Select(e => (byte)e).Take(16).ToArray();
|
||||||
|
|
||||||
return (client, tree, info);
|
return (client, tree, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int AESCTRDecrypt(byte[] Key, byte[] IV, byte[] Data)
|
static int AESCTRDecrypt(byte[] Key, byte[] IV, byte[] Data)
|
||||||
{
|
{
|
||||||
IBufferedCipher cipher = CipherUtilities.GetCipher("AES/CTR/NoPadding");
|
IBufferedCipher cipher = CipherUtilities.GetCipher("AES/CTR/NoPadding");
|
||||||
@ -283,27 +290,26 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
|
|
||||||
public override string[] GetMetaIni()
|
public override string[] GetMetaIni()
|
||||||
{
|
{
|
||||||
return new[] {"[General]", $"directURL=https://bethesda.net/en/mods/{GameName}/mod-detail/{ContentId}"};
|
return new[] { "[General]", $"directURL=https://bethesda.net/en/mods/{GameName}/mod-detail/{ContentId}" };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class BeamLoginResponse
|
private class BeamLoginResponse
|
||||||
{
|
{
|
||||||
public string access_token { get; set; }
|
public string access_token { get; set; } = string.Empty;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CDPLoginResponse
|
private class CDPLoginResponse
|
||||||
{
|
{
|
||||||
public string token { get; set; }
|
public string token { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CollectedBNetInfo
|
private class CollectedBNetInfo
|
||||||
{
|
{
|
||||||
public byte[] AESKey { get; set; }
|
public byte[] AESKey { get; set; } = null!;
|
||||||
public byte[] AESIV { get; set; }
|
public byte[] AESIV { get; set; } = null!;
|
||||||
public string AccessToken { get; set; }
|
public string AccessToken { get; set; } = string.Empty;
|
||||||
public string CDPToken { get; set; }
|
public string CDPToken { get; set; } = string.Empty;
|
||||||
public int CDPBranchId { get; set; }
|
public int CDPBranchId { get; set; }
|
||||||
public int CDPProductId { get; set; }
|
public int CDPProductId { get; set; }
|
||||||
public int CDPPropertiesId { get; set; }
|
public int CDPPropertiesId { get; set; }
|
||||||
@ -311,24 +317,24 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
|
|
||||||
public class CDPTree
|
public class CDPTree
|
||||||
{
|
{
|
||||||
public List<Depot> depot_list { get; set; }
|
public List<Depot> depot_list { get; set; } = null!;
|
||||||
|
|
||||||
public class Depot
|
public class Depot
|
||||||
{
|
{
|
||||||
public List<CDPFile> file_list { get; set; }
|
public List<CDPFile> file_list { get; set; } = null!;
|
||||||
|
|
||||||
public class CDPFile
|
public class CDPFile
|
||||||
{
|
{
|
||||||
public int chunk_count { get; set; }
|
public int chunk_count { get; set; }
|
||||||
public List<Chunk> chunk_list { get; set; }
|
public List<Chunk> chunk_list { get; set; } = null!;
|
||||||
|
|
||||||
public string name { get; set; }
|
public string? name { get; set; }
|
||||||
|
|
||||||
public class Chunk
|
public class Chunk
|
||||||
{
|
{
|
||||||
public int chunk_size { get; set; }
|
public int chunk_size { get; set; }
|
||||||
public int index { get; set; }
|
public int index { get; set; }
|
||||||
public string sha { get; set; }
|
public string sha { get; set; } = string.Empty;
|
||||||
public int uncompressed_size { get; set; }
|
public int uncompressed_size { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -345,7 +351,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
public class RequestBethesdaNetLogin : AUserIntervention
|
public class RequestBethesdaNetLogin : AUserIntervention
|
||||||
{
|
{
|
||||||
public override string ShortDescription => "Logging into Bethesda.NET";
|
public override string ShortDescription => "Logging into Bethesda.NET";
|
||||||
public override string ExtendedDescription { get; }
|
public override string ExtendedDescription { get; } = string.Empty;
|
||||||
|
|
||||||
private readonly TaskCompletionSource<BethesdaNetData> _source = new TaskCompletionSource<BethesdaNetData>();
|
private readonly TaskCompletionSource<BethesdaNetData> _source = new TaskCompletionSource<BethesdaNetData>();
|
||||||
public Task<BethesdaNetData> Task => _source.Task;
|
public Task<BethesdaNetData> Task => _source.Task;
|
||||||
@ -367,8 +373,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
[JsonName("BethesdaNetData")]
|
[JsonName("BethesdaNetData")]
|
||||||
public class BethesdaNetData
|
public class BethesdaNetData
|
||||||
{
|
{
|
||||||
public string body { get; set; }
|
public string body { get; set; } = string.Empty;
|
||||||
public Dictionary<string, string> headers = new Dictionary<string, string>();
|
public Dictionary<string, string> headers = new Dictionary<string, string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
IndexedDownloaders = Downloaders.ToDictionary(d => d.GetType());
|
IndexedDownloaders = Downloaders.ToDictionary(d => d.GetType());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<AbstractDownloadState> Infer(Uri uri)
|
public static async Task<AbstractDownloadState?> Infer(Uri uri)
|
||||||
{
|
{
|
||||||
foreach (var inf in Inferencers)
|
foreach (var inf in Inferencers)
|
||||||
{
|
{
|
||||||
@ -73,7 +73,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ini"></param>
|
/// <param name="ini"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static AbstractDownloadState ResolveArchive(string url)
|
public static AbstractDownloadState? ResolveArchive(string url)
|
||||||
{
|
{
|
||||||
return Downloaders.OfType<IUrlDownloader>().Select(d => d.GetDownloaderState(url)).FirstOrDefault(result => result != null);
|
return Downloaders.OfType<IUrlDownloader>().Select(d => d.GetDownloaderState(url)).FirstOrDefault(result => result != null);
|
||||||
}
|
}
|
||||||
@ -111,13 +111,9 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
var patchName = $"{archive.Hash.ToHex()}_{upgrade.Hash.ToHex()}";
|
var patchName = $"{archive.Hash.ToHex()}_{upgrade.Hash.ToHex()}";
|
||||||
var patchPath = destination.Parent.Combine("_Patch_" + patchName);
|
var patchPath = destination.Parent.Combine("_Patch_" + patchName);
|
||||||
|
|
||||||
var patchState = new Archive
|
var patchState = new Archive(new HTTPDownloader.State($"https://wabbajackcdn.b-cdn.net/updates/{patchName}"))
|
||||||
{
|
{
|
||||||
Name = patchName,
|
Name = patchName,
|
||||||
State = new HTTPDownloader.State
|
|
||||||
{
|
|
||||||
Url = $"https://wabbajackcdn.b-cdn.net/updates/{patchName}"
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var patchResult = await Download(patchState, patchPath);
|
var patchResult = await Download(patchState, patchPath);
|
||||||
|
@ -4,7 +4,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
{
|
{
|
||||||
public static class DownloaderUtils
|
public static class DownloaderUtils
|
||||||
{
|
{
|
||||||
public static Uri GetDirectURL(dynamic meta)
|
public static Uri? GetDirectURL(dynamic? meta)
|
||||||
{
|
{
|
||||||
var url = meta?.General?.directURL;
|
var url = meta?.General?.directURL;
|
||||||
if (url == null) return null;
|
if (url == null) return null;
|
||||||
|
@ -7,13 +7,13 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
{
|
{
|
||||||
public class DropboxDownloader : IDownloader, IUrlDownloader
|
public class DropboxDownloader : IDownloader, IUrlDownloader
|
||||||
{
|
{
|
||||||
public async Task<AbstractDownloadState> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
public async Task<AbstractDownloadState?> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
||||||
{
|
{
|
||||||
var urlstring = archiveINI?.General?.directURL;
|
var urlstring = archiveINI?.General?.directURL;
|
||||||
return GetDownloaderState(urlstring);
|
return GetDownloaderState(urlstring);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractDownloadState GetDownloaderState(string url)
|
public AbstractDownloadState? GetDownloaderState(string url)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -29,10 +29,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
|
|
||||||
uri.Query = query.ToString();
|
uri.Query = query.ToString();
|
||||||
|
|
||||||
return new HTTPDownloader.State()
|
return new HTTPDownloader.State(uri.ToString().Replace("dropbox.com:443/", "dropbox.com/"));
|
||||||
{
|
|
||||||
Url = uri.ToString().Replace("dropbox.com:443/", "dropbox.com/")
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
@ -9,33 +9,30 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
{
|
{
|
||||||
public class GameFileSourceDownloader : IDownloader
|
public class GameFileSourceDownloader : IDownloader
|
||||||
{
|
{
|
||||||
public async Task<AbstractDownloadState> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
public async Task<AbstractDownloadState?> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
||||||
{
|
{
|
||||||
var gameName = (string)archiveINI?.General?.gameName;
|
var gameName = (string?)archiveINI?.General?.gameName;
|
||||||
var gameFile = (string)archiveINI?.General?.gameFile;
|
var gameFile = (string?)archiveINI?.General?.gameFile;
|
||||||
|
|
||||||
if (gameFile == null || gameFile == null)
|
if (gameName == null || gameFile == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var game = GameRegistry.GetByFuzzyName(gameName);
|
if (!GameRegistry.TryGetByFuzzyName(gameName, out var game)) return null;
|
||||||
if (game == null) return null;
|
|
||||||
|
|
||||||
var path = game.GameLocation();
|
var path = game.TryGetGameLocation();
|
||||||
var filePath = path?.Combine(gameFile);
|
var filePath = path?.Combine(gameFile);
|
||||||
|
|
||||||
|
if (!(filePath?.Exists ?? false))
|
||||||
if (!filePath?.Exists ?? false)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var fp = filePath.Value;
|
var fp = filePath.Value;
|
||||||
var hash = await fp.FileHashCachedAsync();
|
var hash = await fp.FileHashCachedAsync();
|
||||||
|
|
||||||
return new State
|
return new State(game.InstalledVersion)
|
||||||
{
|
{
|
||||||
Game = game.Game,
|
Game = game.Game,
|
||||||
GameFile = (RelativePath)gameFile,
|
GameFile = (RelativePath)gameFile,
|
||||||
Hash = hash,
|
Hash = hash
|
||||||
GameVersion = game.InstalledVersion
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,10 +46,15 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
public Game Game { get; set; }
|
public Game Game { get; set; }
|
||||||
public RelativePath GameFile { get; set; }
|
public RelativePath GameFile { get; set; }
|
||||||
public Hash Hash { get; set; }
|
public Hash Hash { get; set; }
|
||||||
public string GameVersion { get; set; }
|
public string GameVersion { get; }
|
||||||
|
|
||||||
|
public State(string gameVersion)
|
||||||
|
{
|
||||||
|
GameVersion = gameVersion;
|
||||||
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
internal AbsolutePath SourcePath => Game.MetaData().GameLocation().Value.Combine(GameFile);
|
internal AbsolutePath SourcePath => Game.MetaData().GameLocation().Combine(GameFile);
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override object[] PrimaryKey { get => new object[] {Game, GameVersion, GameFile}; }
|
public override object[] PrimaryKey { get => new object[] {Game, GameVersion, GameFile}; }
|
||||||
@ -82,7 +84,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
return DownloadDispatcher.GetInstance<GameFileSourceDownloader>();
|
return DownloadDispatcher.GetInstance<GameFileSourceDownloader>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string GetManifestURL(Archive a)
|
public override string? GetManifestURL(Archive a)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -10,22 +10,19 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
{
|
{
|
||||||
public class GoogleDriveDownloader : IDownloader, IUrlDownloader
|
public class GoogleDriveDownloader : IDownloader, IUrlDownloader
|
||||||
{
|
{
|
||||||
public async Task<AbstractDownloadState> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
public async Task<AbstractDownloadState?> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
||||||
{
|
{
|
||||||
var url = archiveINI?.General?.directURL;
|
var url = archiveINI?.General?.directURL;
|
||||||
return GetDownloaderState(url);
|
return GetDownloaderState(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractDownloadState GetDownloaderState(string url)
|
public AbstractDownloadState? GetDownloaderState(string url)
|
||||||
{
|
{
|
||||||
if (url != null && url.StartsWith("https://drive.google.com"))
|
if (url != null && url.StartsWith("https://drive.google.com"))
|
||||||
{
|
{
|
||||||
var regex = new Regex("((?<=id=)[a-zA-Z0-9_-]*)|(?<=\\/file\\/d\\/)[a-zA-Z0-9_-]*");
|
var regex = new Regex("((?<=id=)[a-zA-Z0-9_-]*)|(?<=\\/file\\/d\\/)[a-zA-Z0-9_-]*");
|
||||||
var match = regex.Match(url);
|
var match = regex.Match(url);
|
||||||
return new State
|
return new State(match.ToString());
|
||||||
{
|
|
||||||
Id = match.ToString()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -38,10 +35,15 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
[JsonName("GoogleDriveDownloader")]
|
[JsonName("GoogleDriveDownloader")]
|
||||||
public class State : AbstractDownloadState
|
public class State : AbstractDownloadState
|
||||||
{
|
{
|
||||||
public string Id { get; set; }
|
public string Id { get; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override object[] PrimaryKey { get => new object[] {Id}; }
|
public override object[] PrimaryKey => new object[] { Id };
|
||||||
|
|
||||||
|
public State(string id)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
}
|
||||||
|
|
||||||
public override bool IsWhitelisted(ServerWhitelist whitelist)
|
public override bool IsWhitelisted(ServerWhitelist whitelist)
|
||||||
{
|
{
|
||||||
@ -64,7 +66,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
var regex = new Regex("(?<=/uc\\?export=download&confirm=).*(?=;id=)");
|
var regex = new Regex("(?<=/uc\\?export=download&confirm=).*(?=;id=)");
|
||||||
var confirm = regex.Match(await response.Content.ReadAsStringAsync());
|
var confirm = regex.Match(await response.Content.ReadAsStringAsync());
|
||||||
var url = $"https://drive.google.com/uc?export=download&confirm={confirm}&id={Id}";
|
var url = $"https://drive.google.com/uc?export=download&confirm={confirm}&id={Id}";
|
||||||
var httpState = new HTTPDownloader.State {Url = url, Client = client};
|
var httpState = new HTTPDownloader.State(url) { Client = client };
|
||||||
return httpState;
|
return httpState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,29 +15,24 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
{
|
{
|
||||||
public class HTTPDownloader : IDownloader, IUrlDownloader
|
public class HTTPDownloader : IDownloader, IUrlDownloader
|
||||||
{
|
{
|
||||||
|
public async Task<AbstractDownloadState?> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
||||||
public async Task<AbstractDownloadState> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
|
||||||
{
|
{
|
||||||
var url = archiveINI?.General?.directURL;
|
var url = archiveINI?.General?.directURL;
|
||||||
return GetDownloaderState(url, archiveINI);
|
return GetDownloaderState(url, archiveINI);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractDownloadState GetDownloaderState(string uri)
|
public AbstractDownloadState? GetDownloaderState(string uri)
|
||||||
{
|
{
|
||||||
return GetDownloaderState(uri, null);
|
return GetDownloaderState(uri, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractDownloadState GetDownloaderState(string url, dynamic archiveINI)
|
public AbstractDownloadState? GetDownloaderState(string url, dynamic? archiveINI)
|
||||||
{
|
{
|
||||||
if (url != null)
|
if (url != null)
|
||||||
{
|
{
|
||||||
var tmp = new State
|
var tmp = new State(url);
|
||||||
{
|
|
||||||
Url = url
|
|
||||||
};
|
|
||||||
if (archiveINI?.General?.directURLHeaders != null)
|
if (archiveINI?.General?.directURLHeaders != null)
|
||||||
{
|
{
|
||||||
tmp.Headers = new List<string>();
|
|
||||||
tmp.Headers.AddRange(archiveINI?.General.directURLHeaders.Split('|'));
|
tmp.Headers.AddRange(archiveINI?.General.directURLHeaders.Split('|'));
|
||||||
}
|
}
|
||||||
return tmp;
|
return tmp;
|
||||||
@ -53,15 +48,20 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
[JsonName("HttpDownloader")]
|
[JsonName("HttpDownloader")]
|
||||||
public class State : AbstractDownloadState
|
public class State : AbstractDownloadState
|
||||||
{
|
{
|
||||||
public string Url { get; set; }
|
public string Url { get; }
|
||||||
|
|
||||||
public List<string> Headers { get; set; }
|
public List<string> Headers { get; } = new List<string>();
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public Common.Http.Client Client { get; set; }
|
public Common.Http.Client? Client { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override object[] PrimaryKey { get => new object[] {Url};}
|
public override object[] PrimaryKey => new object[] { Url };
|
||||||
|
|
||||||
|
public State(string url)
|
||||||
|
{
|
||||||
|
Url = url;
|
||||||
|
}
|
||||||
|
|
||||||
public override bool IsWhitelisted(ServerWhitelist whitelist)
|
public override bool IsWhitelisted(ServerWhitelist whitelist)
|
||||||
{
|
{
|
||||||
@ -85,19 +85,18 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
var client = Client ?? new Common.Http.Client();
|
var client = Client ?? new Common.Http.Client();
|
||||||
client.Headers.Add(("User-Agent", Consts.UserAgent));
|
client.Headers.Add(("User-Agent", Consts.UserAgent));
|
||||||
|
|
||||||
if (Headers != null)
|
foreach (var header in Headers)
|
||||||
foreach (var header in Headers)
|
{
|
||||||
{
|
var idx = header.IndexOf(':');
|
||||||
var idx = header.IndexOf(':');
|
var k = header.Substring(0, idx);
|
||||||
var k = header.Substring(0, idx);
|
var v = header.Substring(idx + 1);
|
||||||
var v = header.Substring(idx + 1);
|
client.Headers.Add((k, v));
|
||||||
client.Headers.Add((k, v));
|
}
|
||||||
}
|
|
||||||
|
|
||||||
long totalRead = 0;
|
long totalRead = 0;
|
||||||
var bufferSize = 1024 * 32;
|
var bufferSize = 1024 * 32;
|
||||||
|
|
||||||
Utils.Status($"Starting Download {a?.Name ?? Url}", Percent.Zero);
|
Utils.Status($"Starting Download {a.Name ?? Url}", Percent.Zero);
|
||||||
var response = await client.GetAsync(Url);
|
var response = await client.GetAsync(Url);
|
||||||
TOP:
|
TOP:
|
||||||
|
|
||||||
@ -177,7 +176,7 @@ TOP:
|
|||||||
if (read == 0) break;
|
if (read == 0) break;
|
||||||
Utils.Status($"Downloading {a.Name}", Percent.FactoryPutInRange(totalRead, contentSize));
|
Utils.Status($"Downloading {a.Name}", Percent.FactoryPutInRange(totalRead, contentSize));
|
||||||
|
|
||||||
fs.Write(buffer, 0, read);
|
fs!.Write(buffer, 0, read);
|
||||||
totalRead += read;
|
totalRead += read;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,7 +202,7 @@ TOP:
|
|||||||
|
|
||||||
public override string[] GetMetaIni()
|
public override string[] GetMetaIni()
|
||||||
{
|
{
|
||||||
if (Headers != null)
|
if (Headers.Count > 0)
|
||||||
return new [] {"[General]",
|
return new [] {"[General]",
|
||||||
$"directURL={Url}",
|
$"directURL={Url}",
|
||||||
$"directURLHeaders={string.Join("|", Headers)}"};
|
$"directURLHeaders={string.Join("|", Headers)}"};
|
||||||
|
@ -4,12 +4,11 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
{
|
{
|
||||||
public interface IDownloader
|
public interface IDownloader
|
||||||
{
|
{
|
||||||
Task<AbstractDownloadState> GetDownloaderState(dynamic archiveINI, bool quickMode = false);
|
Task<AbstractDownloadState?> GetDownloaderState(dynamic archiveINI, bool quickMode = false);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called before any downloads are inacted by the installer;
|
/// Called before any downloads are inacted by the installer;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task Prepare();
|
Task Prepare();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,9 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
ReactiveCommand<Unit, Unit> ClearLogin { get; }
|
ReactiveCommand<Unit, Unit> ClearLogin { get; }
|
||||||
IObservable<bool> IsLoggedIn { get; }
|
IObservable<bool> IsLoggedIn { get; }
|
||||||
string SiteName { get; }
|
string SiteName { get; }
|
||||||
IObservable<string> MetaInfo { get; }
|
IObservable<string>? MetaInfo { get; }
|
||||||
Uri SiteURL { get; }
|
Uri SiteURL { get; }
|
||||||
Uri IconUri { get; }
|
Uri? IconUri { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct LoginReturnMessage
|
public struct LoginReturnMessage
|
||||||
|
@ -2,6 +2,6 @@
|
|||||||
{
|
{
|
||||||
public interface IUrlDownloader : IDownloader
|
public interface IUrlDownloader : IDownloader
|
||||||
{
|
{
|
||||||
AbstractDownloadState GetDownloaderState(string url);
|
AbstractDownloadState? GetDownloaderState(string url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,8 +23,6 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
authInfos = MegaApiClient.GenerateAuthInfos(username, password.ToNormalString());
|
authInfos = MegaApiClient.GenerateAuthInfos(username, password.ToNormalString());
|
||||||
username = null;
|
|
||||||
password = null;
|
|
||||||
}
|
}
|
||||||
catch (ApiException e)
|
catch (ApiException e)
|
||||||
{
|
{
|
||||||
@ -72,16 +70,16 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
IsLoggedIn.ObserveOnGuiThread());
|
IsLoggedIn.ObserveOnGuiThread());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<AbstractDownloadState> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
public async Task<AbstractDownloadState?> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
||||||
{
|
{
|
||||||
var url = archiveINI?.General?.directURL;
|
var url = archiveINI?.General?.directURL;
|
||||||
return GetDownloaderState(url);
|
return GetDownloaderState(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractDownloadState GetDownloaderState(string url)
|
public AbstractDownloadState? GetDownloaderState(string url)
|
||||||
{
|
{
|
||||||
if (url != null && url.StartsWith(Consts.MegaPrefix))
|
if (url != null && url.StartsWith(Consts.MegaPrefix))
|
||||||
return new State { Url = url};
|
return new State(url);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,6 +90,11 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
[JsonName("MegaDownloader")]
|
[JsonName("MegaDownloader")]
|
||||||
public class State : HTTPDownloader.State
|
public class State : HTTPDownloader.State
|
||||||
{
|
{
|
||||||
|
public State(string url)
|
||||||
|
: base(url)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
private static MegaApiClient MegaApiClient => DownloadDispatcher.GetInstance<MegaDownloader>().MegaApiClient;
|
private static MegaApiClient MegaApiClient => DownloadDispatcher.GetInstance<MegaDownloader>().MegaApiClient;
|
||||||
|
|
||||||
private void MegaLogin()
|
private void MegaLogin()
|
||||||
|
@ -18,8 +18,8 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
|
|
||||||
class FileEvent
|
class FileEvent
|
||||||
{
|
{
|
||||||
public string FullPath { get; set; }
|
public string FullPath { get; set; } = string.Empty;
|
||||||
public string Name { get; set; }
|
public string Name { get; set; } = string.Empty;
|
||||||
public long Size { get; set; }
|
public long Size { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,10 +57,10 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<AbstractDownloadState> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
public async Task<AbstractDownloadState?> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
||||||
{
|
{
|
||||||
var url = archiveINI?.General?.manualURL;
|
var url = archiveINI?.General?.manualURL;
|
||||||
return url != null ? new State { Url = url} : null;
|
return url != null ? new State(url) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Prepare()
|
public async Task Prepare()
|
||||||
@ -70,10 +70,15 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
[JsonName("ManualDownloader")]
|
[JsonName("ManualDownloader")]
|
||||||
public class State : AbstractDownloadState
|
public class State : AbstractDownloadState
|
||||||
{
|
{
|
||||||
public string Url { get; set; }
|
public string Url { get; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override object[] PrimaryKey { get => new object[] {Url}; }
|
public override object[] PrimaryKey => new object[] { Url };
|
||||||
|
|
||||||
|
public State(string url)
|
||||||
|
{
|
||||||
|
Url = url;
|
||||||
|
}
|
||||||
|
|
||||||
public override bool IsWhitelisted(ServerWhitelist whitelist)
|
public override bool IsWhitelisted(ServerWhitelist whitelist)
|
||||||
{
|
{
|
||||||
@ -83,7 +88,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
public override async Task<bool> Download(Archive a, AbsolutePath destination)
|
public override async Task<bool> Download(Archive a, AbsolutePath destination)
|
||||||
{
|
{
|
||||||
var (uri, client) = await Utils.Log(await ManuallyDownloadFile.Create(this)).Task;
|
var (uri, client) = await Utils.Log(await ManuallyDownloadFile.Create(this)).Task;
|
||||||
var state = new HTTPDownloader.State {Url = uri.ToString(), Client = client};
|
var state = new HTTPDownloader.State(uri.ToString()) { Client = client };
|
||||||
return await state.Download(a, destination);
|
return await state.Download(a, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,10 +109,10 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
|
|
||||||
public override string[] GetMetaIni()
|
public override string[] GetMetaIni()
|
||||||
{
|
{
|
||||||
return new [] {
|
return new []
|
||||||
|
{
|
||||||
"[General]",
|
"[General]",
|
||||||
$"manualURL={Url}"
|
$"manualURL={Url}",
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,22 +10,24 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
{
|
{
|
||||||
public class MediaFireDownloader : IUrlDownloader
|
public class MediaFireDownloader : IUrlDownloader
|
||||||
{
|
{
|
||||||
public async Task<AbstractDownloadState> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
public async Task<AbstractDownloadState?> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
||||||
{
|
{
|
||||||
Uri url = DownloaderUtils.GetDirectURL(archiveINI);
|
Uri url = DownloaderUtils.GetDirectURL(archiveINI);
|
||||||
if (url == null || url.Host != "www.mediafire.com") return null;
|
if (url == null || url.Host != "www.mediafire.com") return null;
|
||||||
|
|
||||||
return new State
|
return new State(url.ToString());
|
||||||
{
|
|
||||||
Url = url.ToString()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class State : AbstractDownloadState
|
public class State : AbstractDownloadState
|
||||||
{
|
{
|
||||||
public string Url { get; set; }
|
public string Url { get; }
|
||||||
|
|
||||||
public override object[] PrimaryKey { get => new object[] {Url};}
|
public override object[] PrimaryKey => new object[] { Url };
|
||||||
|
|
||||||
|
public State(string url)
|
||||||
|
{
|
||||||
|
Url = url;
|
||||||
|
}
|
||||||
|
|
||||||
public override bool IsWhitelisted(ServerWhitelist whitelist)
|
public override bool IsWhitelisted(ServerWhitelist whitelist)
|
||||||
{
|
{
|
||||||
@ -35,6 +37,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
public override async Task<bool> Download(Archive a, AbsolutePath destination)
|
public override async Task<bool> Download(Archive a, AbsolutePath destination)
|
||||||
{
|
{
|
||||||
var result = await Resolve();
|
var result = await Resolve();
|
||||||
|
if (result == null) return false;
|
||||||
return await result.Download(a, destination);
|
return await result.Download(a, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +46,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
return await Resolve() != null;
|
return await Resolve() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<HTTPDownloader.State> Resolve()
|
private async Task<HTTPDownloader.State?> Resolve()
|
||||||
{
|
{
|
||||||
using (var d = await Driver.Create())
|
using (var d = await Driver.Create())
|
||||||
{
|
{
|
||||||
@ -52,10 +55,9 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
await Task.Delay(1000);
|
await Task.Delay(1000);
|
||||||
var newURL = await d.GetAttr("a.input", "href");
|
var newURL = await d.GetAttr("a.input", "href");
|
||||||
if (newURL == null || !newURL.StartsWith("http")) return null;
|
if (newURL == null || !newURL.StartsWith("http")) return null;
|
||||||
return new HTTPDownloader.State()
|
return new HTTPDownloader.State(newURL)
|
||||||
{
|
{
|
||||||
Client = new Common.Http.Client(),
|
Client = new Common.Http.Client(),
|
||||||
Url = newURL
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,15 +86,12 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractDownloadState GetDownloaderState(string u)
|
public AbstractDownloadState? GetDownloaderState(string u)
|
||||||
{
|
{
|
||||||
var url = new Uri(u);
|
var url = new Uri(u);
|
||||||
if (url.Host != "www.mediafire.com") return null;
|
if (url.Host != "www.mediafire.com") return null;
|
||||||
|
|
||||||
return new State
|
return new State(url.ToString());
|
||||||
{
|
|
||||||
Url = url.ToString()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,20 +11,17 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
{
|
{
|
||||||
public class ModDBDownloader : IDownloader, IUrlDownloader
|
public class ModDBDownloader : IDownloader, IUrlDownloader
|
||||||
{
|
{
|
||||||
public async Task<AbstractDownloadState> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
public async Task<AbstractDownloadState?> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
||||||
{
|
{
|
||||||
var url = archiveINI?.General?.directURL;
|
var url = archiveINI?.General?.directURL;
|
||||||
return GetDownloaderState(url);
|
return GetDownloaderState(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractDownloadState GetDownloaderState(string url)
|
public AbstractDownloadState? GetDownloaderState(string url)
|
||||||
{
|
{
|
||||||
if (url != null && url.StartsWith("https://www.moddb.com/downloads/start"))
|
if (url != null && url.StartsWith("https://www.moddb.com/downloads/start"))
|
||||||
{
|
{
|
||||||
return new State
|
return new State(url);
|
||||||
{
|
|
||||||
Url = url
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -37,10 +34,15 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
[JsonName("ModDBDownloader")]
|
[JsonName("ModDBDownloader")]
|
||||||
public class State : AbstractDownloadState
|
public class State : AbstractDownloadState
|
||||||
{
|
{
|
||||||
public string Url { get; set; }
|
public string Url { get; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override object[] PrimaryKey { get => new object[]{Url}; }
|
public override object[] PrimaryKey => new object[] { Url };
|
||||||
|
|
||||||
|
public State(string url)
|
||||||
|
{
|
||||||
|
Url = url;
|
||||||
|
}
|
||||||
|
|
||||||
public override bool IsWhitelisted(ServerWhitelist whitelist)
|
public override bool IsWhitelisted(ServerWhitelist whitelist)
|
||||||
{
|
{
|
||||||
@ -56,7 +58,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await new HTTPDownloader.State {Url = url}.Download(a, destination);
|
await new HTTPDownloader.State(url).Download(a, destination);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
|
@ -19,8 +19,8 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
{
|
{
|
||||||
private bool _prepared;
|
private bool _prepared;
|
||||||
private AsyncLock _lock = new AsyncLock();
|
private AsyncLock _lock = new AsyncLock();
|
||||||
private UserStatus _status;
|
private UserStatus? _status;
|
||||||
private NexusApiClient _client;
|
private NexusApiClient? _client;
|
||||||
|
|
||||||
public IObservable<bool> IsLoggedIn => Utils.HaveEncryptedJsonObservable("nexusapikey");
|
public IObservable<bool> IsLoggedIn => Utils.HaveEncryptedJsonObservable("nexusapikey");
|
||||||
|
|
||||||
@ -50,9 +50,9 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
canExecute: IsLoggedIn.ObserveOnGuiThread());
|
canExecute: IsLoggedIn.ObserveOnGuiThread());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<AbstractDownloadState> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
public async Task<AbstractDownloadState?> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
||||||
{
|
{
|
||||||
var general = archiveINI?.General;
|
var general = archiveINI.General;
|
||||||
|
|
||||||
if (general.modID != null && general.fileID != null && general.gameName != null)
|
if (general.modID != null && general.fileID != null && general.gameName != null)
|
||||||
{
|
{
|
||||||
@ -135,17 +135,17 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public Uri URL => new Uri($"http://nexusmods.com/{Game.MetaData().NexusName}/mods/{ModID}");
|
public Uri URL => new Uri($"http://nexusmods.com/{Game.MetaData().NexusName}/mods/{ModID}");
|
||||||
|
|
||||||
public string Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
|
||||||
public string Author { get; set; }
|
public string? Author { get; set; }
|
||||||
|
|
||||||
public string Version { get; set; }
|
public string? Version { get; set; }
|
||||||
|
|
||||||
public string ImageURL { get; set; }
|
public string? ImageURL { get; set; }
|
||||||
|
|
||||||
public bool IsNSFW { get; set; }
|
public bool IsNSFW { get; set; }
|
||||||
|
|
||||||
public string Description { get; set; }
|
public string? Description { get; set; }
|
||||||
|
|
||||||
[JsonProperty("GameName")]
|
[JsonProperty("GameName")]
|
||||||
[JsonConverter(typeof(Utils.GameConverter))]
|
[JsonConverter(typeof(Utils.GameConverter))]
|
||||||
@ -184,10 +184,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
|
|
||||||
Utils.Log($"Downloading Nexus Archive - {a.Name} - {Game} - {ModID} - {FileID}");
|
Utils.Log($"Downloading Nexus Archive - {a.Name} - {Game} - {ModID} - {FileID}");
|
||||||
|
|
||||||
return await new HTTPDownloader.State
|
return await new HTTPDownloader.State(url).Download(a, destination);
|
||||||
{
|
|
||||||
Url = url
|
|
||||||
}.Download(a, destination);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<bool> Verify(Archive a)
|
public override async Task<bool> Verify(Archive a)
|
||||||
|
@ -14,20 +14,21 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
{
|
{
|
||||||
public class SteamWorkshopDownloader : IUrlDownloader
|
public class SteamWorkshopDownloader : IUrlDownloader
|
||||||
{
|
{
|
||||||
private SteamWorkshopItem _item;
|
public async Task<AbstractDownloadState?> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
||||||
|
|
||||||
public async Task<AbstractDownloadState> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
|
||||||
{
|
{
|
||||||
var id = archiveINI?.General?.itemID;
|
var id = archiveINI?.General?.itemID;
|
||||||
var steamID = archiveINI?.General?.steamID;
|
var steamID = archiveINI?.General?.steamID;
|
||||||
var size = archiveINI?.General?.itemSize;
|
var size = archiveINI?.General?.itemSize;
|
||||||
_item = new SteamWorkshopItem
|
if (steamID == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Steam workshop item had no steam ID.");
|
||||||
|
}
|
||||||
|
var item = new SteamWorkshopItem(GameRegistry.GetBySteamID(int.Parse(steamID)))
|
||||||
{
|
{
|
||||||
ItemID = id != null ? int.Parse(id) : 0,
|
ItemID = id != null ? int.Parse(id) : 0,
|
||||||
Size = size != null ? int.Parse(size) : 0,
|
Size = size != null ? int.Parse(size) : 0,
|
||||||
Game = steamID != null ? GameRegistry.GetBySteamID(int.Parse(steamID)) : null
|
|
||||||
};
|
};
|
||||||
return new State {Item = _item};
|
return new State(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Prepare()
|
public async Task Prepare()
|
||||||
@ -41,8 +42,14 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
|
|
||||||
public class State : AbstractDownloadState
|
public class State : AbstractDownloadState
|
||||||
{
|
{
|
||||||
public SteamWorkshopItem Item { get; set; }
|
public SteamWorkshopItem Item { get; }
|
||||||
public override object[] PrimaryKey { get => new object[] {Item.Game, Item.ItemID}; }
|
|
||||||
|
public override object[] PrimaryKey => new object[] { Item.Game, Item.ItemID };
|
||||||
|
|
||||||
|
public State(SteamWorkshopItem item)
|
||||||
|
{
|
||||||
|
Item = item;
|
||||||
|
}
|
||||||
|
|
||||||
public override bool IsWhitelisted(ServerWhitelist whitelist)
|
public override bool IsWhitelisted(ServerWhitelist whitelist)
|
||||||
{
|
{
|
||||||
|
@ -5,7 +5,7 @@ namespace Wabbajack.Lib.Downloaders.UrlDownloaders
|
|||||||
{
|
{
|
||||||
public class BethesdaNetInferencer : IUrlInferencer
|
public class BethesdaNetInferencer : IUrlInferencer
|
||||||
{
|
{
|
||||||
public async Task<AbstractDownloadState> Infer(Uri uri)
|
public async Task<AbstractDownloadState?> Infer(Uri uri)
|
||||||
{
|
{
|
||||||
return BethesdaNetDownloader.StateFromUrl(uri);
|
return BethesdaNetDownloader.StateFromUrl(uri);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,6 @@ namespace Wabbajack.Lib.Downloaders.UrlDownloaders
|
|||||||
{
|
{
|
||||||
public interface IUrlInferencer
|
public interface IUrlInferencer
|
||||||
{
|
{
|
||||||
Task<AbstractDownloadState> Infer(Uri uri);
|
Task<AbstractDownloadState?> Infer(Uri uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,9 @@ namespace Wabbajack.Lib.Downloaders.UrlDownloaders
|
|||||||
{
|
{
|
||||||
public class YoutubeInferencer : IUrlInferencer
|
public class YoutubeInferencer : IUrlInferencer
|
||||||
{
|
{
|
||||||
|
public async Task<AbstractDownloadState?> Infer(Uri uri)
|
||||||
public async Task<AbstractDownloadState> Infer(Uri uri)
|
|
||||||
{
|
{
|
||||||
var state = (YouTubeDownloader.State)YouTubeDownloader.UriToState(uri);
|
var state = YouTubeDownloader.UriToState(uri) as YouTubeDownloader.State;
|
||||||
if (state == null) return null;
|
if (state == null) return null;
|
||||||
|
|
||||||
var client = new YoutubeClient(Common.Http.ClientFactory.Client);
|
var client = new YoutubeClient(Common.Http.ClientFactory.Client);
|
||||||
@ -24,13 +23,13 @@ namespace Wabbajack.Lib.Downloaders.UrlDownloaders
|
|||||||
.Select(line =>
|
.Select(line =>
|
||||||
{
|
{
|
||||||
var segments = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
var segments = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||||
if (segments.Length == 0) return (TimeSpan.Zero, null);
|
if (segments.Length == 0) return (TimeSpan.Zero, string.Empty);
|
||||||
|
|
||||||
if (TryParseEx(segments.First(), out var s1))
|
if (TryParseEx(segments.First(), out var s1))
|
||||||
return (s1, string.Join(" ", segments.Skip(1)));
|
return (s1, string.Join(" ", segments.Skip(1)));
|
||||||
if (TryParseEx(Enumerable.Last(segments), out var s2))
|
if (TryParseEx(Enumerable.Last(segments), out var s2))
|
||||||
return (s2, string.Join(" ", Utils.ButLast(segments)));
|
return (s2, string.Join(" ", Utils.ButLast(segments)));
|
||||||
return (TimeSpan.Zero, null);
|
return (TimeSpan.Zero, string.Empty);
|
||||||
})
|
})
|
||||||
.Where(t => t.Item2 != null)
|
.Where(t => t.Item2 != null)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
@ -21,10 +21,10 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
{
|
{
|
||||||
public class YouTubeDownloader : IDownloader
|
public class YouTubeDownloader : IDownloader
|
||||||
{
|
{
|
||||||
public async Task<AbstractDownloadState> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
public async Task<AbstractDownloadState?> GetDownloaderState(dynamic archiveINI, bool quickMode)
|
||||||
{
|
{
|
||||||
var directURL = (Uri)DownloaderUtils.GetDirectURL(archiveINI);
|
var directURL = (Uri)DownloaderUtils.GetDirectURL(archiveINI);
|
||||||
var state = (State)UriToState(directURL);
|
var state = UriToState(directURL) as State;
|
||||||
if (state == null) return state;
|
if (state == null) return state;
|
||||||
|
|
||||||
var idx = 0;
|
var idx = 0;
|
||||||
@ -45,7 +45,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static AbstractDownloadState UriToState(Uri directURL)
|
internal static AbstractDownloadState? UriToState(Uri directURL)
|
||||||
{
|
{
|
||||||
if (directURL == null || !directURL.Host.EndsWith("youtube.com"))
|
if (directURL == null || !directURL.Host.EndsWith("youtube.com"))
|
||||||
{
|
{
|
||||||
@ -53,7 +53,7 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
}
|
}
|
||||||
|
|
||||||
var key = HttpUtility.ParseQueryString(directURL.Query)["v"];
|
var key = HttpUtility.ParseQueryString(directURL.Query)["v"];
|
||||||
return key != null ? new State {Key = key} : null;
|
return key != null ? new State(key) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Prepare()
|
public async Task Prepare()
|
||||||
@ -63,13 +63,18 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
[JsonName("YouTubeDownloader")]
|
[JsonName("YouTubeDownloader")]
|
||||||
public class State : AbstractDownloadState
|
public class State : AbstractDownloadState
|
||||||
{
|
{
|
||||||
public string Key { get; set; }
|
public string Key { get; }
|
||||||
|
|
||||||
public List<Track> Tracks { get; set; } = new List<Track>();
|
public List<Track> Tracks { get; set; } = new List<Track>();
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override object[] PrimaryKey => new object[] {Key};
|
public override object[] PrimaryKey => new object[] {Key};
|
||||||
|
|
||||||
|
public State(string key)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
}
|
||||||
|
|
||||||
[JsonName("YouTubeTrack")]
|
[JsonName("YouTubeTrack")]
|
||||||
public class Track
|
public class Track
|
||||||
{
|
{
|
||||||
@ -79,8 +84,8 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
WAV
|
WAV
|
||||||
}
|
}
|
||||||
public FormatEnum Format { get; set; }
|
public FormatEnum Format { get; set; }
|
||||||
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
public TimeSpan Start { get; set; }
|
public TimeSpan Start { get; set; }
|
||||||
|
|
||||||
|
@ -12,6 +12,5 @@ namespace Wabbajack.Lib.Exceptions
|
|||||||
Code = code;
|
Code = code;
|
||||||
Reason = reason;
|
Reason = reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,9 @@ namespace Wabbajack.Lib.FileUploader
|
|||||||
{
|
{
|
||||||
public static IObservable<bool> HaveAuthorAPIKey => Utils.HaveEncryptedJsonObservable(Consts.AuthorAPIKeyFile);
|
public static IObservable<bool> HaveAuthorAPIKey => Utils.HaveEncryptedJsonObservable(Consts.AuthorAPIKeyFile);
|
||||||
|
|
||||||
public static string ApiKeyOverride = null;
|
public static string? ApiKeyOverride = null;
|
||||||
|
|
||||||
public static async Task<string> GetAPIKey(string apiKey = null)
|
public static async Task<string> GetAPIKey(string? apiKey = null)
|
||||||
{
|
{
|
||||||
if (ApiKeyOverride != null) return ApiKeyOverride;
|
if (ApiKeyOverride != null) return ApiKeyOverride;
|
||||||
return apiKey ?? (await Consts.LocalAppDataPath.Combine(Consts.AuthorAPIKeyFile).ReadAllTextAsync()).Trim();
|
return apiKey ?? (await Consts.LocalAppDataPath.Combine(Consts.AuthorAPIKeyFile).ReadAllTextAsync()).Trim();
|
||||||
@ -32,7 +32,7 @@ namespace Wabbajack.Lib.FileUploader
|
|||||||
public static Uri UploadURL => new Uri($"{Consts.WabbajackBuildServerUri}upload_file");
|
public static Uri UploadURL => new Uri($"{Consts.WabbajackBuildServerUri}upload_file");
|
||||||
public static long BLOCK_SIZE = (long)1024 * 1024 * 2;
|
public static long BLOCK_SIZE = (long)1024 * 1024 * 2;
|
||||||
public static int MAX_CONNECTIONS = 8;
|
public static int MAX_CONNECTIONS = 8;
|
||||||
public static Task<string> UploadFile(AbsolutePath filename, Action<double> progressFn, string apikey=null)
|
public static Task<string> UploadFile(AbsolutePath filename, Action<double> progressFn, string? apikey = null)
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<string>();
|
var tcs = new TaskCompletionSource<string>();
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
@ -123,7 +123,7 @@ namespace Wabbajack.Lib.FileUploader
|
|||||||
return tcs.Task;
|
return tcs.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<Common.Http.Client> GetAuthorizedClient(string apiKey=null)
|
public static async Task<Common.Http.Client> GetAuthorizedClient(string? apiKey = null)
|
||||||
{
|
{
|
||||||
var client = new Common.Http.Client();
|
var client = new Common.Http.Client();
|
||||||
client.Headers.Add(("X-API-KEY", await GetAPIKey(apiKey)));
|
client.Headers.Add(("X-API-KEY", await GetAPIKey(apiKey)));
|
||||||
|
@ -78,10 +78,10 @@ namespace Wabbajack.Lib.LibCefHelpers
|
|||||||
[JsonName("HttpCookie")]
|
[JsonName("HttpCookie")]
|
||||||
public class Cookie
|
public class Cookie
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; } = string.Empty;
|
||||||
public string Value { get; set; }
|
public string Value { get; set; } = string.Empty;
|
||||||
public string Domain { get; set; }
|
public string Domain { get; set; } = string.Empty;
|
||||||
public string Path { get; set; }
|
public string Path { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Init()
|
public static void Init()
|
||||||
|
@ -25,7 +25,6 @@ namespace Wabbajack.Lib
|
|||||||
{
|
{
|
||||||
public class MO2Compiler : ACompiler
|
public class MO2Compiler : ACompiler
|
||||||
{
|
{
|
||||||
|
|
||||||
private AbsolutePath _mo2DownloadsFolder;
|
private AbsolutePath _mo2DownloadsFolder;
|
||||||
|
|
||||||
public AbsolutePath MO2Folder;
|
public AbsolutePath MO2Folder;
|
||||||
@ -38,7 +37,7 @@ namespace Wabbajack.Lib
|
|||||||
|
|
||||||
public override AbsolutePath GamePath { get; }
|
public override AbsolutePath GamePath { get; }
|
||||||
|
|
||||||
public GameMetaData CompilingGame { get; set; }
|
public GameMetaData CompilingGame { get; }
|
||||||
|
|
||||||
public override AbsolutePath ModListOutputFolder => ((RelativePath)"output_folder").RelativeToEntryPoint();
|
public override AbsolutePath ModListOutputFolder => ((RelativePath)"output_folder").RelativeToEntryPoint();
|
||||||
|
|
||||||
@ -48,7 +47,19 @@ namespace Wabbajack.Lib
|
|||||||
Consts.LocalAppDataPath.Combine(
|
Consts.LocalAppDataPath.Combine(
|
||||||
$"vfs_compile_cache-{Path.Combine((string)MO2Folder ?? "Unknown", "ModOrganizer.exe").StringSha256Hex()}.bin");
|
$"vfs_compile_cache-{Path.Combine((string)MO2Folder ?? "Unknown", "ModOrganizer.exe").StringSha256Hex()}.bin");
|
||||||
|
|
||||||
|
public dynamic MO2Ini { get; }
|
||||||
|
|
||||||
|
public static AbsolutePath GetTypicalDownloadsFolder(AbsolutePath mo2Folder) => mo2Folder.Combine("downloads");
|
||||||
|
|
||||||
|
public AbsolutePath MO2ProfileDir => MO2Folder.Combine("profiles", MO2Profile);
|
||||||
|
|
||||||
|
public ConcurrentBag<Directive> ExtraFiles { get; private set; } = new ConcurrentBag<Directive>();
|
||||||
|
public Dictionary<AbsolutePath, dynamic> ModInis { get; } = new Dictionary<AbsolutePath, dynamic>();
|
||||||
|
|
||||||
|
public HashSet<string> SelectedProfiles { get; set; } = new HashSet<string>();
|
||||||
|
|
||||||
public MO2Compiler(AbsolutePath mo2Folder, string mo2Profile, AbsolutePath outputFile)
|
public MO2Compiler(AbsolutePath mo2Folder, string mo2Profile, AbsolutePath outputFile)
|
||||||
|
: base(steps: 20)
|
||||||
{
|
{
|
||||||
MO2Folder = mo2Folder;
|
MO2Folder = mo2Folder;
|
||||||
MO2Profile = mo2Profile;
|
MO2Profile = mo2Profile;
|
||||||
@ -59,8 +70,6 @@ namespace Wabbajack.Lib
|
|||||||
ModListOutputFile = outputFile;
|
ModListOutputFile = outputFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public dynamic MO2Ini { get; }
|
|
||||||
|
|
||||||
public AbsolutePath MO2DownloadsFolder
|
public AbsolutePath MO2DownloadsFolder
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -75,20 +84,10 @@ namespace Wabbajack.Lib
|
|||||||
set => _mo2DownloadsFolder = value;
|
set => _mo2DownloadsFolder = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AbsolutePath GetTypicalDownloadsFolder(AbsolutePath mo2Folder) => mo2Folder.Combine("downloads");
|
|
||||||
|
|
||||||
public AbsolutePath MO2ProfileDir => MO2Folder.Combine("profiles", MO2Profile);
|
|
||||||
|
|
||||||
internal UserStatus User { get; private set; }
|
|
||||||
public ConcurrentBag<Directive> ExtraFiles { get; private set; }
|
|
||||||
public Dictionary<AbsolutePath, dynamic> ModInis { get; private set; }
|
|
||||||
|
|
||||||
public HashSet<string> SelectedProfiles { get; set; } = new HashSet<string>();
|
|
||||||
|
|
||||||
protected override async Task<bool> _Begin(CancellationToken cancel)
|
protected override async Task<bool> _Begin(CancellationToken cancel)
|
||||||
{
|
{
|
||||||
if (cancel.IsCancellationRequested) return false;
|
if (cancel.IsCancellationRequested) return false;
|
||||||
ConfigureProcessor(20, ConstructDynamicNumThreads(await RecommendQueueSize()));
|
Queue.SetActiveThreadsObservable(ConstructDynamicNumThreads(await RecommendQueueSize()));
|
||||||
UpdateTracker.Reset();
|
UpdateTracker.Reset();
|
||||||
UpdateTracker.NextStep("Gathering information");
|
UpdateTracker.NextStep("Gathering information");
|
||||||
Info("Looking for other profiles");
|
Info("Looking for other profiles");
|
||||||
@ -126,6 +125,11 @@ namespace Wabbajack.Lib
|
|||||||
|
|
||||||
if (lootPath.Exists)
|
if (lootPath.Exists)
|
||||||
{
|
{
|
||||||
|
if (CompilingGame.MO2Name == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Compiling game had no MO2 name specified.");
|
||||||
|
}
|
||||||
|
|
||||||
var lootGameDirs = new []
|
var lootGameDirs = new []
|
||||||
{
|
{
|
||||||
CompilingGame.MO2Name, // most of the games use the MO2 name
|
CompilingGame.MO2Name, // most of the games use the MO2 name
|
||||||
@ -170,9 +174,8 @@ namespace Wabbajack.Lib
|
|||||||
|
|
||||||
IndexedArchives = (await MO2DownloadsFolder.EnumerateFiles()
|
IndexedArchives = (await MO2DownloadsFolder.EnumerateFiles()
|
||||||
.Where(f => f.WithExtension(Consts.MetaFileExtension).Exists)
|
.Where(f => f.WithExtension(Consts.MetaFileExtension).Exists)
|
||||||
.PMap(Queue, async f => new IndexedArchive
|
.PMap(Queue, async f => new IndexedArchive(VFS.Index.ByRootPath[f])
|
||||||
{
|
{
|
||||||
File = VFS.Index.ByRootPath[f],
|
|
||||||
Name = (string)f.FileName,
|
Name = (string)f.FileName,
|
||||||
IniData = f.WithExtension(Consts.MetaFileExtension).LoadIniFile(),
|
IniData = f.WithExtension(Consts.MetaFileExtension).LoadIniFile(),
|
||||||
Meta = await f.WithExtension(Consts.MetaFileExtension).ReadAllTextAsync()
|
Meta = await f.WithExtension(Consts.MetaFileExtension).ReadAllTextAsync()
|
||||||
@ -216,10 +219,9 @@ namespace Wabbajack.Lib
|
|||||||
.GroupBy(f => f.Hash)
|
.GroupBy(f => f.Hash)
|
||||||
.ToDictionary(f => f.Key, f => f.AsEnumerable());
|
.ToDictionary(f => f.Key, f => f.AsEnumerable());
|
||||||
|
|
||||||
AllFiles = mo2Files.Concat(gameFiles)
|
AllFiles.SetTo(mo2Files.Concat(gameFiles)
|
||||||
.Concat(lootFiles)
|
.Concat(lootFiles)
|
||||||
.DistinctBy(f => f.Path)
|
.DistinctBy(f => f.Path));
|
||||||
.ToList();
|
|
||||||
|
|
||||||
Info($"Found {AllFiles.Count} files to build into mod list");
|
Info($"Found {AllFiles.Count} files to build into mod list");
|
||||||
|
|
||||||
@ -239,13 +241,10 @@ namespace Wabbajack.Lib
|
|||||||
Error($"Found {dups.Count} duplicates, exiting");
|
Error($"Found {dups.Count} duplicates, exiting");
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtraFiles = new ConcurrentBag<Directive>();
|
|
||||||
|
|
||||||
|
|
||||||
if (cancel.IsCancellationRequested) return false;
|
if (cancel.IsCancellationRequested) return false;
|
||||||
UpdateTracker.NextStep("Loading INIs");
|
UpdateTracker.NextStep("Loading INIs");
|
||||||
|
|
||||||
ModInis = MO2Folder.Combine(Consts.MO2ModFolderName)
|
ModInis.SetTo(MO2Folder.Combine(Consts.MO2ModFolderName)
|
||||||
.EnumerateDirectories()
|
.EnumerateDirectories()
|
||||||
.Select(f =>
|
.Select(f =>
|
||||||
{
|
{
|
||||||
@ -254,7 +253,7 @@ namespace Wabbajack.Lib
|
|||||||
return metaPath.Exists ? (mod_name: f, metaPath.LoadIniFile()) : default;
|
return metaPath.Exists ? (mod_name: f, metaPath.LoadIniFile()) : default;
|
||||||
})
|
})
|
||||||
.Where(f => f.Item1 != default)
|
.Where(f => f.Item1 != default)
|
||||||
.ToDictionary(f => f.Item1, f => f.Item2);
|
.Select(f => new KeyValuePair<AbsolutePath, dynamic>(f.Item1, f.Item2)));
|
||||||
|
|
||||||
if (cancel.IsCancellationRequested) return false;
|
if (cancel.IsCancellationRequested) return false;
|
||||||
var stack = MakeStack();
|
var stack = MakeStack();
|
||||||
@ -270,7 +269,7 @@ namespace Wabbajack.Lib
|
|||||||
PrintNoMatches(noMatch);
|
PrintNoMatches(noMatch);
|
||||||
if (CheckForNoMatchExit(noMatch)) return false;
|
if (CheckForNoMatchExit(noMatch)) return false;
|
||||||
|
|
||||||
InstallDirectives = results.Where(i => !(i is IgnoredDirectly)).ToList();
|
InstallDirectives.SetTo(results.Where(i => !(i is IgnoredDirectly)));
|
||||||
|
|
||||||
Info("Getting Nexus api_key, please click authorize if a browser window appears");
|
Info("Getting Nexus api_key, please click authorize if a browser window appears");
|
||||||
|
|
||||||
@ -337,7 +336,7 @@ namespace Wabbajack.Lib
|
|||||||
{
|
{
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
})).Where(a => a != null).ToHashSet();
|
})).NotNull().ToHashSet();
|
||||||
|
|
||||||
if (remove.Count == 0)
|
if (remove.Count == 0)
|
||||||
return;
|
return;
|
||||||
@ -389,7 +388,6 @@ namespace Wabbajack.Lib
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task IncludeArchiveMetadata()
|
private async Task IncludeArchiveMetadata()
|
||||||
{
|
{
|
||||||
Utils.Log($"Including {SelectedArchives.Count} .meta files for downloads");
|
Utils.Log($"Including {SelectedArchives.Count} .meta files for downloads");
|
||||||
@ -411,13 +409,12 @@ namespace Wabbajack.Lib
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void ResetMembers()
|
private void ResetMembers()
|
||||||
{
|
{
|
||||||
AllFiles = null;
|
AllFiles = new List<RawSourceFile>();
|
||||||
InstallDirectives = null;
|
InstallDirectives = new List<Directive>();
|
||||||
SelectedArchives = null;
|
SelectedArchives = new List<Archive>();
|
||||||
ExtraFiles = null;
|
ExtraFiles = new ConcurrentBag<Directive>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fills in the Patch fields in files that require them
|
/// Fills in the Patch fields in files that require them
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -487,8 +484,7 @@ namespace Wabbajack.Lib
|
|||||||
return returnStream;
|
return returnStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error($"Couldn't load data for {to}");
|
throw new ArgumentException($"Couldn't load data for {to}");
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<ICompilationStep> GetStack()
|
public override IEnumerable<ICompilationStep> GetStack()
|
||||||
@ -567,12 +563,5 @@ namespace Wabbajack.Lib
|
|||||||
new DropAll(this)
|
new DropAll(this)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public class IndexedFileMatch
|
|
||||||
{
|
|
||||||
public IndexedArchive Archive;
|
|
||||||
public IndexedArchiveEntry Entry;
|
|
||||||
public DateTime LastModified;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,14 +32,18 @@ namespace Wabbajack.Lib
|
|||||||
|
|
||||||
public AbsolutePath? GameFolder { get; set; }
|
public AbsolutePath? GameFolder { get; set; }
|
||||||
|
|
||||||
|
public GameMetaData Game { get; }
|
||||||
|
|
||||||
public MO2Installer(AbsolutePath archive, ModList modList, AbsolutePath outputFolder, AbsolutePath downloadFolder, SystemParameters parameters)
|
public MO2Installer(AbsolutePath archive, ModList modList, AbsolutePath outputFolder, AbsolutePath downloadFolder, SystemParameters parameters)
|
||||||
: base(
|
: base(
|
||||||
archive: archive,
|
archive: archive,
|
||||||
modList: modList,
|
modList: modList,
|
||||||
outputFolder: outputFolder,
|
outputFolder: outputFolder,
|
||||||
downloadFolder: downloadFolder,
|
downloadFolder: downloadFolder,
|
||||||
parameters: parameters)
|
parameters: parameters,
|
||||||
|
steps: 20)
|
||||||
{
|
{
|
||||||
|
Game = ModList.GameType.MetaData();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task<bool> _Begin(CancellationToken cancel)
|
protected override async Task<bool> _Begin(CancellationToken cancel)
|
||||||
@ -48,28 +52,27 @@ namespace Wabbajack.Lib
|
|||||||
var metric = Metrics.Send(Metrics.BeginInstall, ModList.Name);
|
var metric = Metrics.Send(Metrics.BeginInstall, ModList.Name);
|
||||||
Utils.Log("Configuring Processor");
|
Utils.Log("Configuring Processor");
|
||||||
|
|
||||||
ConfigureProcessor(20, ConstructDynamicNumThreads(await RecommendQueueSize()));
|
Queue.SetActiveThreadsObservable(ConstructDynamicNumThreads(await RecommendQueueSize()));
|
||||||
var game = ModList.GameType.MetaData();
|
|
||||||
|
|
||||||
if (GameFolder == null)
|
if (GameFolder == null)
|
||||||
GameFolder = game.GameLocation();
|
GameFolder = Game.TryGetGameLocation();
|
||||||
|
|
||||||
if (GameFolder == null)
|
if (GameFolder == null)
|
||||||
{
|
{
|
||||||
var otherGame = game.CommonlyConfusedWith.Where(g => g.MetaData().IsInstalled).Select(g => g.MetaData()).FirstOrDefault();
|
var otherGame = Game.CommonlyConfusedWith.Where(g => g.MetaData().IsInstalled).Select(g => g.MetaData()).FirstOrDefault();
|
||||||
if (otherGame != null)
|
if (otherGame != null)
|
||||||
{
|
{
|
||||||
await Utils.Log(new CriticalFailureIntervention(
|
await Utils.Log(new CriticalFailureIntervention(
|
||||||
$"In order to do a proper install Wabbajack needs to know where your {game.HumanFriendlyGameName} folder resides. However this game doesn't seem to be installed, we did however find a installed " +
|
$"In order to do a proper install Wabbajack needs to know where your {Game.HumanFriendlyGameName} folder resides. However this game doesn't seem to be installed, we did however find a installed " +
|
||||||
$"copy of {otherGame.HumanFriendlyGameName}, did you install the wrong game?",
|
$"copy of {otherGame.HumanFriendlyGameName}, did you install the wrong game?",
|
||||||
$"Could not locate {game.HumanFriendlyGameName}"))
|
$"Could not locate {Game.HumanFriendlyGameName}"))
|
||||||
.Task;
|
.Task;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await Utils.Log(new CriticalFailureIntervention(
|
await Utils.Log(new CriticalFailureIntervention(
|
||||||
$"In order to do a proper install Wabbajack needs to know where your {game.HumanFriendlyGameName} folder resides. However this game doesn't seem to be installed",
|
$"In order to do a proper install Wabbajack needs to know where your {Game.HumanFriendlyGameName} folder resides. However this game doesn't seem to be installed",
|
||||||
$"Could not locate {game.HumanFriendlyGameName}"))
|
$"Could not locate {Game.HumanFriendlyGameName}"))
|
||||||
.Task;
|
.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +172,6 @@ namespace Wabbajack.Lib
|
|||||||
|
|
||||||
private void CreateOutputMods()
|
private void CreateOutputMods()
|
||||||
{
|
{
|
||||||
|
|
||||||
OutputFolder.Combine("profiles")
|
OutputFolder.Combine("profiles")
|
||||||
.EnumerateFiles(true)
|
.EnumerateFiles(true)
|
||||||
.Where(f => f.FileName == Consts.SettingsIni)
|
.Where(f => f.FileName == Consts.SettingsIni)
|
||||||
@ -235,7 +237,7 @@ namespace Wabbajack.Lib
|
|||||||
foreach (var esm in ModList.Directives.OfType<CleanedESM>().ToList())
|
foreach (var esm in ModList.Directives.OfType<CleanedESM>().ToList())
|
||||||
{
|
{
|
||||||
var filename = esm.To.FileName;
|
var filename = esm.To.FileName;
|
||||||
var gameFile = GameFolder.Value.Combine((RelativePath)"Data", filename);
|
var gameFile = GameFolder!.Value.Combine((RelativePath)"Data", filename);
|
||||||
Utils.Log($"Validating {filename}");
|
Utils.Log($"Validating {filename}");
|
||||||
var hash = gameFile.FileHash();
|
var hash = gameFile.FileHash();
|
||||||
if (hash != esm.SourceESMHash)
|
if (hash != esm.SourceESMHash)
|
||||||
@ -310,7 +312,7 @@ namespace Wabbajack.Lib
|
|||||||
private async Task GenerateCleanedESM(CleanedESM directive)
|
private async Task GenerateCleanedESM(CleanedESM directive)
|
||||||
{
|
{
|
||||||
var filename = directive.To.FileName;
|
var filename = directive.To.FileName;
|
||||||
var gameFile = GameFolder.Value.Combine((RelativePath)"Data", filename);
|
var gameFile = GameFolder!.Value.Combine((RelativePath)"Data", filename);
|
||||||
Info($"Generating cleaned ESM for {filename}");
|
Info($"Generating cleaned ESM for {filename}");
|
||||||
if (!gameFile.Exists) throw new InvalidDataException($"Missing {filename} at {gameFile}");
|
if (!gameFile.Exists) throw new InvalidDataException($"Missing {filename} at {gameFile}");
|
||||||
Status($"Hashing game version of {filename}");
|
Status($"Hashing game version of {filename}");
|
||||||
@ -329,6 +331,10 @@ namespace Wabbajack.Lib
|
|||||||
|
|
||||||
private void SetScreenSizeInPrefs()
|
private void SetScreenSizeInPrefs()
|
||||||
{
|
{
|
||||||
|
if (SystemParameters == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("System Parameters was null. Cannot set screen size prefs");
|
||||||
|
}
|
||||||
var config = new IniParserConfiguration {AllowDuplicateKeys = true, AllowDuplicateSections = true};
|
var config = new IniParserConfiguration {AllowDuplicateKeys = true, AllowDuplicateSections = true};
|
||||||
foreach (var file in OutputFolder.Combine("profiles").EnumerateFiles()
|
foreach (var file in OutputFolder.Combine("profiles").EnumerateFiles()
|
||||||
.Where(f => ((string)f.FileName).EndsWith("refs.ini")))
|
.Where(f => ((string)f.FileName).EndsWith("refs.ini")))
|
||||||
@ -375,8 +381,8 @@ namespace Wabbajack.Lib
|
|||||||
{
|
{
|
||||||
var data = Encoding.UTF8.GetString(await LoadBytesFromPath(directive.SourceDataID));
|
var data = Encoding.UTF8.GetString(await LoadBytesFromPath(directive.SourceDataID));
|
||||||
|
|
||||||
data = data.Replace(Consts.GAME_PATH_MAGIC_BACK, (string)GameFolder);
|
data = data.Replace(Consts.GAME_PATH_MAGIC_BACK, (string)GameFolder!);
|
||||||
data = data.Replace(Consts.GAME_PATH_MAGIC_DOUBLE_BACK, ((string)GameFolder).Replace("\\", "\\\\"));
|
data = data.Replace(Consts.GAME_PATH_MAGIC_DOUBLE_BACK, ((string)GameFolder!).Replace("\\", "\\\\"));
|
||||||
data = data.Replace(Consts.GAME_PATH_MAGIC_FORWARD, ((string)GameFolder).Replace("\\", "/"));
|
data = data.Replace(Consts.GAME_PATH_MAGIC_FORWARD, ((string)GameFolder).Replace("\\", "/"));
|
||||||
|
|
||||||
data = data.Replace(Consts.MO2_PATH_MAGIC_BACK, (string)OutputFolder);
|
data = data.Replace(Consts.MO2_PATH_MAGIC_BACK, (string)OutputFolder);
|
||||||
|
@ -41,12 +41,11 @@ namespace Wabbajack.Lib
|
|||||||
InstallSize = modlist.InstallSize;
|
InstallSize = modlist.InstallSize;
|
||||||
|
|
||||||
// meta is being omitted due to it being useless and not very space friendly
|
// meta is being omitted due to it being useless and not very space friendly
|
||||||
Archives = modlist.Archives.Select(a => new Archive
|
Archives = modlist.Archives.Select(a => new Archive(a.State)
|
||||||
{
|
{
|
||||||
Hash = a.Hash,
|
Hash = a.Hash,
|
||||||
Name = a.Name,
|
Name = a.Name,
|
||||||
Size = a.Size,
|
Size = a.Size,
|
||||||
State = a.State
|
|
||||||
}).ToList();
|
}).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,13 +13,13 @@ namespace Wabbajack.Lib.ModListRegistry
|
|||||||
public class ModlistMetadata
|
public class ModlistMetadata
|
||||||
{
|
{
|
||||||
[JsonProperty("title")]
|
[JsonProperty("title")]
|
||||||
public string Title { get; set; }
|
public string Title { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonProperty("description")]
|
[JsonProperty("description")]
|
||||||
public string Description { get; set; }
|
public string Description { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonProperty("author")]
|
[JsonProperty("author")]
|
||||||
public string Author { get; set; }
|
public string Author { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonProperty("game")]
|
[JsonProperty("game")]
|
||||||
public Game Game { get; set; }
|
public Game Game { get; set; }
|
||||||
@ -33,7 +33,7 @@ namespace Wabbajack.Lib.ModListRegistry
|
|||||||
public LinksObject Links { get; set; } = new LinksObject();
|
public LinksObject Links { get; set; } = new LinksObject();
|
||||||
|
|
||||||
[JsonProperty("download_metadata")]
|
[JsonProperty("download_metadata")]
|
||||||
public DownloadMetadata DownloadMetadata { get; set; }
|
public DownloadMetadata? DownloadMetadata { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public ModListSummary ValidationSummary { get; set; } = new ModListSummary();
|
public ModListSummary ValidationSummary { get; set; } = new ModListSummary();
|
||||||
@ -42,22 +42,18 @@ namespace Wabbajack.Lib.ModListRegistry
|
|||||||
public class LinksObject
|
public class LinksObject
|
||||||
{
|
{
|
||||||
[JsonProperty("image")]
|
[JsonProperty("image")]
|
||||||
public string ImageUri { get; set; }
|
public string ImageUri { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonProperty("readme")]
|
[JsonProperty("readme")]
|
||||||
public string Readme { get; set; }
|
public string Readme { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonProperty("download")]
|
[JsonProperty("download")]
|
||||||
public string Download { get; set; }
|
public string Download { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonProperty("machineURL")]
|
[JsonProperty("machineURL")]
|
||||||
public string MachineURL { get; set; }
|
public string MachineURL { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static async Task<List<ModlistMetadata>> LoadFromGithub()
|
public static async Task<List<ModlistMetadata>> LoadFromGithub()
|
||||||
{
|
{
|
||||||
var client = new Common.Http.Client();
|
var client = new Common.Http.Client();
|
||||||
@ -103,18 +99,17 @@ namespace Wabbajack.Lib.ModListRegistry
|
|||||||
public long SizeOfArchives { get; set; }
|
public long SizeOfArchives { get; set; }
|
||||||
public long NumberOfInstalledFiles { get; set; }
|
public long NumberOfInstalledFiles { get; set; }
|
||||||
public long SizeOfInstalledFiles { get; set; }
|
public long SizeOfInstalledFiles { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonName("ModListSummary")]
|
[JsonName("ModListSummary")]
|
||||||
public class ModListSummary
|
public class ModListSummary
|
||||||
{
|
{
|
||||||
[JsonProperty("name")]
|
[JsonProperty("name")]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonProperty("machineURL")]
|
[JsonProperty("machineURL")]
|
||||||
public string MachineURL { get; set; }
|
public string MachineURL { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonProperty("checked")]
|
[JsonProperty("checked")]
|
||||||
public DateTime Checked { get; set; }
|
public DateTime Checked { get; set; }
|
||||||
[JsonProperty("failed")]
|
[JsonProperty("failed")]
|
||||||
|
@ -4,57 +4,57 @@ namespace Wabbajack.Lib.NexusApi
|
|||||||
{
|
{
|
||||||
public class UserStatus
|
public class UserStatus
|
||||||
{
|
{
|
||||||
public string email;
|
public string email = string.Empty;
|
||||||
public bool is_premium;
|
public bool is_premium;
|
||||||
public bool is_supporter;
|
public bool is_supporter;
|
||||||
public string key;
|
public string key = string.Empty;
|
||||||
public string name;
|
public string name = string.Empty;
|
||||||
public string profile_url;
|
public string profile_url = string.Empty;
|
||||||
public string user_id;
|
public string user_id = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class NexusFileInfo
|
public class NexusFileInfo
|
||||||
{
|
{
|
||||||
public long category_id { get; set; }
|
public long category_id { get; set; }
|
||||||
public string category_name { get; set; }
|
public string category_name { get; set; } = string.Empty;
|
||||||
public string changelog_html { get; set; }
|
public string changelog_html { get; set; } = string.Empty;
|
||||||
public string description { get; set; }
|
public string description { get; set; } = string.Empty;
|
||||||
public string external_virus_scan_url { get; set; }
|
public string external_virus_scan_url { get; set; } = string.Empty;
|
||||||
public long file_id { get; set; }
|
public long file_id { get; set; }
|
||||||
public string file_name { get; set; }
|
public string file_name { get; set; } = string.Empty;
|
||||||
public bool is_primary { get; set; }
|
public bool is_primary { get; set; }
|
||||||
public string mod_version { get; set; }
|
public string mod_version { get; set; } = string.Empty;
|
||||||
public string name { get; set; }
|
public string name { get; set; } = string.Empty;
|
||||||
public long size { get; set; }
|
public long size { get; set; }
|
||||||
public long size_kb { get; set; }
|
public long size_kb { get; set; }
|
||||||
public DateTime uploaded_time { get; set; }
|
public DateTime uploaded_time { get; set; }
|
||||||
public long uploaded_timestamp { get; set; }
|
public long uploaded_timestamp { get; set; }
|
||||||
public string version { get; set; }
|
public string version { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ModInfo
|
public class ModInfo
|
||||||
{
|
{
|
||||||
public uint _internal_version { get; set; }
|
public uint _internal_version { get; set; }
|
||||||
public string game_name { get; set; }
|
public string game_name { get; set; } = string.Empty;
|
||||||
public string mod_id { get; set; }
|
public string mod_id { get; set; } = string.Empty;
|
||||||
public string name { get; set; }
|
public string name { get; set; } = string.Empty;
|
||||||
public string summary { get; set; }
|
public string summary { get; set; } = string.Empty;
|
||||||
public string author { get; set; }
|
public string author { get; set; } = string.Empty;
|
||||||
public string uploaded_by { get; set; }
|
public string uploaded_by { get; set; } = string.Empty;
|
||||||
public string uploaded_users_profile_url { get; set; }
|
public string uploaded_users_profile_url { get; set; } = string.Empty;
|
||||||
public string picture_url { get; set; }
|
public string picture_url { get; set; } = string.Empty;
|
||||||
public bool contains_adult_content { get; set; }
|
public bool contains_adult_content { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MD5Response
|
public class MD5Response
|
||||||
{
|
{
|
||||||
public ModInfo mod;
|
public ModInfo? mod;
|
||||||
public NexusFileInfo file_details;
|
public NexusFileInfo? file_details;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EndorsementResponse
|
public class EndorsementResponse
|
||||||
{
|
{
|
||||||
public string message;
|
public string message = string.Empty;
|
||||||
public string status;
|
public string status = string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,12 +27,11 @@ namespace Wabbajack.Lib.NexusApi
|
|||||||
|
|
||||||
#region Authentication
|
#region Authentication
|
||||||
|
|
||||||
public string ApiKey { get; }
|
public string? ApiKey { get; }
|
||||||
|
|
||||||
public bool IsAuthenticated => ApiKey != null;
|
public bool IsAuthenticated => ApiKey != null;
|
||||||
|
|
||||||
private Task<UserStatus> _userStatus;
|
private Task<UserStatus>? _userStatus;
|
||||||
|
|
||||||
public Task<UserStatus> UserStatus
|
public Task<UserStatus> UserStatus
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -214,7 +213,7 @@ namespace Wabbajack.Lib.NexusApi
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private NexusApiClient(string apiKey = null)
|
private NexusApiClient(string? apiKey = null)
|
||||||
{
|
{
|
||||||
ApiKey = apiKey;
|
ApiKey = apiKey;
|
||||||
|
|
||||||
@ -230,9 +229,9 @@ namespace Wabbajack.Lib.NexusApi
|
|||||||
Directory.CreateDirectory(Consts.NexusCacheDirectory);
|
Directory.CreateDirectory(Consts.NexusCacheDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<NexusApiClient> Get(string apiKey = null)
|
public static async Task<NexusApiClient> Get(string? apiKey = null)
|
||||||
{
|
{
|
||||||
apiKey = apiKey ?? await GetApiKey();
|
apiKey ??= await GetApiKey();
|
||||||
return new NexusApiClient(apiKey);
|
return new NexusApiClient(apiKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,9 +315,10 @@ namespace Wabbajack.Lib.NexusApi
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GetModFilesResponse
|
public class GetModFilesResponse
|
||||||
{
|
{
|
||||||
public List<NexusFileInfo> files { get; set; }
|
public List<NexusFileInfo> files { get; set; } = new List<NexusFileInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<GetModFilesResponse> GetModFiles(Game game, long modid, bool useCache = true)
|
public async Task<GetModFilesResponse> GetModFiles(Game game, long modid, bool useCache = true)
|
||||||
@ -350,18 +350,18 @@ namespace Wabbajack.Lib.NexusApi
|
|||||||
|
|
||||||
private class DownloadLink
|
private class DownloadLink
|
||||||
{
|
{
|
||||||
public string URI { get; set; }
|
public string URI { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MethodInfo CacheMethod { get; set; }
|
private static string? _localCacheDir;
|
||||||
|
|
||||||
private static string _localCacheDir;
|
|
||||||
public static string LocalCacheDir
|
public static string LocalCacheDir
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_localCacheDir == null)
|
if (_localCacheDir == null)
|
||||||
_localCacheDir = Environment.GetEnvironmentVariable("NEXUSCACHEDIR");
|
_localCacheDir = Environment.GetEnvironmentVariable("NEXUSCACHEDIR");
|
||||||
|
if (_localCacheDir == null)
|
||||||
|
throw new ArgumentNullException($"Enviornment variable could not be located: NEXUSCACHEDIR");
|
||||||
return _localCacheDir;
|
return _localCacheDir;
|
||||||
}
|
}
|
||||||
set => _localCacheDir = value;
|
set => _localCacheDir = value;
|
||||||
|
@ -19,15 +19,10 @@ namespace Wabbajack.Lib.NexusApi
|
|||||||
|
|
||||||
public static string FixupSummary(string argSummary)
|
public static string FixupSummary(string argSummary)
|
||||||
{
|
{
|
||||||
if (argSummary != null)
|
return argSummary.Replace("'", "'")
|
||||||
{
|
.Replace("<br/>", "\n\n")
|
||||||
return argSummary.Replace("'", "'")
|
.Replace("<br />", "\n\n")
|
||||||
.Replace("<br/>", "\n\n")
|
.Replace("!", "!");
|
||||||
.Replace("<br />", "\n\n")
|
|
||||||
.Replace("!", "!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return argSummary;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,7 @@ namespace Wabbajack.Lib.NexusApi
|
|||||||
{
|
{
|
||||||
var parts = link.Uri.AbsolutePath.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
var parts = link.Uri.AbsolutePath.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
var foundGame = GameRegistry.GetByFuzzyName(parts[0]);
|
if (!GameRegistry.TryGetByFuzzyName(parts[0], out var foundGame))
|
||||||
if (foundGame == null)
|
|
||||||
{
|
{
|
||||||
game = Game.Oblivion;
|
game = Game.Oblivion;
|
||||||
modId = 0;
|
modId = 0;
|
||||||
@ -65,7 +64,8 @@ namespace Wabbajack.Lib.NexusApi
|
|||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}).Where(v => v != null);
|
})
|
||||||
|
.NotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ namespace Wabbajack.Lib.NexusApi
|
|||||||
public class RequestNexusAuthorization : AUserIntervention
|
public class RequestNexusAuthorization : AUserIntervention
|
||||||
{
|
{
|
||||||
public override string ShortDescription => "Getting User's Nexus API Key";
|
public override string ShortDescription => "Getting User's Nexus API Key";
|
||||||
public override string ExtendedDescription { get; }
|
public override string ExtendedDescription { get; } = string.Empty;
|
||||||
|
|
||||||
private readonly TaskCompletionSource<string> _source = new TaskCompletionSource<string>();
|
private readonly TaskCompletionSource<string> _source = new TaskCompletionSource<string>();
|
||||||
public Task<string> Task => _source.Task;
|
public Task<string> Task => _source.Task;
|
||||||
|
@ -10,7 +10,7 @@ namespace Wabbajack.Lib
|
|||||||
public class ConfirmUpdateOfExistingInstall : ConfirmationIntervention
|
public class ConfirmUpdateOfExistingInstall : ConfirmationIntervention
|
||||||
{
|
{
|
||||||
public AbsolutePath OutputFolder { get; set; }
|
public AbsolutePath OutputFolder { get; set; }
|
||||||
public string ModListName { get; set; }
|
public string ModListName { get; set; } = string.Empty;
|
||||||
|
|
||||||
public override string ShortDescription { get; } = "Do you want to overwrite existing files?";
|
public override string ShortDescription { get; } = "Do you want to overwrite existing files?";
|
||||||
|
|
||||||
|
@ -9,9 +9,9 @@ namespace Wabbajack.Lib
|
|||||||
public class ManuallyDownloadFile : AUserIntervention
|
public class ManuallyDownloadFile : AUserIntervention
|
||||||
{
|
{
|
||||||
public ManualDownloader.State State { get; }
|
public ManualDownloader.State State { get; }
|
||||||
public override string ShortDescription { get; }
|
public override string ShortDescription { get; } = string.Empty;
|
||||||
public override string ExtendedDescription { get; }
|
public override string ExtendedDescription { get; } = string.Empty;
|
||||||
|
|
||||||
private TaskCompletionSource<(Uri, Common.Http.Client)> _tcs = new TaskCompletionSource<(Uri, Common.Http.Client)>();
|
private TaskCompletionSource<(Uri, Common.Http.Client)> _tcs = new TaskCompletionSource<(Uri, Common.Http.Client)>();
|
||||||
public Task<(Uri, Common.Http.Client)> Task => _tcs.Task;
|
public Task<(Uri, Common.Http.Client)> Task => _tcs.Task;
|
||||||
|
|
||||||
|
@ -9,9 +9,9 @@ namespace Wabbajack.Lib
|
|||||||
public class ManuallyDownloadNexusFile : AUserIntervention
|
public class ManuallyDownloadNexusFile : AUserIntervention
|
||||||
{
|
{
|
||||||
public NexusDownloader.State State { get; }
|
public NexusDownloader.State State { get; }
|
||||||
public override string ShortDescription { get; }
|
public override string ShortDescription { get; } = string.Empty;
|
||||||
public override string ExtendedDescription { get; }
|
public override string ExtendedDescription { get; } = string.Empty;
|
||||||
|
|
||||||
private TaskCompletionSource<Uri> _tcs = new TaskCompletionSource<Uri>();
|
private TaskCompletionSource<Uri> _tcs = new TaskCompletionSource<Uri>();
|
||||||
public Task<Uri> Task => _tcs.Task;
|
public Task<Uri> Task => _tcs.Task;
|
||||||
|
|
||||||
|
@ -14,9 +14,9 @@ namespace Wabbajack.Lib
|
|||||||
public int ScreenWidth { get; set; }
|
public int ScreenWidth { get; set; }
|
||||||
public long VideoMemorySize { get; set; }
|
public long VideoMemorySize { get; set; }
|
||||||
public long SystemMemorySize { get; set; }
|
public long SystemMemorySize { get; set; }
|
||||||
|
|
||||||
public Version WindowsVersion { get; set; }
|
public Version WindowsVersion { get; set; } = Environment.OSVersion.Version;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Value used in LE ENBs for VideoMemorySizeMb
|
/// Value used in LE ENBs for VideoMemorySizeMb
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -10,33 +10,32 @@ namespace Wabbajack.Lib.Validation
|
|||||||
public bool? CanUseInOtherGames { get; set; }
|
public bool? CanUseInOtherGames { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Author
|
//public class Author
|
||||||
{
|
//{
|
||||||
public Permissions Permissions { get; set; }
|
// public Permissions Permissions { get; set; }
|
||||||
public Dictionary<string, Game> Games;
|
// public Dictionary<string, Game> Games;
|
||||||
}
|
//}
|
||||||
|
|
||||||
public class Game
|
//public class Game
|
||||||
{
|
//{
|
||||||
public Permissions Permissions;
|
// public Permissions Permissions;
|
||||||
public Dictionary<string, Mod> Mods;
|
// public Dictionary<string, Mod> Mods;
|
||||||
}
|
//}
|
||||||
|
|
||||||
public class Mod
|
//public class Mod
|
||||||
{
|
//{
|
||||||
public Permissions Permissions;
|
// public Permissions Permissions;
|
||||||
public Dictionary<string, File> Files;
|
// public Dictionary<string, File> Files;
|
||||||
}
|
//}
|
||||||
|
|
||||||
public class File
|
|
||||||
{
|
|
||||||
public Permissions Permissions;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
//public class File
|
||||||
|
//{
|
||||||
|
// public Permissions Permissions;
|
||||||
|
//}
|
||||||
|
|
||||||
public class ServerWhitelist
|
public class ServerWhitelist
|
||||||
{
|
{
|
||||||
public List<string> GoogleIDs;
|
public List<string> GoogleIDs = new List<string>();
|
||||||
public List<string> AllowedPrefixes;
|
public List<string> AllowedPrefixes = new List<string>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ namespace Wabbajack.Lib
|
|||||||
protected void RaiseAndSetIfChanged<T>(
|
protected void RaiseAndSetIfChanged<T>(
|
||||||
ref T item,
|
ref T item,
|
||||||
T newItem,
|
T newItem,
|
||||||
[CallerMemberName] string propertyName = null)
|
[CallerMemberName] string? propertyName = null)
|
||||||
{
|
{
|
||||||
if (EqualityComparer<T>.Default.Equals(item, newItem)) return;
|
if (EqualityComparer<T>.Default.Equals(item, newItem)) return;
|
||||||
item = newItem;
|
item = newItem;
|
||||||
|
@ -11,6 +11,7 @@ using Directory = Alphaleonis.Win32.Filesystem.Directory;
|
|||||||
using DirectoryInfo = Alphaleonis.Win32.Filesystem.DirectoryInfo;
|
using DirectoryInfo = Alphaleonis.Win32.Filesystem.DirectoryInfo;
|
||||||
using File = Alphaleonis.Win32.Filesystem.File;
|
using File = Alphaleonis.Win32.Filesystem.File;
|
||||||
using Path = Alphaleonis.Win32.Filesystem.Path;
|
using Path = Alphaleonis.Win32.Filesystem.Path;
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
namespace Wabbajack.Lib
|
namespace Wabbajack.Lib
|
||||||
{
|
{
|
||||||
@ -26,7 +27,8 @@ namespace Wabbajack.Lib
|
|||||||
modList: modList,
|
modList: modList,
|
||||||
outputFolder: outputFolder,
|
outputFolder: outputFolder,
|
||||||
downloadFolder: downloadFolder,
|
downloadFolder: downloadFolder,
|
||||||
parameters: parameters)
|
parameters: parameters,
|
||||||
|
steps: 10)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
// TODO: only for testing
|
// TODO: only for testing
|
||||||
@ -51,7 +53,7 @@ namespace Wabbajack.Lib
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cancel.IsCancellationRequested) return false;
|
if (cancel.IsCancellationRequested) return false;
|
||||||
ConfigureProcessor(10, ConstructDynamicNumThreads(await RecommendQueueSize()));
|
Queue.SetActiveThreadsObservable(ConstructDynamicNumThreads(await RecommendQueueSize()));
|
||||||
DownloadFolder.CreateDirectory();
|
DownloadFolder.CreateDirectory();
|
||||||
|
|
||||||
if (cancel.IsCancellationRequested) return false;
|
if (cancel.IsCancellationRequested) return false;
|
||||||
@ -122,7 +124,7 @@ namespace Wabbajack.Lib
|
|||||||
|
|
||||||
var manualFilesDir = OutputFolder.Combine(Consts.ManualGameFilesDir);
|
var manualFilesDir = OutputFolder.Combine(Consts.ManualGameFilesDir);
|
||||||
|
|
||||||
var gameFolder = GameInfo.GameLocation();
|
var gameFolder = GameInfo.TryGetGameLocation();
|
||||||
|
|
||||||
Info($"Copying files from {manualFilesDir} " +
|
Info($"Copying files from {manualFilesDir} " +
|
||||||
$"to the game folder at {gameFolder}");
|
$"to the game folder at {gameFolder}");
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
<Platforms>x64</Platforms>
|
<Platforms>x64</Platforms>
|
||||||
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CefSharp.Common">
|
<PackageReference Include="CefSharp.Common">
|
||||||
|
@ -13,8 +13,8 @@ namespace Wabbajack.Lib.WebAutomation
|
|||||||
{
|
{
|
||||||
public class CefSharpWrapper : IWebDriver
|
public class CefSharpWrapper : IWebDriver
|
||||||
{
|
{
|
||||||
private IWebBrowser _browser;
|
private readonly IWebBrowser _browser;
|
||||||
public Action<Uri> DownloadHandler { get; set; }
|
public Action<Uri>? DownloadHandler { get; set; }
|
||||||
public CefSharpWrapper(IWebBrowser browser)
|
public CefSharpWrapper(IWebBrowser browser)
|
||||||
{
|
{
|
||||||
_browser = browser;
|
_browser = browser;
|
||||||
@ -24,7 +24,7 @@ namespace Wabbajack.Lib.WebAutomation
|
|||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<bool>();
|
var tcs = new TaskCompletionSource<bool>();
|
||||||
|
|
||||||
EventHandler<LoadingStateChangedEventArgs> handler = null;
|
EventHandler<LoadingStateChangedEventArgs>? handler = null;
|
||||||
handler = (sender, e) =>
|
handler = (sender, e) =>
|
||||||
{
|
{
|
||||||
if (!e.IsLoading)
|
if (!e.IsLoading)
|
||||||
@ -68,7 +68,7 @@ namespace Wabbajack.Lib.WebAutomation
|
|||||||
|
|
||||||
public class PopupBlocker : ILifeSpanHandler
|
public class PopupBlocker : ILifeSpanHandler
|
||||||
{
|
{
|
||||||
private CefSharpWrapper _wrapper;
|
private readonly CefSharpWrapper _wrapper;
|
||||||
|
|
||||||
public PopupBlocker(CefSharpWrapper cefSharpWrapper)
|
public PopupBlocker(CefSharpWrapper cefSharpWrapper)
|
||||||
{
|
{
|
||||||
@ -77,7 +77,7 @@ namespace Wabbajack.Lib.WebAutomation
|
|||||||
|
|
||||||
public bool OnBeforePopup(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl,
|
public bool OnBeforePopup(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl,
|
||||||
string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures,
|
string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures,
|
||||||
IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser)
|
IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser? newBrowser)
|
||||||
{
|
{
|
||||||
// Block popups
|
// Block popups
|
||||||
newBrowser = null;
|
newBrowser = null;
|
||||||
@ -86,7 +86,6 @@ namespace Wabbajack.Lib.WebAutomation
|
|||||||
|
|
||||||
public void OnAfterCreated(IWebBrowser chromiumWebBrowser, IBrowser browser)
|
public void OnAfterCreated(IWebBrowser chromiumWebBrowser, IBrowser browser)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DoClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
|
public bool DoClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
|
||||||
@ -96,7 +95,6 @@ namespace Wabbajack.Lib.WebAutomation
|
|||||||
|
|
||||||
public void OnBeforeClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
|
public void OnBeforeClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +110,7 @@ namespace Wabbajack.Lib.WebAutomation
|
|||||||
public void OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem,
|
public void OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem,
|
||||||
IBeforeDownloadCallback callback)
|
IBeforeDownloadCallback callback)
|
||||||
{
|
{
|
||||||
_wrapper.DownloadHandler(new Uri(downloadItem.Url));
|
_wrapper.DownloadHandler?.Invoke(new Uri(downloadItem.Url));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnDownloadUpdated(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem,
|
public void OnDownloadUpdated(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem,
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user