mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Merge remote-tracking branch 'wabbajack-tools/master' into async-exploration
This commit is contained in:
commit
86d4003398
@ -10,6 +10,7 @@ namespace Wabbajack.Common
|
||||
public static bool TestMode { get; set; } = false;
|
||||
|
||||
public static string GameFolderFilesDir = "Game Folder Files";
|
||||
public static string ManualGameFilesDir = "Manual Game Files";
|
||||
public static string LOOTFolderFilesDir = "LOOT Config Files";
|
||||
public static string BSACreationDir = "TEMP_BSA_FILES";
|
||||
|
||||
|
@ -67,19 +67,12 @@ namespace Wabbajack.Common
|
||||
public List<string> RequiredFiles { get; internal set; }
|
||||
public bool Disabled { get; internal set; }
|
||||
|
||||
public string GameLocation
|
||||
public string GameLocation(bool steam)
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Consts.TestMode)
|
||||
return Directory.GetCurrentDirectory();
|
||||
if (Consts.TestMode)
|
||||
return Directory.GetCurrentDirectory();
|
||||
|
||||
return (string) Registry.GetValue(GameLocationRegistryKey, "installed path", null)
|
||||
??
|
||||
(string) Registry.GetValue(
|
||||
GameLocationRegistryKey.Replace(@"HKEY_LOCAL_MACHINE\SOFTWARE\",
|
||||
@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\"), "installed path", null);
|
||||
}
|
||||
return steam ? SteamHandler.Instance.Games.FirstOrDefault(g => g.Game == Game)?.InstallDir : GOGHandler.Instance.Games.FirstOrDefault(g => g.Game == Game)?.Path;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ using ICSharpCode.SharpZipLib.BZip2;
|
||||
using IniParser;
|
||||
using Newtonsoft.Json;
|
||||
using ReactiveUI;
|
||||
using Syroot.Windows.IO;
|
||||
using Wabbajack.Common.StatusFeed;
|
||||
using Wabbajack.Common.StatusFeed.Errors;
|
||||
using YamlDotNet.Serialization;
|
||||
@ -923,5 +924,28 @@ namespace Wabbajack.Common
|
||||
}
|
||||
p.WaitForExit();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a file to JSON but in an encrypted format in the user's app local directory.
|
||||
/// The data will be encrypted so that it can only be read by this machine and this user.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="data"></param>
|
||||
public static void ToEcryptedJson<T>(this T data, string key)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(data.ToJSON());
|
||||
var encoded = ProtectedData.Protect(bytes, Encoding.UTF8.GetBytes(key), DataProtectionScope.LocalMachine);
|
||||
var path = Path.Combine(KnownFolders.LocalAppData.Path, "Wabbajack", key);
|
||||
File.WriteAllBytes(path, encoded);
|
||||
}
|
||||
|
||||
public static T FromEncryptedJson<T>(string key)
|
||||
{
|
||||
var path = Path.Combine(KnownFolders.LocalAppData.Path, "Wabbajack", key);
|
||||
var bytes = File.ReadAllBytes(path);
|
||||
var decoded = ProtectedData.Unprotect(bytes, Encoding.UTF8.GetBytes(key), DataProtectionScope.LocalMachine);
|
||||
return Encoding.UTF8.GetString(decoded).FromJSONString<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,12 +75,16 @@
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Syroot.KnownFolders">
|
||||
<HintPath>..\..\..\Users\tbald\.nuget\packages\syroot.windows.io.knownfolders\1.2.1\lib\net452\Syroot.KnownFolders.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Security" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
@ -169,6 +173,9 @@
|
||||
<PackageReference Include="ReactiveUI">
|
||||
<Version>10.5.30</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Syroot.Windows.IO.KnownFolders">
|
||||
<Version>1.2.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Data.HashFunction.xxHash">
|
||||
<Version>2.0.0</Version>
|
||||
</PackageReference>
|
||||
|
81
Wabbajack.Lib/LibCefHelpers/Init.cs
Normal file
81
Wabbajack.Lib/LibCefHelpers/Init.cs
Normal file
@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
using Wabbajack.Common;
|
||||
using Xilium.CefGlue;
|
||||
|
||||
namespace Wabbajack.Lib.LibCefHelpers
|
||||
{
|
||||
public static class Helpers
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// We bundle the cef libs inside the .exe, we need to extract them before loading any wpf code that requires them
|
||||
/// </summary>
|
||||
public static void ExtractLibs()
|
||||
{
|
||||
if (File.Exists("cefglue.7z") && File.Exists("libcef.dll")) return;
|
||||
|
||||
using (var fs = File.OpenWrite("cefglue.7z"))
|
||||
using (var rs = Assembly.GetExecutingAssembly().GetManifestResourceStream("Wabbajack.Lib.LibCefHelpers.cefglue.7z"))
|
||||
{
|
||||
rs.CopyTo(fs);
|
||||
Utils.Log("Extracting libCef files");
|
||||
}
|
||||
using (var wq = new WorkQueue(1))
|
||||
FileExtractor.ExtractAll(wq, "cefglue.7z", ".");
|
||||
|
||||
}
|
||||
public static async Task<Cookie[]> GetCookies(string domainEnding)
|
||||
{
|
||||
var manager = CefCookieManager.GetGlobal(null);
|
||||
var visitor = new CookieVisitor();
|
||||
if (!manager.VisitAllCookies(visitor))
|
||||
return new Cookie[0];
|
||||
var cc = await visitor.Task;
|
||||
|
||||
return (await visitor.Task).Where(c => c.Domain.EndsWith(domainEnding)).ToArray();
|
||||
}
|
||||
|
||||
private class CookieVisitor : CefCookieVisitor
|
||||
{
|
||||
TaskCompletionSource<List<Cookie>> _source = new TaskCompletionSource<List<Cookie>>();
|
||||
public Task<List<Cookie>> Task => _source.Task;
|
||||
|
||||
public List<Cookie> Cookies { get; } = new List<Cookie>();
|
||||
protected override bool Visit(CefCookie cookie, int count, int total, out bool delete)
|
||||
{
|
||||
Cookies.Add(new Cookie
|
||||
{
|
||||
Name = cookie.Name,
|
||||
Value = cookie.Value,
|
||||
Domain = cookie.Domain,
|
||||
Path = cookie.Path
|
||||
});
|
||||
if (count == total)
|
||||
_source.SetResult(Cookies);
|
||||
delete = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
_source.SetResult(Cookies);
|
||||
}
|
||||
}
|
||||
|
||||
public class Cookie
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Value { get; set; }
|
||||
public string Domain { get; set; }
|
||||
public string Path { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
BIN
Wabbajack.Lib/LibCefHelpers/cefglue.7z
Normal file
BIN
Wabbajack.Lib/LibCefHelpers/cefglue.7z
Normal file
Binary file not shown.
@ -44,7 +44,7 @@ namespace Wabbajack.Lib
|
||||
var game = GameRegistry.Games[ModList.GameType];
|
||||
|
||||
if (GameFolder == null)
|
||||
GameFolder = game.GameLocation;
|
||||
GameFolder = game.GameLocation(SteamHandler.Instance.Games.Any(g => g.Game == game.Game));
|
||||
|
||||
if (GameFolder == null)
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
using ReactiveUI;
|
||||
using ReactiveUI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
@ -12,10 +12,16 @@ using System.Security.Authentication;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Documents;
|
||||
using Syroot.Windows.IO;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib.Downloaders;
|
||||
using Wabbajack.Lib.LibCefHelpers;
|
||||
using WebSocketSharp;
|
||||
using Xilium.CefGlue;
|
||||
using Xilium.CefGlue.Common;
|
||||
using Xilium.CefGlue.Common.Handlers;
|
||||
using Xilium.CefGlue.WPF;
|
||||
using static Wabbajack.Lib.NexusApi.NexusApiUtils;
|
||||
using System.Threading;
|
||||
|
||||
@ -26,18 +32,13 @@ namespace Wabbajack.Lib.NexusApi
|
||||
private static readonly string API_KEY_CACHE_FILE = "nexus.key_cache";
|
||||
private static string _additionalEntropy = "vtP2HF6ezg";
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public HttpClient HttpClient => _httpClient;
|
||||
|
||||
public HttpClient HttpClient { get; } = new HttpClient();
|
||||
|
||||
#region Authentication
|
||||
|
||||
private readonly string _apiKey;
|
||||
public string ApiKey { get; }
|
||||
|
||||
public string ApiKey => _apiKey;
|
||||
|
||||
public bool IsAuthenticated => _apiKey != null;
|
||||
public bool IsAuthenticated => ApiKey != null;
|
||||
|
||||
private Task<UserStatus> _userStatus;
|
||||
|
||||
@ -71,25 +72,12 @@ namespace Wabbajack.Lib.NexusApi
|
||||
File.Delete(API_KEY_CACHE_FILE);
|
||||
}
|
||||
|
||||
var cacheFolder = Path.Combine(new KnownFolder(KnownFolderType.LocalAppData).Path, "Wabbajack");
|
||||
if (!Directory.Exists(cacheFolder))
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(cacheFolder);
|
||||
return Utils.FromEncryptedJson<string>("nexusapikey");
|
||||
}
|
||||
|
||||
var cacheFile = Path.Combine(cacheFolder, _additionalEntropy);
|
||||
if (File.Exists(cacheFile))
|
||||
catch (Exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Encoding.UTF8.GetString(
|
||||
ProtectedData.Unprotect(File.ReadAllBytes(cacheFile),
|
||||
Encoding.UTF8.GetBytes(_additionalEntropy), DataProtectionScope.CurrentUser));
|
||||
}
|
||||
catch (CryptographicException)
|
||||
{
|
||||
File.Delete(cacheFile);
|
||||
}
|
||||
}
|
||||
|
||||
var env_key = Environment.GetEnvironmentVariable("NEXUSAPIKEY");
|
||||
@ -98,29 +86,8 @@ namespace Wabbajack.Lib.NexusApi
|
||||
return env_key;
|
||||
}
|
||||
|
||||
// open a web socket to receive the api key
|
||||
var guid = Guid.NewGuid();
|
||||
var _websocket = new WebSocket("wss://sso.nexusmods.com")
|
||||
{
|
||||
SslConfiguration =
|
||||
{
|
||||
EnabledSslProtocols = SslProtocols.Tls12
|
||||
}
|
||||
};
|
||||
|
||||
var api_key = new TaskCompletionSource<string>();
|
||||
_websocket.OnMessage += (sender, msg) => { api_key.SetResult(msg.Data); };
|
||||
|
||||
_websocket.Connect();
|
||||
_websocket.Send("{\"id\": \"" + guid + "\", \"appid\": \"" + Consts.AppName + "\"}");
|
||||
|
||||
// open a web browser to get user permission
|
||||
Process.Start($"https://www.nexusmods.com/sso?id={guid}&application=" + Consts.AppName);
|
||||
|
||||
// get the api key from the socket and cache it
|
||||
var result = await api_key.Task;
|
||||
File.WriteAllBytes(cacheFile, ProtectedData.Protect(Encoding.UTF8.GetBytes(result),
|
||||
Encoding.UTF8.GetBytes(_additionalEntropy), DataProtectionScope.CurrentUser));
|
||||
var result = await Utils.Log(new RequestNexusAuthorization()).Task;
|
||||
result.ToEcryptedJson("nexusapikey");
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
@ -129,6 +96,60 @@ namespace Wabbajack.Lib.NexusApi
|
||||
}
|
||||
}
|
||||
|
||||
class RefererHandler : RequestHandler
|
||||
{
|
||||
private string _referer;
|
||||
|
||||
public RefererHandler(string referer)
|
||||
{
|
||||
_referer = referer;
|
||||
}
|
||||
protected override bool OnBeforeBrowse(CefBrowser browser, CefFrame frame, CefRequest request, bool userGesture, bool isRedirect)
|
||||
{
|
||||
base.OnBeforeBrowse(browser, frame, request, userGesture, isRedirect);
|
||||
if (request.ReferrerURL == null)
|
||||
request.SetReferrer(_referer, CefReferrerPolicy.Default);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<string> SetupNexusLogin(BaseCefBrowser browser, Action<string> updateStatus)
|
||||
{
|
||||
updateStatus("Please Log Into the Nexus");
|
||||
browser.Address = "https://users.nexusmods.com/auth/continue?client_id=nexus&redirect_uri=https://www.nexusmods.com/oauth/callback&response_type=code&referrer=//www.nexusmods.com";
|
||||
while (true)
|
||||
{
|
||||
var cookies = (await Helpers.GetCookies("nexusmods.com"));
|
||||
if (cookies.FirstOrDefault(c => c.Name == "member_id") != null)
|
||||
break;
|
||||
await Task.Delay(500);
|
||||
}
|
||||
|
||||
|
||||
// open a web socket to receive the api key
|
||||
var guid = Guid.NewGuid();
|
||||
var _websocket = new WebSocket("wss://sso.nexusmods.com")
|
||||
{
|
||||
SslConfiguration =
|
||||
{
|
||||
EnabledSslProtocols = SslProtocols.Tls12
|
||||
}
|
||||
};
|
||||
|
||||
updateStatus("Please Authorize Wabbajack to Download Mods");
|
||||
var api_key = new TaskCompletionSource<string>();
|
||||
_websocket.OnMessage += (sender, msg) => { api_key.SetResult(msg.Data); };
|
||||
|
||||
_websocket.Connect();
|
||||
_websocket.Send("{\"id\": \"" + guid + "\", \"appid\": \"" + Consts.AppName + "\"}");
|
||||
await Task.Delay(1000);
|
||||
|
||||
// open a web browser to get user permission
|
||||
browser.Address = $"https://www.nexusmods.com/sso?id={guid}&application={Consts.AppName}";
|
||||
|
||||
return await api_key.Task;
|
||||
}
|
||||
|
||||
public async Task<UserStatus> GetUserStatus()
|
||||
{
|
||||
var url = "https://api.nexusmods.com/v1/users/validate.json";
|
||||
@ -189,16 +210,14 @@ namespace Wabbajack.Lib.NexusApi
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
private NexusApiClient(string apiKey)
|
||||
private NexusApiClient(string apiKey = null)
|
||||
{
|
||||
_apiKey = apiKey;
|
||||
_httpClient = new HttpClient();
|
||||
ApiKey = apiKey;
|
||||
|
||||
// set default headers for all requests to the Nexus API
|
||||
var headers = _httpClient.DefaultRequestHeaders;
|
||||
var headers = HttpClient.DefaultRequestHeaders;
|
||||
headers.Add("User-Agent", Consts.UserAgent);
|
||||
headers.Add("apikey", _apiKey);
|
||||
headers.Add("apikey", ApiKey);
|
||||
headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
headers.Add("Application-Name", Consts.AppName);
|
||||
headers.Add("Application-Version", $"{Assembly.GetEntryAssembly()?.GetName()?.Version ?? new Version(0, 1)}");
|
||||
@ -215,8 +234,7 @@ namespace Wabbajack.Lib.NexusApi
|
||||
|
||||
private async Task<T> Get<T>(string url)
|
||||
{
|
||||
HttpResponseMessage response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
|
||||
|
||||
var response = await HttpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
|
||||
UpdateRemaining(response);
|
||||
|
||||
using (var stream = await response.Content.ReadAsStreamAsync())
|
||||
@ -327,7 +345,7 @@ namespace Wabbajack.Lib.NexusApi
|
||||
|
||||
var content = new FormUrlEncodedContent(new Dictionary<string, string> { { "version", mod.Version } });
|
||||
|
||||
using (var stream = await _httpClient.PostStream(url, content))
|
||||
using (var stream = await HttpClient.PostStream(url, content))
|
||||
{
|
||||
return stream.FromJSON<EndorsementResponse>();
|
||||
}
|
||||
@ -368,6 +386,7 @@ namespace Wabbajack.Lib.NexusApi
|
||||
set => _localCacheDir = value;
|
||||
}
|
||||
|
||||
|
||||
public async Task ClearUpdatedModsInCache()
|
||||
{
|
||||
if (!UseLocalCache) return;
|
||||
@ -393,7 +412,7 @@ namespace Wabbajack.Lib.NexusApi
|
||||
using (var queue = new WorkQueue())
|
||||
{
|
||||
var to_purge = (await Directory.EnumerateFiles(LocalCacheDir, "*.json")
|
||||
.PMap(queue,f =>
|
||||
.PMap(queue, f =>
|
||||
{
|
||||
Utils.Status("Cleaning Nexus cache for");
|
||||
var uri = new Uri(Encoding.UTF8.GetString(Path.GetFileNameWithoutExtension(f).FromHex()));
|
||||
@ -425,8 +444,6 @@ namespace Wabbajack.Lib.NexusApi
|
||||
File.Delete(f.f);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
27
Wabbajack.Lib/NexusApi/RequestNexusAuthorization.cs
Normal file
27
Wabbajack.Lib/NexusApi/RequestNexusAuthorization.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.Common.StatusFeed;
|
||||
|
||||
namespace Wabbajack.Lib.NexusApi
|
||||
{
|
||||
public class RequestNexusAuthorization : AStatusMessage, IUserIntervention
|
||||
{
|
||||
public override string ShortDescription => "Getting User's Nexus API Key";
|
||||
public override string ExtendedDescription { get; }
|
||||
|
||||
private readonly TaskCompletionSource<string> _source = new TaskCompletionSource<string>();
|
||||
public Task<string> Task => _source.Task;
|
||||
|
||||
public void Resume(string apikey)
|
||||
{
|
||||
_source.SetResult(apikey);
|
||||
}
|
||||
public void Cancel()
|
||||
{
|
||||
_source.SetCanceled();
|
||||
}
|
||||
}
|
||||
}
|
@ -190,7 +190,15 @@ namespace Wabbajack.Lib
|
||||
#endif
|
||||
|
||||
var replace = f;
|
||||
replace.Path = Path.Combine("Manual Game Files", element.FullPath.Substring(DownloadsFolder.Length + 1).Replace('|', '\\'));
|
||||
var name = replace.File.Name;
|
||||
var archiveName = targetArchive.Name;
|
||||
var elementPath = element.FullPath.Substring(element.FullPath.IndexOf('|')+1);
|
||||
var gameToFile = name.Substring(GamePath.Length + 1).Replace(elementPath, "");
|
||||
if (gameToFile.EndsWith("\\"))
|
||||
gameToFile = gameToFile.Substring(0, gameToFile.Length - 1);
|
||||
//replace.Path = replace.Path.Replace(Consts.GameFolderFilesDir, Consts.ManualGameFilesDir);
|
||||
replace.Path = Path.Combine(Consts.ManualGameFilesDir, archiveName, gameToFile, elementPath);
|
||||
//replace.Path = Path.Combine(Consts.ManualGameFilesDir, element.FullPath.Substring(DownloadsFolder.Length + 1).Replace('|', '\\'));
|
||||
AllFiles.RemoveAt(i);
|
||||
AllFiles.Insert(i, replace);
|
||||
//AllFiles.Replace(f, replace);
|
||||
@ -324,7 +332,7 @@ namespace Wabbajack.Lib
|
||||
.Where(File.Exists)
|
||||
.Select(async f =>
|
||||
{
|
||||
if (Path.GetExtension(f) != ".meta" && !File.Exists($"{f}.meta") && ActiveArchives.Contains(Path.GetFileNameWithoutExtension(f)))
|
||||
if (Path.GetExtension(f) != ".meta" && Path.GetExtension(f) != ".xxHash" && !File.Exists($"{f}.meta") && ActiveArchives.Contains(Path.GetFileNameWithoutExtension(f)))
|
||||
{
|
||||
Utils.Log($"Trying to create meta file for {Path.GetFileName(f)}");
|
||||
var metaString = "[General]\n" +
|
||||
|
@ -1,10 +1,13 @@
|
||||
using System.Diagnostics;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
using Wabbajack.Common;
|
||||
using Directory = Alphaleonis.Win32.Filesystem.Directory;
|
||||
using DirectoryInfo = Alphaleonis.Win32.Filesystem.DirectoryInfo;
|
||||
using File = Alphaleonis.Win32.Filesystem.File;
|
||||
using Path = Alphaleonis.Win32.Filesystem.Path;
|
||||
|
||||
@ -76,6 +79,9 @@ namespace Wabbajack.Lib
|
||||
if (cancel.IsCancellationRequested) return false;
|
||||
await InstallIncludedFiles();
|
||||
|
||||
if (cancel.IsCancellationRequested) return false;
|
||||
await InstallManualGameFiles();
|
||||
|
||||
if (cancel.IsCancellationRequested) return false;
|
||||
await InstallSteamWorkshopItems();
|
||||
//InstallIncludedDownloadMetas();
|
||||
@ -84,6 +90,57 @@ namespace Wabbajack.Lib
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task InstallManualGameFiles()
|
||||
{
|
||||
if (!ModList.Directives.Any(d => d.To.StartsWith(Consts.ManualGameFilesDir)))
|
||||
return;
|
||||
|
||||
var result = MessageBox.Show("Some mods from this ModList must be installed directly into " +
|
||||
"the game folder. Do you want to do this manually or do you want Wabbajack " +
|
||||
"to do this for you?", "Question", MessageBoxButton.YesNo);
|
||||
|
||||
if (result != MessageBoxResult.Yes)
|
||||
return;
|
||||
|
||||
var manualFilesDir = Path.Combine(OutputFolder, Consts.ManualGameFilesDir);
|
||||
|
||||
var gameFolder = GameInfo.GameLocation(SteamHandler.Instance.Games.Any(g => g.Game == GameInfo.Game));
|
||||
|
||||
Info($"Copying files from {manualFilesDir} " +
|
||||
$"to the game folder at {gameFolder}");
|
||||
|
||||
if (!Directory.Exists(manualFilesDir))
|
||||
{
|
||||
Info($"{manualFilesDir} does not exist!");
|
||||
return;
|
||||
}
|
||||
|
||||
await Directory.EnumerateDirectories(manualFilesDir).PMap(Queue, dir =>
|
||||
{
|
||||
var dirInfo = new DirectoryInfo(dir);
|
||||
dirInfo.GetDirectories("*", SearchOption.AllDirectories).Do(d =>
|
||||
{
|
||||
var destPath = d.FullName.Replace(dir, gameFolder);
|
||||
Status($"Creating directory {destPath}");
|
||||
Directory.CreateDirectory(destPath);
|
||||
});
|
||||
|
||||
dirInfo.GetFiles("*", SearchOption.AllDirectories).Do(f =>
|
||||
{
|
||||
var destPath = f.FullName.Replace(dir, gameFolder);
|
||||
Status($"Copying file {f.FullName} to {destPath}");
|
||||
try
|
||||
{
|
||||
File.Copy(f.FullName, destPath);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Info($"Could not copy file {f.FullName} to {destPath}. The file may already exist, skipping...");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async Task InstallSteamWorkshopItems()
|
||||
{
|
||||
//var currentLib = "";
|
||||
|
@ -117,6 +117,7 @@
|
||||
<Compile Include="CompilationSteps\PatchStockESMs.cs" />
|
||||
<Compile Include="CompilationSteps\Serialization.cs" />
|
||||
<Compile Include="Downloaders\SteamWorkshopDownloader.cs" />
|
||||
<Compile Include="LibCefHelpers\Init.cs" />
|
||||
<Compile Include="MO2Compiler.cs" />
|
||||
<Compile Include="Data.cs" />
|
||||
<Compile Include="Downloaders\AbstractDownloadState.cs" />
|
||||
@ -138,6 +139,7 @@
|
||||
<Compile Include="NexusApi\Dtos.cs" />
|
||||
<Compile Include="NexusApi\NexusApi.cs" />
|
||||
<Compile Include="NexusApi\NexusApiUtils.cs" />
|
||||
<Compile Include="NexusApi\RequestNexusAuthorization.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ReportBuilder.cs" />
|
||||
<Compile Include="StatusMessages\ConfirmUpdateOfExistingInstall.cs" />
|
||||
@ -156,6 +158,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<EmbeddedResource Include="LibCefHelpers\cefglue.7z" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(SolutionDir)\Compression.BSA\Compression.BSA.csproj">
|
||||
@ -181,6 +184,9 @@
|
||||
<PackageReference Include="AlphaFS">
|
||||
<Version>2.2.6</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="CefGlue.Wpf">
|
||||
<Version>75.1.28</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Ceras">
|
||||
<Version>4.1.7</Version>
|
||||
</PackageReference>
|
||||
|
@ -3,6 +3,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib;
|
||||
using Wabbajack.Lib.LibCefHelpers;
|
||||
|
||||
namespace Wabbajack.Test
|
||||
{
|
||||
@ -14,6 +15,7 @@ namespace Wabbajack.Test
|
||||
[TestInitialize]
|
||||
public void TestInitialize()
|
||||
{
|
||||
Helpers.ExtractLibs();
|
||||
Consts.TestMode = true;
|
||||
|
||||
utils = new TestUtils();
|
||||
|
@ -5,6 +5,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib;
|
||||
using Wabbajack.Lib.Downloaders;
|
||||
using Wabbajack.Lib.LibCefHelpers;
|
||||
using Wabbajack.Lib.NexusApi;
|
||||
using Wabbajack.Lib.Validation;
|
||||
using File = Alphaleonis.Win32.Filesystem.File;
|
||||
@ -17,6 +18,7 @@ namespace Wabbajack.Test
|
||||
[TestInitialize]
|
||||
public void Setup()
|
||||
{
|
||||
Helpers.ExtractLibs();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -141,6 +141,9 @@
|
||||
<PackageReference Include="AlphaFS">
|
||||
<Version>2.2.6</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="CefGlue.Wpf">
|
||||
<Version>75.1.28</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MSTest.TestAdapter">
|
||||
<Version>2.0.0</Version>
|
||||
</PackageReference>
|
||||
|
@ -4,6 +4,7 @@ using System.Reflection;
|
||||
using System.Windows;
|
||||
using MahApps.Metro;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib.LibCefHelpers;
|
||||
|
||||
namespace Wabbajack
|
||||
{
|
||||
@ -14,7 +15,7 @@ namespace Wabbajack
|
||||
{
|
||||
public App()
|
||||
{
|
||||
// Initialization in MainWindow ctor
|
||||
Helpers.ExtractLibs();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,10 +102,14 @@ namespace Wabbajack
|
||||
{
|
||||
if (e.Action == NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
_listBox.ScrollIntoView(e.NewItems[0]);
|
||||
_listBox.SelectedItem = e.NewItems[0];
|
||||
try
|
||||
{
|
||||
_listBox.ScrollIntoView(e.NewItems[0]);
|
||||
_listBox.SelectedItem = e.NewItems[0];
|
||||
}
|
||||
catch (ArgumentOutOfRangeException) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,10 +6,13 @@ using System;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Common.StatusFeed;
|
||||
using Wabbajack.Lib;
|
||||
using Wabbajack.Lib.NexusApi;
|
||||
using Wabbajack.Lib.StatusMessages;
|
||||
|
||||
namespace Wabbajack
|
||||
@ -33,15 +36,19 @@ namespace Wabbajack
|
||||
public readonly Lazy<InstallerVM> Installer;
|
||||
public readonly Lazy<ModListGalleryVM> Gallery;
|
||||
public readonly ModeSelectionVM ModeSelectionVM;
|
||||
public readonly WebBrowserVM WebBrowserVM;
|
||||
public Dispatcher ViewDispatcher { get; set; }
|
||||
|
||||
public MainWindowVM(MainWindow mainWindow, MainSettings settings)
|
||||
{
|
||||
MainWindow = mainWindow;
|
||||
ViewDispatcher = MainWindow.Dispatcher;
|
||||
Settings = settings;
|
||||
Installer = new Lazy<InstallerVM>(() => new InstallerVM(this));
|
||||
Compiler = new Lazy<CompilerVM>(() => new CompilerVM(this));
|
||||
Gallery = new Lazy<ModListGalleryVM>(() => new ModListGalleryVM(this));
|
||||
ModeSelectionVM = new ModeSelectionVM(this);
|
||||
WebBrowserVM = new WebBrowserVM();
|
||||
|
||||
// Set up logging
|
||||
Utils.LogMessages
|
||||
@ -59,6 +66,10 @@ namespace Wabbajack
|
||||
.OfType<ConfirmUpdateOfExistingInstall>()
|
||||
.Subscribe(msg => ConfirmUpdate(msg));
|
||||
|
||||
Utils.LogMessages
|
||||
.OfType<RequestNexusAuthorization>()
|
||||
.Subscribe(HandleRequestNexusAuthorization);
|
||||
|
||||
if (IsStartingFromModlist(out var path))
|
||||
{
|
||||
Installer.Value.ModListLocation.TargetPath = path;
|
||||
@ -71,6 +82,38 @@ namespace Wabbajack
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleRequestNexusAuthorization(RequestNexusAuthorization msg)
|
||||
{
|
||||
ViewDispatcher.InvokeAsync(async () =>
|
||||
{
|
||||
var oldPane = ActivePane;
|
||||
var vm = new WebBrowserVM();
|
||||
ActivePane = vm;
|
||||
try
|
||||
{
|
||||
vm.BackCommand = ReactiveCommand.Create(() =>
|
||||
{
|
||||
ActivePane = oldPane;
|
||||
msg.Cancel();
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{ }
|
||||
|
||||
try
|
||||
{
|
||||
var key = await NexusApiClient.SetupNexusLogin(vm.Browser, m => vm.Instructions = m);
|
||||
msg.Resume(key);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
msg.Cancel();
|
||||
}
|
||||
ActivePane = oldPane;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void ConfirmUpdate(ConfirmUpdateOfExistingInstall msg)
|
||||
{
|
||||
var result = MessageBox.Show(msg.ExtendedDescription, msg.ShortDescription, MessageBoxButton.OKCancel);
|
||||
|
30
Wabbajack/View Models/WebBrowserVM.cs
Normal file
30
Wabbajack/View Models/WebBrowserVM.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Wabbajack.Lib;
|
||||
using Xilium.CefGlue.Common;
|
||||
using Xilium.CefGlue.WPF;
|
||||
|
||||
namespace Wabbajack
|
||||
{
|
||||
public class WebBrowserVM : ViewModel
|
||||
{
|
||||
[Reactive]
|
||||
public string Instructions { get; set; }
|
||||
|
||||
public WpfCefBrowser Browser { get; }
|
||||
|
||||
[Reactive]
|
||||
public IReactiveCommand BackCommand { get; set; }
|
||||
public WebBrowserVM(string url = "http://www.wabbajack.org")
|
||||
{
|
||||
Browser = new WpfCefBrowser();
|
||||
Browser.Address = url;
|
||||
Instructions = "Wabbajack Web Browser";
|
||||
}
|
||||
}
|
||||
}
|
@ -33,6 +33,9 @@
|
||||
<DataTemplate DataType="{x:Type local:ModListGalleryVM}">
|
||||
<local:ModListGalleryView />
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type local:WebBrowserVM}">
|
||||
<local:WebBrowserView />
|
||||
</DataTemplate>
|
||||
</ContentPresenter.Resources>
|
||||
</ContentPresenter>
|
||||
</mahapps:MetroWindow>
|
||||
|
71
Wabbajack/Views/WebBrowserView.xaml
Normal file
71
Wabbajack/Views/WebBrowserView.xaml
Normal file
@ -0,0 +1,71 @@
|
||||
<UserControl x:Class="Wabbajack.WebBrowserView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
|
||||
xmlns:wabbajack="clr-namespace:Wabbajack"
|
||||
xmlns:wpf="clr-namespace:Xilium.CefGlue.WPF;assembly=Xilium.CefGlue.WPF"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<Color x:Key="TextBackgroundFill">#92000000</Color>
|
||||
<SolidColorBrush x:Key="TextBackgroundFillBrush" Color="{StaticResource TextBackgroundFill}" />
|
||||
<Color x:Key="TextBackgroundHoverFill">#DF000000</Color>
|
||||
<Style x:Key="BackgroundBlurStyle" TargetType="TextBlock">
|
||||
<Setter Property="Background" Value="{StaticResource TextBackgroundFillBrush}" />
|
||||
<Setter Property="Foreground" Value="Transparent" />
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type Button}}}" Value="True">
|
||||
<DataTrigger.EnterActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<ColorAnimation
|
||||
Storyboard.TargetProperty="(TextBlock.Background).(SolidColorBrush.Color)"
|
||||
To="{StaticResource TextBackgroundHoverFill}"
|
||||
Duration="0:0:0.06" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.EnterActions>
|
||||
<DataTrigger.ExitActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<ColorAnimation
|
||||
Storyboard.TargetProperty="(TextBlock.Background).(SolidColorBrush.Color)"
|
||||
To="{StaticResource TextBackgroundFill}"
|
||||
Duration="0:0:0.06" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.ExitActions>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="47" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<wabbajack:TopProgressView
|
||||
Title="{Binding Instructions}"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
ShadowMargin="False" />
|
||||
<Button
|
||||
x:Name="BackButton"
|
||||
Grid.Row="0"
|
||||
Width="30"
|
||||
Height="30"
|
||||
Margin="7,5,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Command="{Binding BackCommand}"
|
||||
Style="{StaticResource IconCircleButtonStyle}"
|
||||
ToolTip="Back to main menu">
|
||||
<iconPacks:PackIconMaterial Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}" Kind="ArrowLeft" />
|
||||
</Button>
|
||||
<!-- Do it this way so we can access the browser directly from the VM -->
|
||||
<ContentControl Grid.Row="1" Content="{Binding Browser}"></ContentControl>
|
||||
</Grid>
|
||||
</UserControl>
|
28
Wabbajack/Views/WebBrowserView.xaml.cs
Normal file
28
Wabbajack/Views/WebBrowserView.xaml.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Wabbajack
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for WebBrowser.xaml
|
||||
/// </summary>
|
||||
public partial class WebBrowserView : UserControl
|
||||
{
|
||||
public WebBrowserView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@ -162,6 +162,10 @@
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="Xilium.CefGlue.WPF, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\Users\tbald\.nuget\packages\cefglue.wpf\75.1.28\lib\net472\x64\Xilium.CefGlue.WPF.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml">
|
||||
@ -169,6 +173,7 @@
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Compile Include="Converters\EqualsToBoolConverter.cs" />
|
||||
<Compile Include="View Models\WebBrowserVM.cs" />
|
||||
<Compile Include="Views\Installers\MO2InstallerConfigView.xaml.cs">
|
||||
<DependentUpon>MO2InstallerConfigView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
@ -247,6 +252,9 @@
|
||||
<Compile Include="Views\Installers\VortexInstallerConfigView.xaml.cs">
|
||||
<DependentUpon>VortexInstallerConfigView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\WebBrowserView.xaml.cs">
|
||||
<DependentUpon>WebBrowserView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Page Include="Views\Installers\MO2InstallerConfigView.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
@ -333,6 +341,10 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\WebBrowserView.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
@ -411,6 +423,9 @@
|
||||
<PackageReference Include="AlphaFS">
|
||||
<Version>2.2.6</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="CefGlue.Wpf">
|
||||
<Version>75.1.28</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="CommonMark.NET">
|
||||
<Version>0.15.1</Version>
|
||||
</PackageReference>
|
||||
|
Loading…
Reference in New Issue
Block a user