mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Merge branch 'master' into async-exploration
This commit is contained in:
commit
bcd7c95caf
BIN
Branding/PNGs/Wabba_Mouth_Small.png
Normal file
BIN
Branding/PNGs/Wabba_Mouth_Small.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 58 KiB |
@ -71,7 +71,7 @@ namespace Compression.BSA.Test
|
|||||||
var state = new NexusDownloader.State
|
var state = new NexusDownloader.State
|
||||||
{
|
{
|
||||||
ModID = info.Item2.ToString(),
|
ModID = info.Item2.ToString(),
|
||||||
GameName = GameRegistry.Games[info.Item1].NexusName,
|
GameName = info.Item1.MetaData().NexusName,
|
||||||
FileID = file.file_id.ToString()
|
FileID = file.file_id.ToString()
|
||||||
};
|
};
|
||||||
await state.Download(src);
|
await state.Download(src);
|
||||||
|
@ -96,7 +96,7 @@
|
|||||||
<Version>2.0.0</Version>
|
<Version>2.0.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="System.Reactive">
|
<PackageReference Include="System.Reactive">
|
||||||
<Version>4.2.0</Version>
|
<Version>4.3.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
|
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
|
||||||
|
@ -60,10 +60,6 @@
|
|||||||
<Prefer32Bit>true</Prefer32Bit>
|
<Prefer32Bit>true</Prefer32Bit>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="ReactiveUI, Version=10.5.0.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<SpecificVersion>False</SpecificVersion>
|
|
||||||
<HintPath>..\..\..\Users\tbald\.nuget\packages\reactiveui\10.5.7\lib\net461\ReactiveUI.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Windows" />
|
<Reference Include="System.Windows" />
|
||||||
@ -103,8 +99,11 @@
|
|||||||
<PackageReference Include="Newtonsoft.Json">
|
<PackageReference Include="Newtonsoft.Json">
|
||||||
<Version>12.0.3</Version>
|
<Version>12.0.3</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
<PackageReference Include="ReactiveUI">
|
||||||
|
<Version>11.0.1</Version>
|
||||||
|
</PackageReference>
|
||||||
<PackageReference Include="System.Reactive">
|
<PackageReference Include="System.Reactive">
|
||||||
<Version>4.2.0</Version>
|
<Version>4.3.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
@ -103,10 +103,10 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="System.Reactive">
|
<PackageReference Include="System.Reactive">
|
||||||
<Version>4.2.0</Version>
|
<Version>4.3.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe">
|
<PackageReference Include="System.Runtime.CompilerServices.Unsafe">
|
||||||
<Version>4.6.0</Version>
|
<Version>4.7.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
@ -677,4 +677,4 @@ namespace Wabbajack.Common
|
|||||||
return a < b ? b < c ? b : a < c ? c : a : b > c ? b : a > c ? c : a;
|
return a < b ? b < c ? b : a < c ? c : a : b > c ? b : a > c ? c : a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using Alphaleonis.Win32.Filesystem;
|
||||||
|
using Syroot.Windows.IO;
|
||||||
|
|
||||||
namespace Wabbajack.Common
|
namespace Wabbajack.Common
|
||||||
{
|
{
|
||||||
@ -84,6 +86,7 @@ namespace Wabbajack.Common
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static string HashFileExtension => ".xxHash";
|
public static string HashFileExtension => ".xxHash";
|
||||||
|
public static string LocalAppDataPath => Path.Combine(KnownFolders.LocalAppData.Path, "Wabbajack");
|
||||||
|
|
||||||
public static string WabbajackCacheLocation = "http://build.wabbajack.org/nexus_api_cache/";
|
public static string WabbajackCacheLocation = "http://build.wabbajack.org/nexus_api_cache/";
|
||||||
}
|
}
|
||||||
|
@ -67,12 +67,13 @@ namespace Wabbajack.Common
|
|||||||
public List<string> RequiredFiles { get; internal set; }
|
public List<string> RequiredFiles { get; internal set; }
|
||||||
public bool Disabled { get; internal set; }
|
public bool Disabled { get; internal set; }
|
||||||
|
|
||||||
public string GameLocation(bool steam)
|
public string GameLocation()
|
||||||
{
|
{
|
||||||
if (Consts.TestMode)
|
if (Consts.TestMode)
|
||||||
return Directory.GetCurrentDirectory();
|
return Directory.GetCurrentDirectory();
|
||||||
|
|
||||||
return steam ? SteamHandler.Instance.Games.FirstOrDefault(g => g.Game == Game)?.InstallDir : GOGHandler.Instance.Games.FirstOrDefault(g => g.Game == Game)?.Path;
|
return SteamHandler.Instance.Games.FirstOrDefault(g => g.Game == Game)?.InstallDir ??
|
||||||
|
GOGHandler.Instance.Games.FirstOrDefault(g => g.Game == Game)?.Path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Wabbajack.Common.StatusFeed
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Defines a message that requires user interaction. The user must perform some action
|
|
||||||
/// or make a choice.
|
|
||||||
/// </summary>
|
|
||||||
public interface IUserIntervention : IStatusMessage
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The user didn't make a choice, so this action should be aborted
|
|
||||||
/// </summary>
|
|
||||||
void Cancel();
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace Wabbajack.Common
|
||||||
|
{
|
||||||
|
public abstract class AUserIntervention : ReactiveObject, IUserIntervention
|
||||||
|
{
|
||||||
|
public DateTime Timestamp { get; } = DateTime.Now;
|
||||||
|
public abstract string ShortDescription { get; }
|
||||||
|
public abstract string ExtendedDescription { get; }
|
||||||
|
|
||||||
|
private bool _handled;
|
||||||
|
public bool Handled { get => _handled; set => this.RaiseAndSetIfChanged(ref _handled, value); }
|
||||||
|
|
||||||
|
public int CpuID { get; } = WorkQueue.AsyncLocalCurrentQueue.Value?.CpuId ?? WorkQueue.UnassignedCpuId;
|
||||||
|
|
||||||
|
public abstract void Cancel();
|
||||||
|
public ICommand CancelCommand { get; }
|
||||||
|
|
||||||
|
public AUserIntervention()
|
||||||
|
{
|
||||||
|
CancelCommand = ReactiveCommand.Create(() => Cancel());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace Wabbajack.Common
|
||||||
|
{
|
||||||
|
public abstract class ConfirmationIntervention : AUserIntervention
|
||||||
|
{
|
||||||
|
public enum Choice
|
||||||
|
{
|
||||||
|
Continue,
|
||||||
|
Abort
|
||||||
|
}
|
||||||
|
|
||||||
|
private TaskCompletionSource<Choice> _source = new TaskCompletionSource<Choice>();
|
||||||
|
public Task<Choice> Task => _source.Task;
|
||||||
|
|
||||||
|
public ICommand ConfirmCommand { get; }
|
||||||
|
|
||||||
|
public ConfirmationIntervention()
|
||||||
|
{
|
||||||
|
ConfirmCommand = ReactiveCommand.Create(() => Confirm());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Cancel()
|
||||||
|
{
|
||||||
|
Handled = true;
|
||||||
|
_source.SetResult(Choice.Abort);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Confirm()
|
||||||
|
{
|
||||||
|
Handled = true;
|
||||||
|
_source.SetResult(Choice.Continue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using ReactiveUI;
|
||||||
|
using Wabbajack.Common.StatusFeed;
|
||||||
|
|
||||||
|
namespace Wabbajack.Common
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines a message that requires user interaction. The user must perform some action
|
||||||
|
/// or make a choice.
|
||||||
|
/// </summary>
|
||||||
|
public interface IUserIntervention : IStatusMessage, IReactiveObject
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The user didn't make a choice, so this action should be aborted
|
||||||
|
/// </summary>
|
||||||
|
void Cancel();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the interaction has been handled and no longer needs attention
|
||||||
|
/// Note: This needs to be Reactive so that users can monitor its status
|
||||||
|
/// </summary>
|
||||||
|
bool Handled { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// WorkQueue job ID that is blocking on this intervention
|
||||||
|
/// </summary>
|
||||||
|
int CpuID { get; }
|
||||||
|
}
|
||||||
|
}
|
@ -95,6 +95,9 @@ namespace Wabbajack.Common
|
|||||||
paths.Add(s);
|
paths.Add(s);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Default path in the Steam folder isn't in the configs
|
||||||
|
paths.Add(Path.Combine(SteamPath, "steamapps"));
|
||||||
|
|
||||||
InstallFolders = paths;
|
InstallFolders = paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -688,6 +688,7 @@ namespace Wabbajack.Common
|
|||||||
|
|
||||||
using (var f = File.OpenWrite(tmpName))
|
using (var f = File.OpenWrite(tmpName))
|
||||||
{
|
{
|
||||||
|
Status("Creating Patch");
|
||||||
BSDiff.Create(a, b, f);
|
BSDiff.Create(a, b, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -936,7 +937,11 @@ namespace Wabbajack.Common
|
|||||||
{
|
{
|
||||||
var bytes = Encoding.UTF8.GetBytes(data.ToJSON());
|
var bytes = Encoding.UTF8.GetBytes(data.ToJSON());
|
||||||
var encoded = ProtectedData.Protect(bytes, Encoding.UTF8.GetBytes(key), DataProtectionScope.LocalMachine);
|
var encoded = ProtectedData.Protect(bytes, Encoding.UTF8.GetBytes(key), DataProtectionScope.LocalMachine);
|
||||||
var path = Path.Combine(KnownFolders.LocalAppData.Path, "Wabbajack", key);
|
|
||||||
|
if (!Directory.Exists(Consts.LocalAppDataPath))
|
||||||
|
Directory.CreateDirectory(Consts.LocalAppDataPath);
|
||||||
|
|
||||||
|
var path = Path.Combine(Consts.LocalAppDataPath, key);
|
||||||
File.WriteAllBytes(path, encoded);
|
File.WriteAllBytes(path, encoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -947,5 +952,12 @@ namespace Wabbajack.Common
|
|||||||
var decoded = ProtectedData.Unprotect(bytes, Encoding.UTF8.GetBytes(key), DataProtectionScope.LocalMachine);
|
var decoded = ProtectedData.Unprotect(bytes, Encoding.UTF8.GetBytes(key), DataProtectionScope.LocalMachine);
|
||||||
return Encoding.UTF8.GetString(decoded).FromJSONString<T>();
|
return Encoding.UTF8.GetString(decoded).FromJSONString<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsInPath(this string path, string parent)
|
||||||
|
{
|
||||||
|
return path.ToLower().TrimEnd('\\').StartsWith(parent.ToLower().TrimEnd('\\') + "\\");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,6 +114,7 @@
|
|||||||
<Compile Include="SplittingStream.cs" />
|
<Compile Include="SplittingStream.cs" />
|
||||||
<Compile Include="StatusFeed\AErrorMessage.cs" />
|
<Compile Include="StatusFeed\AErrorMessage.cs" />
|
||||||
<Compile Include="StatusFeed\AStatusMessage.cs" />
|
<Compile Include="StatusFeed\AStatusMessage.cs" />
|
||||||
|
<Compile Include="StatusFeed\Interventions\AUserIntervention.cs" />
|
||||||
<Compile Include="StatusFeed\Errors\7zipReturnError.cs" />
|
<Compile Include="StatusFeed\Errors\7zipReturnError.cs" />
|
||||||
<Compile Include="StatusFeed\Errors\FileExtractionError.cs" />
|
<Compile Include="StatusFeed\Errors\FileExtractionError.cs" />
|
||||||
<Compile Include="StatusFeed\Errors\GenericException.cs" />
|
<Compile Include="StatusFeed\Errors\GenericException.cs" />
|
||||||
@ -122,8 +123,9 @@
|
|||||||
<Compile Include="StatusFeed\IError.cs" />
|
<Compile Include="StatusFeed\IError.cs" />
|
||||||
<Compile Include="StatusFeed\IException.cs" />
|
<Compile Include="StatusFeed\IException.cs" />
|
||||||
<Compile Include="StatusFeed\IInfo.cs" />
|
<Compile Include="StatusFeed\IInfo.cs" />
|
||||||
|
<Compile Include="StatusFeed\Interventions\ConfirmationIntervention.cs" />
|
||||||
<Compile Include="StatusFeed\IStatusMessage.cs" />
|
<Compile Include="StatusFeed\IStatusMessage.cs" />
|
||||||
<Compile Include="StatusFeed\IUserIntervention.cs" />
|
<Compile Include="StatusFeed\Interventions\IUserIntervention.cs" />
|
||||||
<Compile Include="StatusFileStream.cs" />
|
<Compile Include="StatusFileStream.cs" />
|
||||||
<Compile Include="StatusUpdate.cs" />
|
<Compile Include="StatusUpdate.cs" />
|
||||||
<Compile Include="SteamHandler.cs" />
|
<Compile Include="SteamHandler.cs" />
|
||||||
@ -171,7 +173,7 @@
|
|||||||
<Version>1.0.2</Version>
|
<Version>1.0.2</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="ReactiveUI">
|
<PackageReference Include="ReactiveUI">
|
||||||
<Version>10.5.30</Version>
|
<Version>11.0.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Syroot.Windows.IO.KnownFolders">
|
<PackageReference Include="Syroot.Windows.IO.KnownFolders">
|
||||||
<Version>1.2.1</Version>
|
<Version>1.2.1</Version>
|
||||||
@ -180,7 +182,7 @@
|
|||||||
<Version>2.0.0</Version>
|
<Version>2.0.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="System.Reactive">
|
<PackageReference Include="System.Reactive">
|
||||||
<Version>4.2.0</Version>
|
<Version>4.3.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="YamlDotNet">
|
<PackageReference Include="YamlDotNet">
|
||||||
<Version>8.0.0</Version>
|
<Version>8.0.0</Version>
|
||||||
|
@ -15,7 +15,10 @@ namespace Wabbajack.Common
|
|||||||
internal BlockingCollection<Func<Task>>
|
internal BlockingCollection<Func<Task>>
|
||||||
Queue = new BlockingCollection<Func<Task>>(new ConcurrentStack<Func<Task>>());
|
Queue = new BlockingCollection<Func<Task>>(new ConcurrentStack<Func<Task>>());
|
||||||
|
|
||||||
private static readonly AsyncLocal<int> CpuId = new AsyncLocal<int>();
|
public const int UnassignedCpuId = 0;
|
||||||
|
|
||||||
|
private static readonly AsyncLocal<int> _cpuId = new AsyncLocal<int>();
|
||||||
|
public int CpuId => _cpuId.Value;
|
||||||
|
|
||||||
internal static bool WorkerThread => ThreadLocalCurrentQueue.Value != null;
|
internal static bool WorkerThread => ThreadLocalCurrentQueue.Value != null;
|
||||||
internal static readonly ThreadLocal<WorkQueue> ThreadLocalCurrentQueue = new ThreadLocal<WorkQueue>();
|
internal static readonly ThreadLocal<WorkQueue> ThreadLocalCurrentQueue = new ThreadLocal<WorkQueue>();
|
||||||
@ -28,6 +31,11 @@ namespace Wabbajack.Common
|
|||||||
|
|
||||||
private CancellationTokenSource _cancel = new CancellationTokenSource();
|
private CancellationTokenSource _cancel = new CancellationTokenSource();
|
||||||
|
|
||||||
|
// This is currently a lie, as it wires to the Utils singleton stream This is still good to have,
|
||||||
|
// so that logic related to a single WorkQueue can subscribe to this dummy member so that If/when we
|
||||||
|
// implement log messages in a non-singleton fashion, they will already be wired up properly.
|
||||||
|
public IObservable<IStatusMessage> LogMessages => Utils.LogMessages;
|
||||||
|
|
||||||
public WorkQueue(int threadCount = 0)
|
public WorkQueue(int threadCount = 0)
|
||||||
{
|
{
|
||||||
StartThreads(threadCount == 0 ? Environment.ProcessorCount : threadCount);
|
StartThreads(threadCount == 0 ? Environment.ProcessorCount : threadCount);
|
||||||
@ -36,7 +44,7 @@ namespace Wabbajack.Common
|
|||||||
private void StartThreads(int threadCount)
|
private void StartThreads(int threadCount)
|
||||||
{
|
{
|
||||||
ThreadCount = threadCount;
|
ThreadCount = threadCount;
|
||||||
Threads = Enumerable.Range(0, threadCount)
|
Threads = Enumerable.Range(1, threadCount)
|
||||||
.Select(idx =>
|
.Select(idx =>
|
||||||
{
|
{
|
||||||
var thread = new Thread(() => ThreadBody(idx).Wait());
|
var thread = new Thread(() => ThreadBody(idx).Wait());
|
||||||
@ -52,7 +60,7 @@ namespace Wabbajack.Common
|
|||||||
|
|
||||||
private async Task ThreadBody(int idx)
|
private async Task ThreadBody(int idx)
|
||||||
{
|
{
|
||||||
CpuId.Value = idx;
|
_cpuId.Value = idx;
|
||||||
ThreadLocalCurrentQueue.Value = this;
|
ThreadLocalCurrentQueue.Value = this;
|
||||||
AsyncLocalCurrentQueue.Value = this;
|
AsyncLocalCurrentQueue.Value = this;
|
||||||
|
|
||||||
@ -79,7 +87,7 @@ namespace Wabbajack.Common
|
|||||||
Progress = progress,
|
Progress = progress,
|
||||||
ProgressPercent = progress / 100f,
|
ProgressPercent = progress / 100f,
|
||||||
Msg = msg,
|
Msg = msg,
|
||||||
ID = CpuId.Value,
|
ID = _cpuId.Value,
|
||||||
IsWorking = isWorking
|
IsWorking = isWorking
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Reactive.Disposables;
|
||||||
using System.Reactive.Subjects;
|
using System.Reactive.Subjects;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Wabbajack.Common;
|
using Wabbajack.Common;
|
||||||
|
using Wabbajack.Common.StatusFeed;
|
||||||
using Wabbajack.VirtualFileSystem;
|
using Wabbajack.VirtualFileSystem;
|
||||||
|
|
||||||
namespace Wabbajack.Lib
|
namespace Wabbajack.Lib
|
||||||
@ -33,6 +35,9 @@ namespace Wabbajack.Lib
|
|||||||
private Subject<CPUStatus> _queueStatus { get; } = new Subject<CPUStatus>();
|
private Subject<CPUStatus> _queueStatus { get; } = new Subject<CPUStatus>();
|
||||||
public IObservable<CPUStatus> QueueStatus => _queueStatus;
|
public IObservable<CPUStatus> QueueStatus => _queueStatus;
|
||||||
|
|
||||||
|
private Subject<IStatusMessage> _logMessages { get; } = new Subject<IStatusMessage>();
|
||||||
|
public IObservable<IStatusMessage> LogMessages => _logMessages;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@ -40,6 +45,8 @@ namespace Wabbajack.Lib
|
|||||||
private int _started;
|
private int _started;
|
||||||
private readonly CancellationTokenSource _cancel = new CancellationTokenSource();
|
private readonly CancellationTokenSource _cancel = new CancellationTokenSource();
|
||||||
|
|
||||||
|
private readonly CompositeDisposable _subs = new CompositeDisposable();
|
||||||
|
|
||||||
protected void ConfigureProcessor(int steps, int threads = 0)
|
protected void ConfigureProcessor(int steps, int threads = 0)
|
||||||
{
|
{
|
||||||
if (1 == Interlocked.CompareExchange(ref _configured, 1, 1))
|
if (1 == Interlocked.CompareExchange(ref _configured, 1, 1))
|
||||||
@ -48,7 +55,10 @@ namespace Wabbajack.Lib
|
|||||||
}
|
}
|
||||||
Queue = new WorkQueue(threads);
|
Queue = new WorkQueue(threads);
|
||||||
UpdateTracker = new StatusUpdateTracker(steps);
|
UpdateTracker = new StatusUpdateTracker(steps);
|
||||||
Queue.Status.Subscribe(_queueStatus);
|
Queue.Status.Subscribe(_queueStatus)
|
||||||
|
.DisposeWith(_subs);
|
||||||
|
Queue.LogMessages.Subscribe(_logMessages)
|
||||||
|
.DisposeWith(_subs);
|
||||||
UpdateTracker.Progress.Subscribe(_percentCompleted);
|
UpdateTracker.Progress.Subscribe(_percentCompleted);
|
||||||
UpdateTracker.StepName.Subscribe(_textStatus);
|
UpdateTracker.StepName.Subscribe(_textStatus);
|
||||||
VFS = new Context(Queue) { UpdateTracker = UpdateTracker };
|
VFS = new Context(Queue) { UpdateTracker = UpdateTracker };
|
||||||
|
@ -209,35 +209,41 @@ namespace Wabbajack.Lib
|
|||||||
{
|
{
|
||||||
if (archives.TryGetValue(sha, out var found))
|
if (archives.TryGetValue(sha, out var found))
|
||||||
{
|
{
|
||||||
if (found.IniData == null)
|
return await ResolveArchive(found);
|
||||||
Error($"No download metadata found for {found.Name}, please use MO2 to query info or add a .meta file and try again.");
|
|
||||||
|
|
||||||
var result = new Archive
|
|
||||||
{
|
|
||||||
State = await DownloadDispatcher.ResolveArchive(found.IniData)
|
|
||||||
};
|
|
||||||
|
|
||||||
if (result.State == null)
|
|
||||||
Error($"{found.Name} could not be handled by any of the downloaders");
|
|
||||||
|
|
||||||
result.Name = found.Name;
|
|
||||||
result.Hash = found.File.Hash;
|
|
||||||
result.Meta = found.Meta;
|
|
||||||
result.Size = found.File.Size;
|
|
||||||
|
|
||||||
Info($"Checking link for {found.Name}");
|
|
||||||
|
|
||||||
if (result.State != null && !await result.State.Verify())
|
|
||||||
Error(
|
|
||||||
$"Unable to resolve link for {found.Name}. If this is hosted on the Nexus the file may have been removed.");
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Error($"No match found for Archive sha: {sha} this shouldn't happen");
|
Error($"No match found for Archive sha: {sha} this shouldn't happen");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Archive> ResolveArchive(IndexedArchive archive)
|
||||||
|
{
|
||||||
|
if (archive.IniData == null)
|
||||||
|
Error(
|
||||||
|
$"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
|
||||||
|
{
|
||||||
|
State = (AbstractDownloadState) DownloadDispatcher.ResolveArchive(archive.IniData)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (result.State == null)
|
||||||
|
Error($"{archive.Name} could not be handled by any of the downloaders");
|
||||||
|
|
||||||
|
result.Name = archive.Name;
|
||||||
|
result.Hash = archive.File.Hash;
|
||||||
|
result.Meta = archive.Meta;
|
||||||
|
result.Size = archive.File.Size;
|
||||||
|
|
||||||
|
Info($"Checking link for {archive.Name}");
|
||||||
|
|
||||||
|
if (result.State != null && !await result.State.Verify())
|
||||||
|
Error(
|
||||||
|
$"Unable to resolve link for {archive.Name}. If this is hosted on the Nexus the file may have been removed.");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<Directive> RunStack(IEnumerable<ICompilationStep> stack, RawSourceFile source)
|
public async Task<Directive> RunStack(IEnumerable<ICompilationStep> stack, RawSourceFile source)
|
||||||
{
|
{
|
||||||
Utils.Status($"Compiling {source.Path}");
|
Utils.Status($"Compiling {source.Path}");
|
||||||
|
@ -349,10 +349,8 @@ namespace Wabbajack.Lib
|
|||||||
{
|
{
|
||||||
var relative_to = f.RelativeTo(OutputFolder);
|
var relative_to = f.RelativeTo(OutputFolder);
|
||||||
Utils.Status($"Checking if modlist file {relative_to}");
|
Utils.Status($"Checking if modlist file {relative_to}");
|
||||||
if (indexed.ContainsKey(relative_to) || f.StartsWith(DownloadFolder + Path.DirectorySeparator))
|
if (indexed.ContainsKey(relative_to) || f.IsInPath(DownloadFolder))
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
Utils.Log($"Deleting {relative_to} it's not part of this modlist");
|
Utils.Log($"Deleting {relative_to} it's not part of this modlist");
|
||||||
File.Delete(f);
|
File.Delete(f);
|
||||||
|
@ -23,7 +23,8 @@ namespace Wabbajack.Lib
|
|||||||
typeof(MegaDownloader.State), typeof(ModDBDownloader.State), typeof(NexusDownloader.State),
|
typeof(MegaDownloader.State), typeof(ModDBDownloader.State), typeof(NexusDownloader.State),
|
||||||
typeof(BSAStateObject), typeof(BSAFileStateObject), typeof(BA2StateObject), typeof(BA2DX10EntryState),
|
typeof(BSAStateObject), typeof(BSAFileStateObject), typeof(BA2StateObject), typeof(BA2DX10EntryState),
|
||||||
typeof(BA2FileEntryState), typeof(MediaFireDownloader.State), typeof(ArchiveMeta),
|
typeof(BA2FileEntryState), typeof(MediaFireDownloader.State), typeof(ArchiveMeta),
|
||||||
typeof(PropertyFile), typeof(SteamMeta), typeof(SteamWorkshopDownloader), typeof(SteamWorkshopDownloader.State)
|
typeof(PropertyFile), typeof(SteamMeta), typeof(SteamWorkshopDownloader), typeof(SteamWorkshopDownloader.State),
|
||||||
|
typeof(LoversLabDownloader.State), typeof(GameFileSourceDownloader.State)
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -10,12 +10,14 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
{
|
{
|
||||||
public static readonly List<IDownloader> Downloaders = new List<IDownloader>()
|
public static readonly List<IDownloader> Downloaders = new List<IDownloader>()
|
||||||
{
|
{
|
||||||
|
new GameFileSourceDownloader(),
|
||||||
new MegaDownloader(),
|
new MegaDownloader(),
|
||||||
new DropboxDownloader(),
|
new DropboxDownloader(),
|
||||||
new GoogleDriveDownloader(),
|
new GoogleDriveDownloader(),
|
||||||
new ModDBDownloader(),
|
new ModDBDownloader(),
|
||||||
new NexusDownloader(),
|
new NexusDownloader(),
|
||||||
new MediaFireDownloader(),
|
new MediaFireDownloader(),
|
||||||
|
new LoversLabDownloader(),
|
||||||
new HTTPDownloader(),
|
new HTTPDownloader(),
|
||||||
new ManualDownloader(),
|
new ManualDownloader(),
|
||||||
};
|
};
|
||||||
@ -27,9 +29,11 @@ namespace Wabbajack.Lib.Downloaders
|
|||||||
IndexedDownloaders = Downloaders.ToDictionary(d => d.GetType());
|
IndexedDownloaders = Downloaders.ToDictionary(d => d.GetType());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T GetInstance<T>()
|
public static T GetInstance<T>() where T : IDownloader
|
||||||
{
|
{
|
||||||
return (T)IndexedDownloaders[typeof(T)];
|
var inst = (T)IndexedDownloaders[typeof(T)];
|
||||||
|
inst.Prepare();
|
||||||
|
return inst;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<AbstractDownloadState> ResolveArchive(dynamic ini)
|
public static async Task<AbstractDownloadState> ResolveArchive(dynamic ini)
|
||||||
|
86
Wabbajack.Lib/Downloaders/GameFileSourceDownloader.cs
Normal file
86
Wabbajack.Lib/Downloaders/GameFileSourceDownloader.cs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Alphaleonis.Win32.Filesystem;
|
||||||
|
using Wabbajack.Common;
|
||||||
|
using Wabbajack.Lib.Validation;
|
||||||
|
using File = Alphaleonis.Win32.Filesystem.File;
|
||||||
|
using Game = Wabbajack.Common.Game;
|
||||||
|
|
||||||
|
namespace Wabbajack.Lib.Downloaders
|
||||||
|
{
|
||||||
|
public class GameFileSourceDownloader : IDownloader
|
||||||
|
{
|
||||||
|
public async Task<AbstractDownloadState> GetDownloaderState(dynamic archiveINI)
|
||||||
|
{
|
||||||
|
var gameName = (string)archiveINI?.General?.gameName;
|
||||||
|
var gameFile = (string)archiveINI?.General?.gameFile;
|
||||||
|
|
||||||
|
if (gameFile == null || gameFile == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var game = GameRegistry.GetByMO2ArchiveName(gameName);
|
||||||
|
if (game == null) return null;
|
||||||
|
|
||||||
|
var path = game.GameLocation();
|
||||||
|
var filePath = Path.Combine(path, gameFile);
|
||||||
|
|
||||||
|
if (!File.Exists(filePath))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var hash = filePath.FileHashCached();
|
||||||
|
|
||||||
|
return new State
|
||||||
|
{
|
||||||
|
Game = GameRegistry.GetByMO2ArchiveName(gameName).Game,
|
||||||
|
GameFile = gameFile,
|
||||||
|
Hash = hash,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Prepare()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class State : AbstractDownloadState
|
||||||
|
{
|
||||||
|
public Game Game { get; set; }
|
||||||
|
public string GameFile { get; set; }
|
||||||
|
public string Hash { get; set; }
|
||||||
|
|
||||||
|
internal string SourcePath => Path.Combine(Game.MetaData().GameLocation(), GameFile);
|
||||||
|
|
||||||
|
public override bool IsWhitelisted(ServerWhitelist whitelist)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task Download(Archive a, string destination)
|
||||||
|
{
|
||||||
|
using(var src = File.OpenRead(SourcePath))
|
||||||
|
using (var dest = File.OpenWrite(destination))
|
||||||
|
{
|
||||||
|
var size = new FileInfo(SourcePath).Length;
|
||||||
|
src.CopyToWithStatus(size, dest, "Copying from Game folder");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<bool> Verify()
|
||||||
|
{
|
||||||
|
return File.Exists(SourcePath) && SourcePath.FileHashCached() == Hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IDownloader GetDownloader()
|
||||||
|
{
|
||||||
|
return DownloadDispatcher.GetInstance<GameFileSourceDownloader>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetReportEntry(Archive a)
|
||||||
|
{
|
||||||
|
return $"* Game File {Game} - {GameFile}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
201
Wabbajack.Lib/Downloaders/LoversLabDownloader.cs
Normal file
201
Wabbajack.Lib/Downloaders/LoversLabDownloader.cs
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Web;
|
||||||
|
using Wabbajack.Common;
|
||||||
|
using Wabbajack.Lib.LibCefHelpers;
|
||||||
|
using Wabbajack.Lib.Validation;
|
||||||
|
using Xilium.CefGlue.Common;
|
||||||
|
using File = Alphaleonis.Win32.Filesystem.File;
|
||||||
|
|
||||||
|
namespace Wabbajack.Lib.Downloaders
|
||||||
|
{
|
||||||
|
public class LoversLabDownloader : IDownloader
|
||||||
|
{
|
||||||
|
internal HttpClient _authedClient;
|
||||||
|
|
||||||
|
public async Task<AbstractDownloadState> GetDownloaderState(dynamic archive_ini)
|
||||||
|
{
|
||||||
|
Uri url = DownloaderUtils.GetDirectURL(archive_ini);
|
||||||
|
if (url == null || url.Host != "www.loverslab.com" || !url.AbsolutePath.StartsWith("/files/file/")) return null;
|
||||||
|
var id = HttpUtility.ParseQueryString(url.Query)["r"];
|
||||||
|
var file = url.AbsolutePath.Split('/').Last(s => s != "");
|
||||||
|
|
||||||
|
return new State
|
||||||
|
{
|
||||||
|
FileID = id,
|
||||||
|
FileName = file
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Prepare()
|
||||||
|
{
|
||||||
|
_authedClient = (await GetAuthedClient()) ?? throw new Exception("not logged into LL, TODO");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<Helpers.Cookie[]> GetAndCacheLoversLabCookies(BaseCefBrowser browser, Action<string> updateStatus, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
updateStatus("Please Log Into Lovers Lab");
|
||||||
|
browser.Address = "https://www.loverslab.com/login";
|
||||||
|
|
||||||
|
async Task<bool> CleanAds()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await browser.EvaluateJavaScript<string>(
|
||||||
|
"document.querySelectorAll(\".ll_adblock\").forEach(function (itm) { itm.innerHTML = \"\";});");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Utils.Error(ex);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var cookies = new Helpers.Cookie[0];
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
cancel.ThrowIfCancellationRequested();
|
||||||
|
await CleanAds();
|
||||||
|
cookies = (await Helpers.GetCookies("loverslab.com"));
|
||||||
|
if (cookies.FirstOrDefault(c => c.Name == "ips4_member_id") != null)
|
||||||
|
break;
|
||||||
|
await Task.Delay(500, cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
cookies.ToEcryptedJson("loverslabcookies");
|
||||||
|
|
||||||
|
return cookies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<HttpClient> GetAuthedClient()
|
||||||
|
{
|
||||||
|
Helpers.Cookie[] cookies;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cookies = Utils.FromEncryptedJson<Helpers.Cookie[]>("loverslabcookies");
|
||||||
|
if (cookies != null)
|
||||||
|
return Helpers.GetClient(cookies, "https://www.loverslab.com");
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException) { }
|
||||||
|
|
||||||
|
cookies = Utils.Log(new RequestLoversLabLogin()).Task.Result;
|
||||||
|
return Helpers.GetClient(cookies, "https://www.loverslab.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
public class State : AbstractDownloadState
|
||||||
|
{
|
||||||
|
public string FileID { get; set; }
|
||||||
|
public string FileName { get; set; }
|
||||||
|
|
||||||
|
public override bool IsWhitelisted(ServerWhitelist whitelist)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task Download(Archive a, string destination)
|
||||||
|
{
|
||||||
|
var stream = await ResolveDownloadStream();
|
||||||
|
using (var file = File.OpenWrite(destination))
|
||||||
|
{
|
||||||
|
stream.CopyTo(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Stream> ResolveDownloadStream()
|
||||||
|
{
|
||||||
|
var result = DownloadDispatcher.GetInstance<LoversLabDownloader>();
|
||||||
|
TOP:
|
||||||
|
var html = await result._authedClient.GetStringAsync(
|
||||||
|
$"https://www.loverslab.com/files/file/{FileName}/?do=download&r={FileID}");
|
||||||
|
|
||||||
|
var pattern = new Regex("(?<=csrfKey=).*(?=[&\"\'])");
|
||||||
|
var csrfKey = pattern.Matches(html).Cast<Match>().Where(m => m.Length == 32).Select(m => m.ToString()).FirstOrDefault();
|
||||||
|
|
||||||
|
if (csrfKey == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var url =
|
||||||
|
$"https://www.loverslab.com/files/file/{FileName}/?do=download&r={FileID}&confirm=1&t=1&csrfKey={csrfKey}";
|
||||||
|
|
||||||
|
var streamResult = await result._authedClient.GetAsync(url);
|
||||||
|
if (streamResult.StatusCode != HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
Utils.Error(new InvalidOperationException(), $"LoversLab servers reported an error for file: {FileID}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var content_type = streamResult.Content.Headers.ContentType;
|
||||||
|
|
||||||
|
if (content_type.MediaType == "application/json")
|
||||||
|
{
|
||||||
|
// Sometimes LL hands back a json object telling us to wait until a certain time
|
||||||
|
var times = (await streamResult.Content.ReadAsStringAsync()).FromJSONString<WaitResponse>();
|
||||||
|
var secs = times.download - times.currentTime;
|
||||||
|
for (int x = 0; x < secs; x++)
|
||||||
|
{
|
||||||
|
Utils.Status($"Waiting for {secs} at the request of LoversLab", x * 100 / secs);
|
||||||
|
await Task.Delay(1000);
|
||||||
|
}
|
||||||
|
Utils.Status("Retrying download");
|
||||||
|
goto TOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await streamResult.Content.ReadAsStreamAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class WaitResponse
|
||||||
|
{
|
||||||
|
public int download { get; set; }
|
||||||
|
public int currentTime { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<bool> Verify()
|
||||||
|
{
|
||||||
|
var stream = await ResolveDownloadStream();
|
||||||
|
if (stream == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.Close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IDownloader GetDownloader()
|
||||||
|
{
|
||||||
|
return DownloadDispatcher.GetInstance<LoversLabDownloader>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetReportEntry(Archive a)
|
||||||
|
{
|
||||||
|
return $"* Lovers Lab - [{a.Name}](https://www.loverslab.com/files/file/{FileName}/?do=download&r={FileID})";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RequestLoversLabLogin : AUserIntervention
|
||||||
|
{
|
||||||
|
public override string ShortDescription => "Getting LoversLab information";
|
||||||
|
public override string ExtendedDescription { get; }
|
||||||
|
|
||||||
|
private readonly TaskCompletionSource<Helpers.Cookie[]> _source = new TaskCompletionSource<Helpers.Cookie[]>();
|
||||||
|
public Task<Helpers.Cookie[]> Task => _source.Task;
|
||||||
|
|
||||||
|
public void Resume(Helpers.Cookie[] cookies)
|
||||||
|
{
|
||||||
|
Handled = true;
|
||||||
|
_source.SetResult(cookies);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Cancel()
|
||||||
|
{
|
||||||
|
Handled = true;
|
||||||
|
_source.SetCanceled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@ -31,6 +33,27 @@ namespace Wabbajack.Lib.LibCefHelpers
|
|||||||
FileExtractor.ExtractAll(wq, "cefglue.7z", ".");
|
FileExtractor.ExtractAll(wq, "cefglue.7z", ".");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
public static HttpClient GetClient(IEnumerable<Cookie> cookies, string referer)
|
||||||
|
{
|
||||||
|
var container = ToCookieContainer(cookies);
|
||||||
|
var handler = new HttpClientHandler { CookieContainer = container };
|
||||||
|
var client = new HttpClient(handler);
|
||||||
|
client.DefaultRequestHeaders.Referrer = new Uri(referer);
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CookieContainer ToCookieContainer(IEnumerable<Cookie> cookies)
|
||||||
|
{
|
||||||
|
var container = new CookieContainer();
|
||||||
|
cookies
|
||||||
|
.Do(cookie =>
|
||||||
|
{
|
||||||
|
container.Add(new System.Net.Cookie(cookie.Name, cookie.Value, cookie.Path, cookie.Domain));
|
||||||
|
});
|
||||||
|
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task<Cookie[]> GetCookies(string domainEnding)
|
public static async Task<Cookie[]> GetCookies(string domainEnding)
|
||||||
{
|
{
|
||||||
var manager = CefCookieManager.GetGlobal(null);
|
var manager = CefCookieManager.GetGlobal(null);
|
||||||
@ -42,6 +65,7 @@ namespace Wabbajack.Lib.LibCefHelpers
|
|||||||
return (await visitor.Task).Where(c => c.Domain.EndsWith(domainEnding)).ToArray();
|
return (await visitor.Task).Where(c => c.Domain.EndsWith(domainEnding)).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class CookieVisitor : CefCookieVisitor
|
private class CookieVisitor : CefCookieVisitor
|
||||||
{
|
{
|
||||||
TaskCompletionSource<List<Cookie>> _source = new TaskCompletionSource<List<Cookie>>();
|
TaskCompletionSource<List<Cookie>> _source = new TaskCompletionSource<List<Cookie>>();
|
||||||
@ -68,6 +92,8 @@ namespace Wabbajack.Lib.LibCefHelpers
|
|||||||
if (disposing)
|
if (disposing)
|
||||||
_source.SetResult(Cookies);
|
_source.SetResult(Cookies);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Cookie
|
public class Cookie
|
||||||
|
@ -6,12 +6,15 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Alphaleonis.Win32.Filesystem;
|
||||||
using Wabbajack.Common;
|
using Wabbajack.Common;
|
||||||
using Wabbajack.Lib.CompilationSteps;
|
using Wabbajack.Lib.CompilationSteps;
|
||||||
using Wabbajack.Lib.NexusApi;
|
using Wabbajack.Lib.NexusApi;
|
||||||
using Wabbajack.Lib.Validation;
|
using Wabbajack.Lib.Validation;
|
||||||
using Directory = Alphaleonis.Win32.Filesystem.Directory;
|
using Directory = Alphaleonis.Win32.Filesystem.Directory;
|
||||||
using File = Alphaleonis.Win32.Filesystem.File;
|
using File = Alphaleonis.Win32.Filesystem.File;
|
||||||
|
using FileInfo = Alphaleonis.Win32.Filesystem.FileInfo;
|
||||||
|
using Game = Wabbajack.Common.Game;
|
||||||
using Path = Alphaleonis.Win32.Filesystem.Path;
|
using Path = Alphaleonis.Win32.Filesystem.Path;
|
||||||
|
|
||||||
namespace Wabbajack.Lib
|
namespace Wabbajack.Lib
|
||||||
@ -30,6 +33,8 @@ namespace Wabbajack.Lib
|
|||||||
|
|
||||||
public override string GamePath { get; }
|
public override string GamePath { get; }
|
||||||
|
|
||||||
|
public GameMetaData CompilingGame { get; set; }
|
||||||
|
|
||||||
public override string ModListOutputFolder => "output_folder";
|
public override string ModListOutputFolder => "output_folder";
|
||||||
|
|
||||||
public override string ModListOutputFile { get; }
|
public override string ModListOutputFile { get; }
|
||||||
@ -39,6 +44,8 @@ namespace Wabbajack.Lib
|
|||||||
MO2Folder = mo2Folder;
|
MO2Folder = mo2Folder;
|
||||||
MO2Profile = mo2Profile;
|
MO2Profile = mo2Profile;
|
||||||
MO2Ini = Path.Combine(MO2Folder, "ModOrganizer.ini").LoadIniFile();
|
MO2Ini = Path.Combine(MO2Folder, "ModOrganizer.ini").LoadIniFile();
|
||||||
|
var mo2game = (string)MO2Ini.General.gameName;
|
||||||
|
CompilingGame = GameRegistry.Games.First(g => g.Value.MO2Name == mo2game).Value;
|
||||||
GamePath = ((string)MO2Ini.General.gamePath).Replace("\\\\", "\\");
|
GamePath = ((string)MO2Ini.General.gamePath).Replace("\\\\", "\\");
|
||||||
ModListOutputFile = outputFile;
|
ModListOutputFile = outputFile;
|
||||||
}
|
}
|
||||||
@ -74,7 +81,7 @@ namespace Wabbajack.Lib
|
|||||||
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(16);
|
ConfigureProcessor(19);
|
||||||
UpdateTracker.Reset();
|
UpdateTracker.Reset();
|
||||||
UpdateTracker.NextStep("Gathering information");
|
UpdateTracker.NextStep("Gathering information");
|
||||||
Info("Looking for other profiles");
|
Info("Looking for other profiles");
|
||||||
@ -122,20 +129,16 @@ namespace Wabbajack.Lib
|
|||||||
Utils.DeleteDirectory(ModListOutputFolder);
|
Utils.DeleteDirectory(ModListOutputFolder);
|
||||||
|
|
||||||
if (cancel.IsCancellationRequested) return false;
|
if (cancel.IsCancellationRequested) return false;
|
||||||
UpdateTracker.NextStep("Finding Install Files");
|
UpdateTracker.NextStep("Inferring metas for game file downloads");
|
||||||
Directory.CreateDirectory(ModListOutputFolder);
|
await InferMetas();
|
||||||
|
|
||||||
var mo2Files = Directory.EnumerateFiles(MO2Folder, "*", SearchOption.AllDirectories)
|
|
||||||
.Where(p => p.FileExists())
|
|
||||||
.Select(p => new RawSourceFile(VFS.Index.ByRootPath[p]) { Path = p.RelativeTo(MO2Folder) });
|
|
||||||
|
|
||||||
var gameFiles = Directory.EnumerateFiles(GamePath, "*", SearchOption.AllDirectories)
|
|
||||||
.Where(p => p.FileExists())
|
|
||||||
.Select(p => new RawSourceFile(VFS.Index.ByRootPath[p])
|
|
||||||
{ Path = Path.Combine(Consts.GameFolderFilesDir, p.RelativeTo(GamePath)) });
|
|
||||||
|
|
||||||
|
|
||||||
|
if (cancel.IsCancellationRequested) return false;
|
||||||
|
UpdateTracker.NextStep("Reindexing downloads after meta inferring");
|
||||||
|
await VFS.AddRoot(MO2DownloadsFolder);
|
||||||
|
await VFS.WriteToFile(_vfsCacheName);
|
||||||
|
|
||||||
|
if (cancel.IsCancellationRequested) return false;
|
||||||
|
UpdateTracker.NextStep("Pre-validating Archives");
|
||||||
|
|
||||||
IndexedArchives = Directory.EnumerateFiles(MO2DownloadsFolder)
|
IndexedArchives = Directory.EnumerateFiles(MO2DownloadsFolder)
|
||||||
.Where(f => File.Exists(f + ".meta"))
|
.Where(f => File.Exists(f + ".meta"))
|
||||||
@ -148,6 +151,21 @@ namespace Wabbajack.Lib
|
|||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
await CleanInvalidArchives();
|
||||||
|
|
||||||
|
UpdateTracker.NextStep("Finding Install Files");
|
||||||
|
Directory.CreateDirectory(ModListOutputFolder);
|
||||||
|
|
||||||
|
var mo2Files = Directory.EnumerateFiles(MO2Folder, "*", SearchOption.AllDirectories)
|
||||||
|
.Where(p => p.FileExists())
|
||||||
|
.Select(p => new RawSourceFile(VFS.Index.ByRootPath[p]) { Path = p.RelativeTo(MO2Folder) });
|
||||||
|
|
||||||
|
var gameFiles = Directory.EnumerateFiles(GamePath, "*", SearchOption.AllDirectories)
|
||||||
|
.Where(p => p.FileExists())
|
||||||
|
.Select(p => new RawSourceFile(VFS.Index.ByRootPath[p])
|
||||||
|
{ Path = Path.Combine(Consts.GameFolderFilesDir, p.RelativeTo(GamePath)) });
|
||||||
|
|
||||||
|
|
||||||
ModMetas = Directory.EnumerateDirectories(Path.Combine(MO2Folder, "mods"))
|
ModMetas = Directory.EnumerateDirectories(Path.Combine(MO2Folder, "mods"))
|
||||||
.Keep(f =>
|
.Keep(f =>
|
||||||
{
|
{
|
||||||
@ -252,7 +270,7 @@ namespace Wabbajack.Lib
|
|||||||
|
|
||||||
ModList = new ModList
|
ModList = new ModList
|
||||||
{
|
{
|
||||||
GameType = GameRegistry.Games.Values.First(f => f.MO2Name == MO2Ini.General.gameName).Game,
|
GameType = CompilingGame.Game,
|
||||||
WabbajackVersion = WabbajackVersion,
|
WabbajackVersion = WabbajackVersion,
|
||||||
Archives = SelectedArchives.ToList(),
|
Archives = SelectedArchives.ToList(),
|
||||||
ModManager = ModManager.MO2,
|
ModManager = ModManager.MO2,
|
||||||
@ -284,6 +302,66 @@ namespace Wabbajack.Lib
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task CleanInvalidArchives()
|
||||||
|
{
|
||||||
|
var remove = (await IndexedArchives.PMap(Queue, async a =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ResolveArchive(a);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
})).Where(a => a != null).ToHashSet();
|
||||||
|
|
||||||
|
if (remove.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Utils.Log(
|
||||||
|
$"Removing {remove.Count} archives from the compilation state, this is probably not an issue but reference this if you have compilation failures");
|
||||||
|
remove.Do(r => Utils.Log($"Resolution failed for: {r.File}"));
|
||||||
|
IndexedArchives.RemoveAll(a => remove.Contains(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InferMetas()
|
||||||
|
{
|
||||||
|
var to_find = Directory.EnumerateFiles(MO2DownloadsFolder)
|
||||||
|
.Where(f => !f.EndsWith(".meta") && !f.EndsWith(Consts.HashFileExtension))
|
||||||
|
.Where(f => !File.Exists(f + ".meta"))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (to_find.Count == 0) return;
|
||||||
|
|
||||||
|
var games = new[]{CompilingGame}.Concat(GameRegistry.Games.Values.Where(g => g != CompilingGame));
|
||||||
|
var game_files = games
|
||||||
|
.Where(g => g.GameLocation() != null)
|
||||||
|
.SelectMany(game => Directory.EnumerateFiles(game.GameLocation(), "*", DirectoryEnumerationOptions.Recursive).Select(name => (game, name)))
|
||||||
|
.GroupBy(f => (Path.GetFileName(f.name), new FileInfo(f.name).Length))
|
||||||
|
.ToDictionary(f => f.Key);
|
||||||
|
|
||||||
|
await to_find.PMap(Queue, f =>
|
||||||
|
{
|
||||||
|
var vf = VFS.Index.ByFullPath[f];
|
||||||
|
if (!game_files.TryGetValue((Path.GetFileName(f), vf.Size), out var found))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var (game, name) = found.FirstOrDefault(ff => ff.name.FileHash() == vf.Hash);
|
||||||
|
if (name == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
File.WriteAllLines(f+".meta", new[]
|
||||||
|
{
|
||||||
|
"[General]",
|
||||||
|
$"gameName={game.MO2ArchiveName}",
|
||||||
|
$"gameFile={name.RelativeTo(game.GameLocation()).Replace("\\", "/")}"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task IncludeArchiveMetadata()
|
private async Task IncludeArchiveMetadata()
|
||||||
{
|
{
|
||||||
|
@ -12,7 +12,6 @@ using Wabbajack.Common;
|
|||||||
using Wabbajack.Lib.CompilationSteps.CompilationErrors;
|
using Wabbajack.Lib.CompilationSteps.CompilationErrors;
|
||||||
using Wabbajack.Lib.Downloaders;
|
using Wabbajack.Lib.Downloaders;
|
||||||
using Wabbajack.Lib.NexusApi;
|
using Wabbajack.Lib.NexusApi;
|
||||||
using Wabbajack.Lib.StatusMessages;
|
|
||||||
using Wabbajack.Lib.Validation;
|
using Wabbajack.Lib.Validation;
|
||||||
using Directory = Alphaleonis.Win32.Filesystem.Directory;
|
using Directory = Alphaleonis.Win32.Filesystem.Directory;
|
||||||
using File = Alphaleonis.Win32.Filesystem.File;
|
using File = Alphaleonis.Win32.Filesystem.File;
|
||||||
@ -41,10 +40,10 @@ namespace Wabbajack.Lib
|
|||||||
{
|
{
|
||||||
if (cancel.IsCancellationRequested) return false;
|
if (cancel.IsCancellationRequested) return false;
|
||||||
ConfigureProcessor(18, await RecommendQueueSize());
|
ConfigureProcessor(18, await RecommendQueueSize());
|
||||||
var game = GameRegistry.Games[ModList.GameType];
|
var game = ModList.GameType.MetaData();
|
||||||
|
|
||||||
if (GameFolder == null)
|
if (GameFolder == null)
|
||||||
GameFolder = game.GameLocation(SteamHandler.Instance.Games.Any(g => g.Game == game.Game));
|
GameFolder = game.GameLocation();
|
||||||
|
|
||||||
if (GameFolder == null)
|
if (GameFolder == null)
|
||||||
{
|
{
|
||||||
|
@ -113,41 +113,47 @@ namespace Wabbajack.Lib.NexusApi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<string> SetupNexusLogin(BaseCefBrowser browser, Action<string> updateStatus)
|
public static async Task<string> SetupNexusLogin(BaseCefBrowser browser, Action<string> updateStatus, CancellationToken cancel)
|
||||||
{
|
{
|
||||||
updateStatus("Please Log Into the Nexus");
|
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";
|
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)
|
while (true)
|
||||||
{
|
{
|
||||||
var cookies = (await Helpers.GetCookies("nexusmods.com"));
|
var cookies = await Helpers.GetCookies("nexusmods.com");
|
||||||
if (cookies.FirstOrDefault(c => c.Name == "member_id") != null)
|
if (cookies.Any(c => c.Name == "member_id"))
|
||||||
break;
|
break;
|
||||||
await Task.Delay(500);
|
cancel.ThrowIfCancellationRequested();
|
||||||
|
await Task.Delay(500, cancel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// open a web socket to receive the api key
|
// open a web socket to receive the api key
|
||||||
var guid = Guid.NewGuid();
|
var guid = Guid.NewGuid();
|
||||||
var _websocket = new WebSocket("wss://sso.nexusmods.com")
|
using (var websocket = new WebSocket("wss://sso.nexusmods.com")
|
||||||
{
|
{
|
||||||
SslConfiguration =
|
SslConfiguration =
|
||||||
{
|
{
|
||||||
EnabledSslProtocols = SslProtocols.Tls12
|
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); };
|
||||||
|
|
||||||
updateStatus("Please Authorize Wabbajack to Download Mods");
|
websocket.Connect();
|
||||||
var api_key = new TaskCompletionSource<string>();
|
websocket.Send("{\"id\": \"" + guid + "\", \"appid\": \"" + Consts.AppName + "\"}");
|
||||||
_websocket.OnMessage += (sender, msg) => { api_key.SetResult(msg.Data); };
|
await Task.Delay(1000, cancel);
|
||||||
|
|
||||||
_websocket.Connect();
|
// open a web browser to get user permission
|
||||||
_websocket.Send("{\"id\": \"" + guid + "\", \"appid\": \"" + Consts.AppName + "\"}");
|
browser.Address = $"https://www.nexusmods.com/sso?id={guid}&application={Consts.AppName}";
|
||||||
await Task.Delay(1000);
|
using (cancel.Register(() =>
|
||||||
|
{
|
||||||
// open a web browser to get user permission
|
api_key.SetCanceled();
|
||||||
browser.Address = $"https://www.nexusmods.com/sso?id={guid}&application={Consts.AppName}";
|
}))
|
||||||
|
{
|
||||||
return await api_key.Task;
|
return await api_key.Task;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<UserStatus> GetUserStatus()
|
public async Task<UserStatus> GetUserStatus()
|
||||||
@ -322,13 +328,13 @@ namespace Wabbajack.Lib.NexusApi
|
|||||||
|
|
||||||
public async Task<GetModFilesResponse> GetModFiles(Game game, int modid)
|
public async Task<GetModFilesResponse> GetModFiles(Game game, int modid)
|
||||||
{
|
{
|
||||||
var url = $"https://api.nexusmods.com/v1/games/{GameRegistry.Games[game].NexusName}/mods/{modid}/files.json";
|
var url = $"https://api.nexusmods.com/v1/games/{game.MetaData().NexusName}/mods/{modid}/files.json";
|
||||||
return await GetCached<GetModFilesResponse>(url);
|
return await GetCached<GetModFilesResponse>(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<MD5Response>> GetModInfoFromMD5(Game game, string md5Hash)
|
public async Task<List<MD5Response>> GetModInfoFromMD5(Game game, string md5Hash)
|
||||||
{
|
{
|
||||||
var url = $"https://api.nexusmods.com/v1/games/{GameRegistry.Games[game].NexusName}/mods/md5_search/{md5Hash}.json";
|
var url = $"https://api.nexusmods.com/v1/games/{game.MetaData().NexusName}/mods/md5_search/{md5Hash}.json";
|
||||||
return await Get<List<MD5Response>>(url);
|
return await Get<List<MD5Response>>(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ namespace Wabbajack.Lib.NexusApi
|
|||||||
|
|
||||||
public static string GetModURL(Game game, string argModId)
|
public static string GetModURL(Game game, string argModId)
|
||||||
{
|
{
|
||||||
return $"https://nexusmods.com/{GameRegistry.Games[game].NexusName}/mods/{argModId}";
|
return $"https://nexusmods.com/{game.MetaData().NexusName}/mods/{argModId}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string FixupSummary(string argSummary)
|
public static string FixupSummary(string argSummary)
|
||||||
|
@ -3,11 +3,11 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Wabbajack.Common.StatusFeed;
|
using Wabbajack.Common;
|
||||||
|
|
||||||
namespace Wabbajack.Lib.NexusApi
|
namespace Wabbajack.Lib.NexusApi
|
||||||
{
|
{
|
||||||
public class RequestNexusAuthorization : AStatusMessage, IUserIntervention
|
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; }
|
||||||
@ -17,10 +17,13 @@ namespace Wabbajack.Lib.NexusApi
|
|||||||
|
|
||||||
public void Resume(string apikey)
|
public void Resume(string apikey)
|
||||||
{
|
{
|
||||||
|
Handled = true;
|
||||||
_source.SetResult(apikey);
|
_source.SetResult(apikey);
|
||||||
}
|
}
|
||||||
public void Cancel()
|
|
||||||
|
public override void Cancel()
|
||||||
{
|
{
|
||||||
|
Handled = true;
|
||||||
_source.SetCanceled();
|
_source.SetCanceled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,24 +3,16 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Wabbajack.Common.StatusFeed;
|
using Wabbajack.Common;
|
||||||
|
|
||||||
namespace Wabbajack.Lib.StatusMessages
|
namespace Wabbajack.Lib
|
||||||
{
|
{
|
||||||
public class ConfirmUpdateOfExistingInstall : AStatusMessage, IUserIntervention
|
public class ConfirmUpdateOfExistingInstall : ConfirmationIntervention
|
||||||
{
|
{
|
||||||
public enum Choice
|
|
||||||
{
|
|
||||||
Continue,
|
|
||||||
Abort
|
|
||||||
}
|
|
||||||
|
|
||||||
public string OutputFolder { get; set; }
|
public string OutputFolder { get; set; }
|
||||||
public string ModListName { get; set; }
|
public string ModListName { get; set; }
|
||||||
public override string ShortDescription { get; } = "Do you want to overwrite existing files?";
|
|
||||||
|
|
||||||
private TaskCompletionSource<Choice> _source = new TaskCompletionSource<Choice>();
|
public override string ShortDescription { get; } = "Do you want to overwrite existing files?";
|
||||||
public Task<Choice> Task => _source.Task;
|
|
||||||
|
|
||||||
public override string ExtendedDescription
|
public override string ExtendedDescription
|
||||||
{
|
{
|
||||||
@ -29,15 +21,5 @@ namespace Wabbajack.Lib.StatusMessages
|
|||||||
Any files that exist in {OutputFolder} will be changed to match the files found in the {ModListName} modlist. This means that save games will be removed, custom settings
|
Any files that exist in {OutputFolder} will be changed to match the files found in the {ModListName} modlist. This means that save games will be removed, custom settings
|
||||||
will be reverted. Are you sure you wish to continue?";
|
will be reverted. Are you sure you wish to continue?";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Cancel()
|
|
||||||
{
|
|
||||||
_source.SetResult(Choice.Abort);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Confirm()
|
|
||||||
{
|
|
||||||
_source.SetResult(Choice.Continue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ namespace Wabbajack.Lib.Validation
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var nexus = NexusApi.NexusApiUtils.ConvertGameName(GameRegistry.Games[modlist.GameType].NexusName);
|
var nexus = NexusApi.NexusApiUtils.ConvertGameName(modlist.GameType.MetaData().NexusName);
|
||||||
|
|
||||||
modlist.Archives
|
modlist.Archives
|
||||||
.Where(a => a.State is NexusDownloader.State)
|
.Where(a => a.State is NexusDownloader.State)
|
||||||
|
@ -52,7 +52,7 @@ namespace Wabbajack.Lib
|
|||||||
Game = game;
|
Game = game;
|
||||||
|
|
||||||
GamePath = gamePath;
|
GamePath = gamePath;
|
||||||
GameName = GameRegistry.Games[game].NexusName;
|
GameName = game.MetaData().NexusName;
|
||||||
VortexFolder = vortexFolder;
|
VortexFolder = vortexFolder;
|
||||||
DownloadsFolder = downloadsFolder;
|
DownloadsFolder = downloadsFolder;
|
||||||
StagingFolder = stagingFolder;
|
StagingFolder = stagingFolder;
|
||||||
@ -272,7 +272,7 @@ namespace Wabbajack.Lib
|
|||||||
Directory.EnumerateFiles(GamePath, "vortex.deployment.json", SearchOption.AllDirectories)
|
Directory.EnumerateFiles(GamePath, "vortex.deployment.json", SearchOption.AllDirectories)
|
||||||
.Where(File.Exists)
|
.Where(File.Exists)
|
||||||
.Do(f => deploymentFile = f);
|
.Do(f => deploymentFile = f);
|
||||||
var currentGame = GameRegistry.Games[Game];
|
var currentGame = Game.MetaData();
|
||||||
if (currentGame.AdditionalFolders != null && currentGame.AdditionalFolders.Count != 0)
|
if (currentGame.AdditionalFolders != null && currentGame.AdditionalFolders.Count != 0)
|
||||||
currentGame.AdditionalFolders.Do(f => Directory.EnumerateFiles(f, "vortex.deployment.json", SearchOption.AllDirectories)
|
currentGame.AdditionalFolders.Do(f => Directory.EnumerateFiles(f, "vortex.deployment.json", SearchOption.AllDirectories)
|
||||||
.Where(File.Exists)
|
.Where(File.Exists)
|
||||||
@ -311,7 +311,7 @@ namespace Wabbajack.Lib
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task AddExternalFolder()
|
private async Task AddExternalFolder()
|
||||||
{
|
{
|
||||||
var currentGame = GameRegistry.Games[Game];
|
var currentGame = Game.MetaData();
|
||||||
if (currentGame.AdditionalFolders == null || currentGame.AdditionalFolders.Count == 0) return;
|
if (currentGame.AdditionalFolders == null || currentGame.AdditionalFolders.Count == 0) return;
|
||||||
foreach (var f in currentGame.AdditionalFolders)
|
foreach (var f in currentGame.AdditionalFolders)
|
||||||
{
|
{
|
||||||
@ -484,13 +484,13 @@ namespace Wabbajack.Lib
|
|||||||
public static string RetrieveDownloadLocation(Game game, string vortexFolderPath = null)
|
public static string RetrieveDownloadLocation(Game game, string vortexFolderPath = null)
|
||||||
{
|
{
|
||||||
vortexFolderPath = vortexFolderPath ?? TypicalVortexFolder();
|
vortexFolderPath = vortexFolderPath ?? TypicalVortexFolder();
|
||||||
return Path.Combine(vortexFolderPath, "downloads", GameRegistry.Games[game].NexusName);
|
return Path.Combine(vortexFolderPath, "downloads", game.MetaData().NexusName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string RetrieveStagingLocation(Game game, string vortexFolderPath = null)
|
public static string RetrieveStagingLocation(Game game, string vortexFolderPath = null)
|
||||||
{
|
{
|
||||||
vortexFolderPath = vortexFolderPath ?? TypicalVortexFolder();
|
vortexFolderPath = vortexFolderPath ?? TypicalVortexFolder();
|
||||||
var gameName = GameRegistry.Games[game].NexusName;
|
var gameName = game.MetaData().NexusName;
|
||||||
return Path.Combine(vortexFolderPath, gameName, "mods");
|
return Path.Combine(vortexFolderPath, gameName, "mods");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -520,7 +520,7 @@ namespace Wabbajack.Lib
|
|||||||
|
|
||||||
public static bool IsActiveVortexGame(Game g)
|
public static bool IsActiveVortexGame(Game g)
|
||||||
{
|
{
|
||||||
return GameRegistry.Games[g].SupportedModManager == ModManager.Vortex && !GameRegistry.Games[g].Disabled;
|
return g.MetaData().SupportedModManager == ModManager.Vortex && !GameRegistry.Games[g].Disabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ namespace Wabbajack.Lib
|
|||||||
IgnoreMissingFiles = true;
|
IgnoreMissingFiles = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
GameInfo = GameRegistry.Games[ModList.GameType];
|
GameInfo = ModList.GameType.MetaData();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task<bool> _Begin(CancellationToken cancel)
|
protected override async Task<bool> _Begin(CancellationToken cancel)
|
||||||
@ -104,7 +104,7 @@ namespace Wabbajack.Lib
|
|||||||
|
|
||||||
var manualFilesDir = Path.Combine(OutputFolder, Consts.ManualGameFilesDir);
|
var manualFilesDir = Path.Combine(OutputFolder, Consts.ManualGameFilesDir);
|
||||||
|
|
||||||
var gameFolder = GameInfo.GameLocation(SteamHandler.Instance.Games.Any(g => g.Game == GameInfo.Game));
|
var gameFolder = GameInfo.GameLocation();
|
||||||
|
|
||||||
Info($"Copying files from {manualFilesDir} " +
|
Info($"Copying files from {manualFilesDir} " +
|
||||||
$"to the game folder at {gameFolder}");
|
$"to the game folder at {gameFolder}");
|
||||||
|
@ -116,6 +116,8 @@
|
|||||||
<Compile Include="CompilationSteps\IStackStep.cs" />
|
<Compile Include="CompilationSteps\IStackStep.cs" />
|
||||||
<Compile Include="CompilationSteps\PatchStockESMs.cs" />
|
<Compile Include="CompilationSteps\PatchStockESMs.cs" />
|
||||||
<Compile Include="CompilationSteps\Serialization.cs" />
|
<Compile Include="CompilationSteps\Serialization.cs" />
|
||||||
|
<Compile Include="Downloaders\GameFileSourceDownloader.cs" />
|
||||||
|
<Compile Include="Downloaders\LoversLabDownloader.cs" />
|
||||||
<Compile Include="Downloaders\SteamWorkshopDownloader.cs" />
|
<Compile Include="Downloaders\SteamWorkshopDownloader.cs" />
|
||||||
<Compile Include="LibCefHelpers\Init.cs" />
|
<Compile Include="LibCefHelpers\Init.cs" />
|
||||||
<Compile Include="MO2Compiler.cs" />
|
<Compile Include="MO2Compiler.cs" />
|
||||||
@ -206,7 +208,7 @@
|
|||||||
<Version>12.0.3</Version>
|
<Version>12.0.3</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="ReactiveUI">
|
<PackageReference Include="ReactiveUI">
|
||||||
<Version>10.5.30</Version>
|
<Version>11.0.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="SharpCompress">
|
<PackageReference Include="SharpCompress">
|
||||||
<Version>0.24.0</Version>
|
<Version>0.24.0</Version>
|
||||||
@ -215,7 +217,7 @@
|
|||||||
<Version>1.2.1</Version>
|
<Version>1.2.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="System.Reactive">
|
<PackageReference Include="System.Reactive">
|
||||||
<Version>4.2.0</Version>
|
<Version>4.3.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="WebSocketSharpFork">
|
<PackageReference Include="WebSocketSharpFork">
|
||||||
<Version>1.0.4</Version>
|
<Version>1.0.4</Version>
|
||||||
|
@ -246,14 +246,12 @@ namespace Wabbajack.Test
|
|||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void CanLoadFromGithub()
|
public async Task CanLoadFromGithub()
|
||||||
{
|
{
|
||||||
using (var workQueue = new WorkQueue())
|
using (var workQueue = new WorkQueue())
|
||||||
{
|
{
|
||||||
new ValidateModlist(workQueue).LoadListsFromGithub();
|
await new ValidateModlist(workQueue).LoadListsFromGithub();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,36 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Reactive.Disposables;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using Alphaleonis.Win32.Filesystem;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using Wabbajack.Common;
|
using Wabbajack.Common;
|
||||||
|
using Wabbajack.Common.StatusFeed;
|
||||||
using Wabbajack.Lib;
|
using Wabbajack.Lib;
|
||||||
using Wabbajack.Lib.Downloaders;
|
using Wabbajack.Lib.Downloaders;
|
||||||
using Wabbajack.Lib.LibCefHelpers;
|
using Wabbajack.Lib.LibCefHelpers;
|
||||||
using Wabbajack.Lib.NexusApi;
|
using Wabbajack.Lib.NexusApi;
|
||||||
using Wabbajack.Lib.Validation;
|
using Wabbajack.Lib.Validation;
|
||||||
using File = Alphaleonis.Win32.Filesystem.File;
|
using File = Alphaleonis.Win32.Filesystem.File;
|
||||||
|
using Game = Wabbajack.Common.Game;
|
||||||
|
|
||||||
namespace Wabbajack.Test
|
namespace Wabbajack.Test
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class DownloaderTests
|
public class DownloaderTests
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public TestContext TestContext { get; set; }
|
||||||
|
|
||||||
[TestInitialize]
|
[TestInitialize]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
Helpers.ExtractLibs();
|
Helpers.ExtractLibs();
|
||||||
|
Utils.LogMessages.OfType<IInfo>().Subscribe(onNext: msg => TestContext.WriteLine(msg.ShortDescription));
|
||||||
|
Utils.LogMessages.OfType<IUserIntervention>().Subscribe(msg =>
|
||||||
|
TestContext.WriteLine("ERROR: User intervetion required: " + msg.ShortDescription));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@ -262,6 +274,59 @@ namespace Wabbajack.Test
|
|||||||
|
|
||||||
Assert.AreEqual("2lZt+1h6wxM=", filename.FileHash());
|
Assert.AreEqual("2lZt+1h6wxM=", filename.FileHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task LoversLabDownload()
|
||||||
|
{
|
||||||
|
await DownloadDispatcher.GetInstance<LoversLabDownloader>().Prepare();
|
||||||
|
var ini = @"[General]
|
||||||
|
directURL=https://www.loverslab.com/files/file/11116-test-file-for-wabbajack-integration/?do=download&r=737123&confirm=1&t=1";
|
||||||
|
|
||||||
|
var state = (AbstractDownloadState)DownloadDispatcher.ResolveArchive(ini.LoadIniString());
|
||||||
|
|
||||||
|
Assert.IsNotNull(state);
|
||||||
|
|
||||||
|
/*var url_state = DownloadDispatcher.ResolveArchive("https://www.loverslab.com/files/file/11116-test-file-for-wabbajack-integration/?do=download&r=737123&confirm=1&t=1");
|
||||||
|
Assert.AreEqual("http://build.wabbajack.org/WABBAJACK_TEST_FILE.txt",
|
||||||
|
((HTTPDownloader.State)url_state).Url);
|
||||||
|
*/
|
||||||
|
var converted = state.ViaJSON();
|
||||||
|
Assert.IsTrue(await converted.Verify());
|
||||||
|
var filename = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
Assert.IsTrue(converted.IsWhitelisted(new ServerWhitelist { AllowedPrefixes = new List<string>() }));
|
||||||
|
|
||||||
|
await converted.Download(new Archive { Name = "MEGA Test.txt" }, filename);
|
||||||
|
|
||||||
|
Assert.AreEqual("eSIyd+KOG3s=", Utils.FileHash(filename));
|
||||||
|
|
||||||
|
Assert.AreEqual(File.ReadAllText(filename), "Cheese for Everyone!");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task GameFileSourceDownload()
|
||||||
|
{
|
||||||
|
await DownloadDispatcher.GetInstance<LoversLabDownloader>().Prepare();
|
||||||
|
var ini = $@"[General]
|
||||||
|
gameName={Game.SkyrimSpecialEdition.MetaData().MO2ArchiveName}
|
||||||
|
gameFile=Data/Update.esm";
|
||||||
|
|
||||||
|
var state = (AbstractDownloadState)DownloadDispatcher.ResolveArchive(ini.LoadIniString());
|
||||||
|
|
||||||
|
Assert.IsNotNull(state);
|
||||||
|
|
||||||
|
var converted = state.ViaJSON();
|
||||||
|
Assert.IsTrue(await converted.Verify());
|
||||||
|
var filename = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
Assert.IsTrue(converted.IsWhitelisted(new ServerWhitelist { AllowedPrefixes = new List<string>() }));
|
||||||
|
|
||||||
|
await converted.Download(new Archive { Name = "Update.esm" }, filename);
|
||||||
|
|
||||||
|
Assert.AreEqual("/DLG/LjdGXI=", Utils.FileHash(filename));
|
||||||
|
CollectionAssert.AreEqual(File.ReadAllBytes(Path.Combine(Game.SkyrimSpecialEdition.MetaData().GameLocation(), "Data/Update.esm")), File.ReadAllBytes(filename));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ namespace Wabbajack.Test
|
|||||||
new List<string>
|
new List<string>
|
||||||
{
|
{
|
||||||
"[General]",
|
"[General]",
|
||||||
$"gameName={GameRegistry.Games[game].MO2ArchiveName}",
|
$"gameName={game.MetaData().MO2ArchiveName}",
|
||||||
$"modID={modid}",
|
$"modID={modid}",
|
||||||
$"fileID={file.file_id}"
|
$"fileID={file.file_id}"
|
||||||
});
|
});
|
||||||
|
@ -46,7 +46,7 @@ namespace Wabbajack.Test
|
|||||||
File.WriteAllLines(Path.Combine(MO2Folder, "ModOrganizer.ini"), new []
|
File.WriteAllLines(Path.Combine(MO2Folder, "ModOrganizer.ini"), new []
|
||||||
{
|
{
|
||||||
"[General]",
|
"[General]",
|
||||||
$"gameName={GameRegistry.Games[Game].MO2Name}",
|
$"gameName={Game.MetaData().MO2Name}",
|
||||||
$"gamePath={GameFolder.Replace("\\", "\\\\")}",
|
$"gamePath={GameFolder.Replace("\\", "\\\\")}",
|
||||||
$"download_directory={DownloadsFolder}"
|
$"download_directory={DownloadsFolder}"
|
||||||
});
|
});
|
||||||
|
26
Wabbajack.Test/UtilsTests.cs
Normal file
26
Wabbajack.Test/UtilsTests.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Wabbajack.Common;
|
||||||
|
|
||||||
|
namespace Wabbajack.Test
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class UtilsTests
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void IsInPathTests()
|
||||||
|
{
|
||||||
|
Assert.IsTrue("c:\\foo\\bar.exe".IsInPath("c:\\foo"));
|
||||||
|
Assert.IsFalse("c:\\foo\\bar.exe".IsInPath("c:\\fo"));
|
||||||
|
Assert.IsTrue("c:\\Foo\\bar.exe".IsInPath("c:\\foo"));
|
||||||
|
Assert.IsTrue("c:\\foo\\bar.exe".IsInPath("c:\\Foo"));
|
||||||
|
Assert.IsTrue("c:\\foo\\bar.exe".IsInPath("c:\\fOo"));
|
||||||
|
Assert.IsTrue("c:\\foo\\bar.exe".IsInPath("c:\\foo\\"));
|
||||||
|
Assert.IsTrue("c:\\foo\\bar\\".IsInPath("c:\\foo\\"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -112,6 +112,7 @@
|
|||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="ContentRightsManagementTests.cs" />
|
<Compile Include="ContentRightsManagementTests.cs" />
|
||||||
<Compile Include="CompilationStackTests.cs" />
|
<Compile Include="CompilationStackTests.cs" />
|
||||||
|
<Compile Include="UtilsTests.cs" />
|
||||||
<Compile Include="VortexTests.cs" />
|
<Compile Include="VortexTests.cs" />
|
||||||
<Compile Include="WebAutomationTests.cs" />
|
<Compile Include="WebAutomationTests.cs" />
|
||||||
<Compile Include="zEditIntegrationTests.cs" />
|
<Compile Include="zEditIntegrationTests.cs" />
|
||||||
@ -154,10 +155,10 @@
|
|||||||
<Version>12.0.3</Version>
|
<Version>12.0.3</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="ReactiveUI">
|
<PackageReference Include="ReactiveUI">
|
||||||
<Version>10.5.30</Version>
|
<Version>11.0.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="System.Reactive">
|
<PackageReference Include="System.Reactive">
|
||||||
<Version>4.2.0</Version>
|
<Version>4.3.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup />
|
<ItemGroup />
|
||||||
|
@ -96,7 +96,7 @@
|
|||||||
<Version>2.0.0</Version>
|
<Version>2.0.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="System.Reactive">
|
<PackageReference Include="System.Reactive">
|
||||||
<Version>4.2.0</Version>
|
<Version>4.3.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
|
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
|
||||||
|
@ -89,7 +89,7 @@
|
|||||||
<Version>2.2.6</Version>
|
<Version>2.2.6</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="System.Collections.Immutable">
|
<PackageReference Include="System.Collections.Immutable">
|
||||||
<Version>1.6.0</Version>
|
<Version>1.7.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
32
Wabbajack/Converters/IsTypeVisibilityConverter.cs
Normal file
32
Wabbajack/Converters/IsTypeVisibilityConverter.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Data;
|
||||||
|
|
||||||
|
namespace Wabbajack
|
||||||
|
{
|
||||||
|
public class IsTypeVisibilityConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (targetType != typeof(Visibility))
|
||||||
|
throw new InvalidOperationException($"The target must be of type {nameof(Visibility)}");
|
||||||
|
|
||||||
|
if (!(parameter is Type paramType))
|
||||||
|
{
|
||||||
|
throw new ArgumentException();
|
||||||
|
}
|
||||||
|
if (value == null) return Visibility.Collapsed;
|
||||||
|
return paramType.Equals(value.GetType()) ? Visibility.Visible : Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
Wabbajack/Resources/Wabba_Mouth_Small.png
Normal file
BIN
Wabbajack/Resources/Wabba_Mouth_Small.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 58 KiB |
@ -20,8 +20,8 @@ namespace Wabbajack
|
|||||||
public InstallerSettings Installer { get; set; } = new InstallerSettings();
|
public InstallerSettings Installer { get; set; } = new InstallerSettings();
|
||||||
public CompilerSettings Compiler { get; set; } = new CompilerSettings();
|
public CompilerSettings Compiler { get; set; } = new CompilerSettings();
|
||||||
|
|
||||||
[JsonIgnoreAttribute]
|
|
||||||
private Subject<Unit> _saveSignal = new Subject<Unit>();
|
private Subject<Unit> _saveSignal = new Subject<Unit>();
|
||||||
|
[JsonIgnoreAttribute]
|
||||||
public IObservable<Unit> SaveSignal => _saveSignal;
|
public IObservable<Unit> SaveSignal => _saveSignal;
|
||||||
|
|
||||||
public static bool TryLoadTypicalSettings(out MainSettings settings)
|
public static bool TryLoadTypicalSettings(out MainSettings settings)
|
||||||
@ -58,6 +58,7 @@ namespace Wabbajack
|
|||||||
{
|
{
|
||||||
public string InstallationLocation { get; set; }
|
public string InstallationLocation { get; set; }
|
||||||
public string DownloadLocation { get; set; }
|
public string DownloadLocation { get; set; }
|
||||||
|
public bool AutomaticallyOverrideExistingInstall { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CompilerSettings
|
public class CompilerSettings
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
<local:InverseBooleanConverter x:Key="InverseBooleanConverter" />
|
<local:InverseBooleanConverter x:Key="InverseBooleanConverter" />
|
||||||
<local:IsNotNullVisibilityConverter x:Key="IsNotNullVisibilityConverter" />
|
<local:IsNotNullVisibilityConverter x:Key="IsNotNullVisibilityConverter" />
|
||||||
<local:EqualsToBoolConverter x:Key="EqualsToBoolConverter" />
|
<local:EqualsToBoolConverter x:Key="EqualsToBoolConverter" />
|
||||||
|
<local:IsTypeVisibilityConverter x:Key="IsTypeVisibilityConverter" />
|
||||||
|
|
||||||
<!-- Colors -->
|
<!-- Colors -->
|
||||||
<Color x:Key="WindowBackgroundColor">#121212</Color>
|
<Color x:Key="WindowBackgroundColor">#121212</Color>
|
||||||
@ -43,6 +44,7 @@
|
|||||||
<Color x:Key="Secondary">#03DAC6</Color>
|
<Color x:Key="Secondary">#03DAC6</Color>
|
||||||
<Color x:Key="DarkSecondary">#0e8f83</Color>
|
<Color x:Key="DarkSecondary">#0e8f83</Color>
|
||||||
<Color x:Key="DarkerSecondary">#095952</Color>
|
<Color x:Key="DarkerSecondary">#095952</Color>
|
||||||
|
<Color x:Key="SecondaryBackground">#042421</Color>
|
||||||
<Color x:Key="OffWhiteSeconday">#cef0ed</Color>
|
<Color x:Key="OffWhiteSeconday">#cef0ed</Color>
|
||||||
<Color x:Key="LightSecondary">#8cede5</Color>
|
<Color x:Key="LightSecondary">#8cede5</Color>
|
||||||
<Color x:Key="IntenseSecondary">#00ffe7</Color>
|
<Color x:Key="IntenseSecondary">#00ffe7</Color>
|
||||||
@ -115,6 +117,7 @@
|
|||||||
<SolidColorBrush x:Key="SecondaryBrush" Color="{StaticResource Secondary}" />
|
<SolidColorBrush x:Key="SecondaryBrush" Color="{StaticResource Secondary}" />
|
||||||
<SolidColorBrush x:Key="DarkSecondaryBrush" Color="{StaticResource DarkSecondary}" />
|
<SolidColorBrush x:Key="DarkSecondaryBrush" Color="{StaticResource DarkSecondary}" />
|
||||||
<SolidColorBrush x:Key="DarkerSecondaryBrush" Color="{StaticResource DarkerSecondary}" />
|
<SolidColorBrush x:Key="DarkerSecondaryBrush" Color="{StaticResource DarkerSecondary}" />
|
||||||
|
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="{StaticResource SecondaryBackground}" />
|
||||||
<SolidColorBrush x:Key="OffWhiteSecondayBrush" Color="{StaticResource OffWhiteSeconday}" />
|
<SolidColorBrush x:Key="OffWhiteSecondayBrush" Color="{StaticResource OffWhiteSeconday}" />
|
||||||
<SolidColorBrush x:Key="LightSecondaryBrush" Color="{StaticResource LightSecondary}" />
|
<SolidColorBrush x:Key="LightSecondaryBrush" Color="{StaticResource LightSecondary}" />
|
||||||
<SolidColorBrush x:Key="IntenseSecondaryBrush" Color="{StaticResource IntenseSecondary}" />
|
<SolidColorBrush x:Key="IntenseSecondaryBrush" Color="{StaticResource IntenseSecondary}" />
|
||||||
@ -3708,4 +3711,58 @@
|
|||||||
</Style>
|
</Style>
|
||||||
<Style BasedOn="{StaticResource MainFilePickerStyle}" TargetType="{x:Type local:FilePicker}" />
|
<Style BasedOn="{StaticResource MainFilePickerStyle}" TargetType="{x:Type local:FilePicker}" />
|
||||||
|
|
||||||
|
<!-- User Intervention Background -->
|
||||||
|
<Style x:Key="AttentionBorderStyle" TargetType="Border">
|
||||||
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
|
<Setter Property="Background" Value="{StaticResource WindowBackgroundBrush}" />
|
||||||
|
<Setter Property="BorderBrush" Value="{StaticResource DarkerSecondaryBrush}" />
|
||||||
|
<Style.Triggers>
|
||||||
|
<MultiDataTrigger>
|
||||||
|
<MultiDataTrigger.Conditions>
|
||||||
|
<Condition Binding="{Binding IsVisible, RelativeSource={RelativeSource Self}}" Value="True" />
|
||||||
|
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="False" />
|
||||||
|
</MultiDataTrigger.Conditions>
|
||||||
|
<MultiDataTrigger.EnterActions>
|
||||||
|
<BeginStoryboard x:Name="BorderGlow">
|
||||||
|
<Storyboard>
|
||||||
|
<ColorAnimation
|
||||||
|
AutoReverse="True"
|
||||||
|
RepeatBehavior="Forever"
|
||||||
|
Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)"
|
||||||
|
To="{StaticResource Secondary}"
|
||||||
|
Duration="0:0:1.5" />
|
||||||
|
</Storyboard>
|
||||||
|
</BeginStoryboard>
|
||||||
|
<BeginStoryboard x:Name="BackgroundGlow">
|
||||||
|
<Storyboard>
|
||||||
|
<ColorAnimation
|
||||||
|
AutoReverse="True"
|
||||||
|
RepeatBehavior="Forever"
|
||||||
|
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
|
||||||
|
To="{StaticResource SecondaryBackground}"
|
||||||
|
Duration="0:0:1.5" />
|
||||||
|
</Storyboard>
|
||||||
|
</BeginStoryboard>
|
||||||
|
</MultiDataTrigger.EnterActions>
|
||||||
|
<MultiDataTrigger.ExitActions>
|
||||||
|
<BeginStoryboard>
|
||||||
|
<Storyboard>
|
||||||
|
<ColorAnimation
|
||||||
|
Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)"
|
||||||
|
To="{StaticResource DarkerSecondary}"
|
||||||
|
Duration="0:0:0.1" />
|
||||||
|
</Storyboard>
|
||||||
|
</BeginStoryboard>
|
||||||
|
<BeginStoryboard>
|
||||||
|
<Storyboard>
|
||||||
|
<ColorAnimation
|
||||||
|
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
|
||||||
|
To="{StaticResource WindowBackgroundColor}"
|
||||||
|
Duration="0:0:0.1" />
|
||||||
|
</Storyboard>
|
||||||
|
</BeginStoryboard>
|
||||||
|
</MultiDataTrigger.ExitActions>
|
||||||
|
</MultiDataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
@ -11,8 +11,11 @@ namespace Wabbajack
|
|||||||
{
|
{
|
||||||
public interface ISubInstallerVM
|
public interface ISubInstallerVM
|
||||||
{
|
{
|
||||||
|
InstallerVM Parent { get; }
|
||||||
IReactiveCommand BeginCommand { get; }
|
IReactiveCommand BeginCommand { get; }
|
||||||
AInstaller ActiveInstallation { get; }
|
AInstaller ActiveInstallation { get; }
|
||||||
void Unload();
|
void Unload();
|
||||||
|
bool SupportsAfterInstallNavigation { get; }
|
||||||
|
void AfterInstallNavigation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ using DynamicData;
|
|||||||
using DynamicData.Binding;
|
using DynamicData.Binding;
|
||||||
using Wabbajack.Common.StatusFeed;
|
using Wabbajack.Common.StatusFeed;
|
||||||
using System.Reactive;
|
using System.Reactive;
|
||||||
using Wabbajack.Common.StatusFeed;
|
|
||||||
|
|
||||||
namespace Wabbajack
|
namespace Wabbajack
|
||||||
{
|
{
|
||||||
@ -79,11 +78,19 @@ namespace Wabbajack
|
|||||||
private readonly ObservableAsPropertyHelper<ModManager?> _TargetManager;
|
private readonly ObservableAsPropertyHelper<ModManager?> _TargetManager;
|
||||||
public ModManager? TargetManager => _TargetManager.Value;
|
public ModManager? TargetManager => _TargetManager.Value;
|
||||||
|
|
||||||
|
private readonly ObservableAsPropertyHelper<IUserIntervention> _ActiveGlobalUserIntervention;
|
||||||
|
public IUserIntervention ActiveGlobalUserIntervention => _ActiveGlobalUserIntervention.Value;
|
||||||
|
|
||||||
|
private readonly ObservableAsPropertyHelper<bool> _Completed;
|
||||||
|
public bool Completed => _Completed.Value;
|
||||||
|
|
||||||
// Command properties
|
// Command properties
|
||||||
public IReactiveCommand ShowReportCommand { get; }
|
public IReactiveCommand ShowReportCommand { get; }
|
||||||
public IReactiveCommand OpenReadmeCommand { get; }
|
public IReactiveCommand OpenReadmeCommand { get; }
|
||||||
public IReactiveCommand VisitWebsiteCommand { get; }
|
public IReactiveCommand VisitWebsiteCommand { get; }
|
||||||
public IReactiveCommand BackCommand { get; }
|
public IReactiveCommand BackCommand { get; }
|
||||||
|
public IReactiveCommand CloseWhenCompleteCommand { get; }
|
||||||
|
public IReactiveCommand GoToInstallCommand { get; }
|
||||||
|
|
||||||
public InstallerVM(MainWindowVM mainWindowVM)
|
public InstallerVM(MainWindowVM mainWindowVM)
|
||||||
{
|
{
|
||||||
@ -293,6 +300,48 @@ namespace Wabbajack
|
|||||||
InstallingMode = true;
|
InstallingMode = true;
|
||||||
})
|
})
|
||||||
.DisposeWith(CompositeDisposable);
|
.DisposeWith(CompositeDisposable);
|
||||||
|
|
||||||
|
// Listen for user interventions, and compile a dynamic list of all unhandled ones
|
||||||
|
var activeInterventions = this.WhenAny(x => x.Installer.ActiveInstallation)
|
||||||
|
.SelectMany(c => c?.LogMessages ?? Observable.Empty<IStatusMessage>())
|
||||||
|
.WhereCastable<IStatusMessage, IUserIntervention>()
|
||||||
|
.ToObservableChangeSet()
|
||||||
|
.AutoRefresh(i => i.Handled)
|
||||||
|
.Filter(i => !i.Handled)
|
||||||
|
.AsObservableList();
|
||||||
|
|
||||||
|
// Find the top intervention /w no CPU ID to be marked as "global"
|
||||||
|
_ActiveGlobalUserIntervention = activeInterventions.Connect()
|
||||||
|
.Filter(x => x.CpuID == WorkQueue.UnassignedCpuId)
|
||||||
|
.QueryWhenChanged(query => query.FirstOrDefault())
|
||||||
|
.ObserveOnGuiThread()
|
||||||
|
.ToProperty(this, nameof(ActiveGlobalUserIntervention));
|
||||||
|
|
||||||
|
_Completed = Observable.CombineLatest(
|
||||||
|
this.WhenAny(x => x.Installing),
|
||||||
|
this.WhenAny(x => x.InstallingMode),
|
||||||
|
resultSelector: (installing, installingMode) =>
|
||||||
|
{
|
||||||
|
return installingMode && !installing;
|
||||||
|
})
|
||||||
|
.ToProperty(this, nameof(Completed));
|
||||||
|
|
||||||
|
CloseWhenCompleteCommand = ReactiveCommand.Create(
|
||||||
|
canExecute: this.WhenAny(x => x.Completed),
|
||||||
|
execute: () =>
|
||||||
|
{
|
||||||
|
MWVM.ShutdownApplication();
|
||||||
|
});
|
||||||
|
|
||||||
|
GoToInstallCommand = ReactiveCommand.Create(
|
||||||
|
canExecute: Observable.CombineLatest(
|
||||||
|
this.WhenAny(x => x.Completed),
|
||||||
|
this.WhenAny(x => x.Installer.SupportsAfterInstallNavigation),
|
||||||
|
resultSelector: (complete, supports) => complete && supports),
|
||||||
|
execute: () =>
|
||||||
|
{
|
||||||
|
Installer.AfterInstallNavigation();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShowReport()
|
private void ShowReport()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
@ -15,7 +16,7 @@ namespace Wabbajack
|
|||||||
{
|
{
|
||||||
public class MO2InstallerVM : ViewModel, ISubInstallerVM
|
public class MO2InstallerVM : ViewModel, ISubInstallerVM
|
||||||
{
|
{
|
||||||
private InstallerVM _installerVM;
|
public InstallerVM Parent { get; }
|
||||||
|
|
||||||
public IReactiveCommand BeginCommand { get; }
|
public IReactiveCommand BeginCommand { get; }
|
||||||
|
|
||||||
@ -29,9 +30,14 @@ namespace Wabbajack
|
|||||||
|
|
||||||
public FilePickerVM DownloadLocation { get; }
|
public FilePickerVM DownloadLocation { get; }
|
||||||
|
|
||||||
|
public bool SupportsAfterInstallNavigation => true;
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public bool AutomaticallyOverwrite { get; set; }
|
||||||
|
|
||||||
public MO2InstallerVM(InstallerVM installerVM)
|
public MO2InstallerVM(InstallerVM installerVM)
|
||||||
{
|
{
|
||||||
_installerVM = installerVM;
|
Parent = installerVM;
|
||||||
|
|
||||||
Location = new FilePickerVM()
|
Location = new FilePickerVM()
|
||||||
{
|
{
|
||||||
@ -131,11 +137,31 @@ namespace Wabbajack
|
|||||||
if (settingsPair.Current == null) return;
|
if (settingsPair.Current == null) return;
|
||||||
Location.TargetPath = settingsPair.Current.InstallationLocation;
|
Location.TargetPath = settingsPair.Current.InstallationLocation;
|
||||||
DownloadLocation.TargetPath = settingsPair.Current.DownloadLocation;
|
DownloadLocation.TargetPath = settingsPair.Current.DownloadLocation;
|
||||||
|
AutomaticallyOverwrite = settingsPair.Current.AutomaticallyOverrideExistingInstall;
|
||||||
})
|
})
|
||||||
.DisposeWith(CompositeDisposable);
|
.DisposeWith(CompositeDisposable);
|
||||||
installerVM.MWVM.Settings.SaveSignal
|
installerVM.MWVM.Settings.SaveSignal
|
||||||
.Subscribe(_ => SaveSettings(CurrentSettings))
|
.Subscribe(_ => SaveSettings(CurrentSettings))
|
||||||
.DisposeWith(CompositeDisposable);
|
.DisposeWith(CompositeDisposable);
|
||||||
|
|
||||||
|
// Hook onto user interventions, and intercept MO2 specific ones for customization
|
||||||
|
this.WhenAny(x => x.ActiveInstallation.LogMessages)
|
||||||
|
.Switch()
|
||||||
|
.Subscribe(x =>
|
||||||
|
{
|
||||||
|
switch (x)
|
||||||
|
{
|
||||||
|
case ConfirmUpdateOfExistingInstall c:
|
||||||
|
if (AutomaticallyOverwrite)
|
||||||
|
{
|
||||||
|
c.Confirm();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.DisposeWith(CompositeDisposable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Unload()
|
public void Unload()
|
||||||
@ -145,10 +171,16 @@ namespace Wabbajack
|
|||||||
|
|
||||||
private void SaveSettings(Mo2ModlistInstallationSettings settings)
|
private void SaveSettings(Mo2ModlistInstallationSettings settings)
|
||||||
{
|
{
|
||||||
_installerVM.MWVM.Settings.Installer.LastInstalledListLocation = _installerVM.ModListLocation.TargetPath;
|
Parent.MWVM.Settings.Installer.LastInstalledListLocation = Parent.ModListLocation.TargetPath;
|
||||||
if (settings == null) return;
|
if (settings == null) return;
|
||||||
settings.InstallationLocation = Location.TargetPath;
|
settings.InstallationLocation = Location.TargetPath;
|
||||||
settings.DownloadLocation = DownloadLocation.TargetPath;
|
settings.DownloadLocation = DownloadLocation.TargetPath;
|
||||||
|
settings.AutomaticallyOverrideExistingInstall = AutomaticallyOverwrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AfterInstallNavigation()
|
||||||
|
{
|
||||||
|
Process.Start("explorer.exe", Location.TargetPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@ namespace Wabbajack
|
|||||||
{
|
{
|
||||||
public class VortexInstallerVM : ViewModel, ISubInstallerVM
|
public class VortexInstallerVM : ViewModel, ISubInstallerVM
|
||||||
{
|
{
|
||||||
|
public InstallerVM Parent { get; }
|
||||||
|
|
||||||
public IReactiveCommand BeginCommand { get; }
|
public IReactiveCommand BeginCommand { get; }
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
@ -27,8 +29,12 @@ namespace Wabbajack
|
|||||||
private readonly ObservableAsPropertyHelper<Game> _TargetGame;
|
private readonly ObservableAsPropertyHelper<Game> _TargetGame;
|
||||||
public Game TargetGame => _TargetGame.Value;
|
public Game TargetGame => _TargetGame.Value;
|
||||||
|
|
||||||
|
public bool SupportsAfterInstallNavigation => false;
|
||||||
|
|
||||||
public VortexInstallerVM(InstallerVM installerVM)
|
public VortexInstallerVM(InstallerVM installerVM)
|
||||||
{
|
{
|
||||||
|
Parent = installerVM;
|
||||||
|
|
||||||
_TargetGame = installerVM.WhenAny(x => x.ModList.SourceModList.GameType)
|
_TargetGame = installerVM.WhenAny(x => x.ModList.SourceModList.GameType)
|
||||||
.ToProperty(this, nameof(TargetGame));
|
.ToProperty(this, nameof(TargetGame));
|
||||||
|
|
||||||
@ -91,5 +97,10 @@ namespace Wabbajack
|
|||||||
public void Unload()
|
public void Unload()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AfterInstallNavigation()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,6 @@ using System.Windows.Threading;
|
|||||||
using Wabbajack.Common;
|
using Wabbajack.Common;
|
||||||
using Wabbajack.Common.StatusFeed;
|
using Wabbajack.Common.StatusFeed;
|
||||||
using Wabbajack.Lib;
|
using Wabbajack.Lib;
|
||||||
using Wabbajack.Lib.NexusApi;
|
|
||||||
using Wabbajack.Lib.StatusMessages;
|
|
||||||
|
|
||||||
namespace Wabbajack
|
namespace Wabbajack
|
||||||
{
|
{
|
||||||
@ -37,6 +35,7 @@ namespace Wabbajack
|
|||||||
public readonly Lazy<ModListGalleryVM> Gallery;
|
public readonly Lazy<ModListGalleryVM> Gallery;
|
||||||
public readonly ModeSelectionVM ModeSelectionVM;
|
public readonly ModeSelectionVM ModeSelectionVM;
|
||||||
public readonly WebBrowserVM WebBrowserVM;
|
public readonly WebBrowserVM WebBrowserVM;
|
||||||
|
public readonly UserInterventionHandlers UserInterventionHandlers;
|
||||||
public Dispatcher ViewDispatcher { get; set; }
|
public Dispatcher ViewDispatcher { get; set; }
|
||||||
|
|
||||||
public MainWindowVM(MainWindow mainWindow, MainSettings settings)
|
public MainWindowVM(MainWindow mainWindow, MainSettings settings)
|
||||||
@ -49,6 +48,7 @@ namespace Wabbajack
|
|||||||
Gallery = new Lazy<ModListGalleryVM>(() => new ModListGalleryVM(this));
|
Gallery = new Lazy<ModListGalleryVM>(() => new ModListGalleryVM(this));
|
||||||
ModeSelectionVM = new ModeSelectionVM(this);
|
ModeSelectionVM = new ModeSelectionVM(this);
|
||||||
WebBrowserVM = new WebBrowserVM();
|
WebBrowserVM = new WebBrowserVM();
|
||||||
|
UserInterventionHandlers = new UserInterventionHandlers(this);
|
||||||
|
|
||||||
// Set up logging
|
// Set up logging
|
||||||
Utils.LogMessages
|
Utils.LogMessages
|
||||||
@ -63,12 +63,11 @@ namespace Wabbajack
|
|||||||
.DisposeWith(CompositeDisposable);
|
.DisposeWith(CompositeDisposable);
|
||||||
|
|
||||||
Utils.LogMessages
|
Utils.LogMessages
|
||||||
.OfType<ConfirmUpdateOfExistingInstall>()
|
.OfType<IUserIntervention>()
|
||||||
.Subscribe(msg => ConfirmUpdate(msg));
|
.ObserveOnGuiThread()
|
||||||
|
.SelectTask(msg => UserInterventionHandlers.Handle(msg))
|
||||||
Utils.LogMessages
|
.Subscribe()
|
||||||
.OfType<RequestNexusAuthorization>()
|
.DisposeWith(CompositeDisposable);
|
||||||
.Subscribe(HandleRequestNexusAuthorization);
|
|
||||||
|
|
||||||
if (IsStartingFromModlist(out var path))
|
if (IsStartingFromModlist(out var path))
|
||||||
{
|
{
|
||||||
@ -82,47 +81,6 @@ 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);
|
|
||||||
if (result == MessageBoxResult.OK)
|
|
||||||
msg.Confirm();
|
|
||||||
else
|
|
||||||
msg.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsStartingFromModlist(out string modlistPath)
|
private static bool IsStartingFromModlist(out string modlistPath)
|
||||||
{
|
{
|
||||||
string[] args = Environment.GetCommandLineArgs();
|
string[] args = Environment.GetCommandLineArgs();
|
||||||
@ -144,5 +102,16 @@ namespace Wabbajack
|
|||||||
ActivePane = installer;
|
ActivePane = installer;
|
||||||
installer.ModListLocation.TargetPath = path;
|
installer.ModListLocation.TargetPath = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ShutdownApplication()
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
Settings.PosX = MainWindow.Left;
|
||||||
|
Settings.PosY = MainWindow.Top;
|
||||||
|
Settings.Width = MainWindow.Width;
|
||||||
|
Settings.Height = MainWindow.Height;
|
||||||
|
MainSettings.SaveSettings(Settings);
|
||||||
|
Application.Current.Shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ namespace Wabbajack
|
|||||||
_targetMod = Observable.CombineLatest(
|
_targetMod = Observable.CombineLatest(
|
||||||
modVMs.QueryWhenChanged(),
|
modVMs.QueryWhenChanged(),
|
||||||
selectedIndex,
|
selectedIndex,
|
||||||
resultSelector: (query, selected) => query.Items.ElementAtOrDefault(selected % query.Count))
|
resultSelector: (query, selected) => query.Items.ElementAtOrDefault(selected % (query.Count == 0 ? 1 : query.Count)))
|
||||||
.StartWith(default(ModVM))
|
.StartWith(default(ModVM))
|
||||||
.ObserveOn(RxApp.MainThreadScheduler)
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.ToProperty(this, nameof(TargetMod));
|
.ToProperty(this, nameof(TargetMod));
|
||||||
|
80
Wabbajack/View Models/UserInterventionHandlers.cs
Normal file
80
Wabbajack/View Models/UserInterventionHandlers.cs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
using ReactiveUI;
|
||||||
|
using Wabbajack.Common;
|
||||||
|
using Wabbajack.Lib.Downloaders;
|
||||||
|
using Wabbajack.Lib.NexusApi;
|
||||||
|
|
||||||
|
namespace Wabbajack
|
||||||
|
{
|
||||||
|
public class UserInterventionHandlers
|
||||||
|
{
|
||||||
|
public MainWindowVM MainWindow { get; }
|
||||||
|
|
||||||
|
public UserInterventionHandlers(MainWindowVM mvm)
|
||||||
|
{
|
||||||
|
MainWindow = mvm;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task WrapBrowserJob(IUserIntervention intervention, Func<WebBrowserVM, CancellationTokenSource, Task> toDo)
|
||||||
|
{
|
||||||
|
CancellationTokenSource cancel = new CancellationTokenSource();
|
||||||
|
var oldPane = MainWindow.ActivePane;
|
||||||
|
var vm = new WebBrowserVM();
|
||||||
|
MainWindow.ActivePane = vm;
|
||||||
|
vm.BackCommand = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
cancel.Cancel();
|
||||||
|
MainWindow.ActivePane = oldPane;
|
||||||
|
intervention.Cancel();
|
||||||
|
});
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await toDo(vm, cancel);
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
intervention.Cancel();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Utils.Error(ex);
|
||||||
|
intervention.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow.ActivePane = oldPane;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Handle(IUserIntervention msg)
|
||||||
|
{
|
||||||
|
switch (msg)
|
||||||
|
{
|
||||||
|
case RequestNexusAuthorization c:
|
||||||
|
await WrapBrowserJob(msg, async (vm, cancel) =>
|
||||||
|
{
|
||||||
|
var key = await NexusApiClient.SetupNexusLogin(vm.Browser, m => vm.Instructions = m, cancel.Token);
|
||||||
|
c.Resume(key);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case RequestLoversLabLogin c:
|
||||||
|
await WrapBrowserJob(msg, async (vm, cancel) =>
|
||||||
|
{
|
||||||
|
var data = await LoversLabDownloader.GetAndCacheLoversLabCookies(vm.Browser, m => vm.Instructions = m, cancel.Token);
|
||||||
|
c.Resume(data);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case ConfirmationIntervention c:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"No handler for {msg}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,6 @@ using System.Threading.Tasks;
|
|||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using ReactiveUI.Fody.Helpers;
|
using ReactiveUI.Fody.Helpers;
|
||||||
using Wabbajack.Lib;
|
using Wabbajack.Lib;
|
||||||
using Xilium.CefGlue.Common;
|
|
||||||
using Xilium.CefGlue.WPF;
|
using Xilium.CefGlue.WPF;
|
||||||
|
|
||||||
namespace Wabbajack
|
namespace Wabbajack
|
||||||
@ -16,13 +15,13 @@ namespace Wabbajack
|
|||||||
[Reactive]
|
[Reactive]
|
||||||
public string Instructions { get; set; }
|
public string Instructions { get; set; }
|
||||||
|
|
||||||
public WpfCefBrowser Browser { get; }
|
public WpfCefBrowser Browser { get; } = new WpfCefBrowser();
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public IReactiveCommand BackCommand { get; set; }
|
public IReactiveCommand BackCommand { get; set; }
|
||||||
|
|
||||||
public WebBrowserVM(string url = "http://www.wabbajack.org")
|
public WebBrowserVM(string url = "http://www.wabbajack.org")
|
||||||
{
|
{
|
||||||
Browser = new WpfCefBrowser();
|
|
||||||
Browser.Address = url;
|
Browser.Address = url;
|
||||||
Instructions = "Wabbajack Web Browser";
|
Instructions = "Wabbajack Web Browser";
|
||||||
}
|
}
|
||||||
|
@ -1,44 +1,17 @@
|
|||||||
<UserControl
|
<UserControl
|
||||||
x:Class="Wabbajack.LogCpuView"
|
x:Class="Wabbajack.CpuView"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:local="clr-namespace:Wabbajack"
|
xmlns:local="clr-namespace:Wabbajack"
|
||||||
xmlns:mahapps="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
|
xmlns:mahapps="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
d:DesignHeight="250"
|
d:DesignHeight="450"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
BorderThickness="0"
|
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Rectangle Fill="{StaticResource HeatedBorderBrush}" Opacity="{Binding ProgressPercent, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
<ColumnDefinition Width="5" />
|
|
||||||
<ColumnDefinition Width="500" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<Rectangle
|
|
||||||
Grid.Column="0"
|
|
||||||
Fill="{StaticResource HeatedBorderBrush}"
|
|
||||||
Opacity="{Binding ProgressPercent, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
|
|
||||||
<ListBox
|
<ListBox
|
||||||
Grid.Column="0"
|
|
||||||
local:AutoScrollBehavior.ScrollOnNewItem="True"
|
|
||||||
BorderBrush="Transparent"
|
|
||||||
BorderThickness="1"
|
|
||||||
ItemsSource="{Binding Log}"
|
|
||||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
|
|
||||||
<ListBox.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Text="{Binding ShortDescription}" TextWrapping="WrapWithOverflow" />
|
|
||||||
</DataTemplate>
|
|
||||||
</ListBox.ItemTemplate>
|
|
||||||
</ListBox>
|
|
||||||
<Rectangle
|
|
||||||
Grid.Column="2"
|
|
||||||
Fill="{StaticResource HeatedBorderBrush}"
|
|
||||||
Opacity="{Binding ProgressPercent, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
|
|
||||||
<ListBox
|
|
||||||
Grid.Column="2"
|
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
BorderBrush="Transparent"
|
BorderBrush="Transparent"
|
||||||
BorderThickness="1"
|
BorderThickness="1"
|
@ -1,22 +1,34 @@
|
|||||||
using System.Windows;
|
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.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
|
namespace Wabbajack
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interaction logic for LogCpuView.xaml
|
/// Interaction logic for CpuView.xaml
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class LogCpuView : UserControl
|
public partial class CpuView : UserControl
|
||||||
{
|
{
|
||||||
public double ProgressPercent
|
public double ProgressPercent
|
||||||
{
|
{
|
||||||
get => (double)GetValue(ProgressPercentProperty);
|
get => (double)GetValue(ProgressPercentProperty);
|
||||||
set => SetValue(ProgressPercentProperty, value);
|
set => SetValue(ProgressPercentProperty, value);
|
||||||
}
|
}
|
||||||
public static readonly DependencyProperty ProgressPercentProperty = DependencyProperty.Register(nameof(ProgressPercent), typeof(double), typeof(LogCpuView),
|
public static readonly DependencyProperty ProgressPercentProperty = DependencyProperty.Register(nameof(ProgressPercent), typeof(double), typeof(CpuView),
|
||||||
new FrameworkPropertyMetadata(default(double)));
|
new FrameworkPropertyMetadata(default(double)));
|
||||||
|
|
||||||
public LogCpuView()
|
public CpuView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
26
Wabbajack/Views/Common/LogView.xaml
Normal file
26
Wabbajack/Views/Common/LogView.xaml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<UserControl
|
||||||
|
x:Class="Wabbajack.LogView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="clr-namespace:Wabbajack"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
d:DesignHeight="450"
|
||||||
|
d:DesignWidth="800"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
<Grid>
|
||||||
|
<Rectangle Fill="{StaticResource HeatedBorderBrush}" Opacity="{Binding ProgressPercent, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
|
||||||
|
<ListBox
|
||||||
|
local:AutoScrollBehavior.ScrollOnNewItem="True"
|
||||||
|
BorderBrush="Transparent"
|
||||||
|
BorderThickness="1"
|
||||||
|
ItemsSource="{Binding Log}"
|
||||||
|
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<TextBlock Text="{Binding ShortDescription}" TextWrapping="WrapWithOverflow" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
36
Wabbajack/Views/Common/LogView.xaml.cs
Normal file
36
Wabbajack/Views/Common/LogView.xaml.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
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 LogView.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class LogView : UserControl
|
||||||
|
{
|
||||||
|
public double ProgressPercent
|
||||||
|
{
|
||||||
|
get => (double)GetValue(ProgressPercentProperty);
|
||||||
|
set => SetValue(ProgressPercentProperty, value);
|
||||||
|
}
|
||||||
|
public static readonly DependencyProperty ProgressPercentProperty = DependencyProperty.Register(nameof(ProgressPercent), typeof(double), typeof(LogView),
|
||||||
|
new FrameworkPropertyMetadata(default(double)));
|
||||||
|
|
||||||
|
public LogView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
x:Class="Wabbajack.CompilerView"
|
x:Class="Wabbajack.CompilerView"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:common="clr-namespace:Wabbajack.Common;assembly=Wabbajack.Common"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:icon="http://metro.mahapps.com/winfx/xaml/iconpacks"
|
xmlns:icon="http://metro.mahapps.com/winfx/xaml/iconpacks"
|
||||||
xmlns:local="clr-namespace:Wabbajack"
|
xmlns:local="clr-namespace:Wabbajack"
|
||||||
@ -240,7 +241,24 @@
|
|||||||
Grid.ColumnSpan="5"
|
Grid.ColumnSpan="5"
|
||||||
Margin="5"
|
Margin="5"
|
||||||
Visibility="{Binding Compiling, Converter={StaticResource bool2VisibilityConverter}, FallbackValue=Hidden}">
|
Visibility="{Binding Compiling, Converter={StaticResource bool2VisibilityConverter}, FallbackValue=Hidden}">
|
||||||
<local:LogCpuView ProgressPercent="{Binding PercentCompleted}" />
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="3*" />
|
||||||
|
<ColumnDefinition Width="5" />
|
||||||
|
<ColumnDefinition Width="2*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<local:LogView Grid.Column="0" ProgressPercent="{Binding PercentCompleted, Mode=OneWay}" />
|
||||||
|
<local:CpuView
|
||||||
|
Grid.Column="2"
|
||||||
|
ProgressPercent="{Binding PercentCompleted, Mode=OneWay}"
|
||||||
|
Visibility="{Binding ActiveGlobalUserIntervention, Converter={StaticResource IsNotNullVisibilityConverter}, ConverterParameter=False}" />
|
||||||
|
<Border
|
||||||
|
Grid.Column="2"
|
||||||
|
Style="{StaticResource AttentionBorderStyle}"
|
||||||
|
Visibility="{Binding ActiveGlobalUserIntervention, Converter={StaticResource IsNotNullVisibilityConverter}}">
|
||||||
|
<Grid>
|
||||||
|
<local:ConfirmationInterventionView DataContext="{Binding ActiveGlobalUserIntervention}" Visibility="{Binding ActiveGlobalUserIntervention, Converter={StaticResource IsTypeVisibilityConverter}, ConverterParameter={x:Type common:ConfirmationIntervention}}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
139
Wabbajack/Views/Installers/InstallationCompleteView.xaml
Normal file
139
Wabbajack/Views/Installers/InstallationCompleteView.xaml
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
<UserControl
|
||||||
|
x:Class="Wabbajack.InstallationCompleteView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:icon="http://metro.mahapps.com/winfx/xaml/iconpacks"
|
||||||
|
xmlns:local="clr-namespace:Wabbajack"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
d:DesignHeight="450"
|
||||||
|
d:DesignWidth="800"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
<Border Style="{StaticResource AttentionBorderStyle}" ClipToBounds="True">
|
||||||
|
<Grid Margin="5">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="3*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.ColumnSpan="3"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
FontFamily="Lucida Sans"
|
||||||
|
FontSize="22"
|
||||||
|
FontWeight="Black"
|
||||||
|
Text="Installation Complete">
|
||||||
|
<TextBlock.Effect>
|
||||||
|
<DropShadowEffect BlurRadius="25" Opacity="0.5" />
|
||||||
|
</TextBlock.Effect>
|
||||||
|
</TextBlock>
|
||||||
|
<Grid
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Button
|
||||||
|
Grid.Row="0"
|
||||||
|
Width="55"
|
||||||
|
Height="55"
|
||||||
|
Command="{Binding BackCommand}"
|
||||||
|
Style="{StaticResource CircleButtonStyle}">
|
||||||
|
<icon:PackIconMaterial
|
||||||
|
Width="28"
|
||||||
|
Height="28"
|
||||||
|
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
|
||||||
|
Kind="ArrowLeft" />
|
||||||
|
</Button>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="1"
|
||||||
|
Margin="0,10,0,0"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Text="Main Menu" />
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Visibility="{Binding InstallerSupportsAfterInstallNavigation, Converter={StaticResource bool2VisibilityConverter}}">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Button
|
||||||
|
Width="55"
|
||||||
|
Height="55"
|
||||||
|
Command="{Binding GoToInstallCommand}"
|
||||||
|
Style="{StaticResource CircleButtonStyle}">
|
||||||
|
<icon:PackIconMaterial
|
||||||
|
Width="25"
|
||||||
|
Height="25"
|
||||||
|
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
|
||||||
|
Kind="FolderMove" />
|
||||||
|
</Button>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="1"
|
||||||
|
Margin="0,10,0,0"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Text="Open Install Folder" />
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="2"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Background="Transparent">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<!--<Button
|
||||||
|
Width="55"
|
||||||
|
Height="55"
|
||||||
|
Background="{StaticResource PrimaryVariantBrush}"
|
||||||
|
BorderBrush="{StaticResource PrimaryVariantBrush}"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
Style="{StaticResource CircleButtonStyle}">
|
||||||
|
<Button.Effect>
|
||||||
|
<BlurEffect Radius="35" />
|
||||||
|
</Button.Effect>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
Width="55"
|
||||||
|
Height="55"
|
||||||
|
Background="{StaticResource SecondaryBrush}"
|
||||||
|
BorderBrush="{StaticResource SecondaryBrush}"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
Style="{StaticResource CircleButtonStyle}">
|
||||||
|
<Button.Effect>
|
||||||
|
<BlurEffect Radius="15" />
|
||||||
|
</Button.Effect>
|
||||||
|
</Button>-->
|
||||||
|
<Button
|
||||||
|
Width="55"
|
||||||
|
Height="55"
|
||||||
|
Command="{Binding CloseWhenCompleteCommand}"
|
||||||
|
Style="{StaticResource CircleButtonStyle}">
|
||||||
|
<icon:PackIconMaterial
|
||||||
|
Width="30"
|
||||||
|
Height="30"
|
||||||
|
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
|
||||||
|
Kind="Check" />
|
||||||
|
</Button>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="1"
|
||||||
|
Margin="0,10,0,0"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Text="Close" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</UserControl>
|
28
Wabbajack/Views/Installers/InstallationCompleteView.xaml.cs
Normal file
28
Wabbajack/Views/Installers/InstallationCompleteView.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 InstallationCompleteView.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class InstallationCompleteView : UserControl
|
||||||
|
{
|
||||||
|
public InstallationCompleteView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,8 +2,10 @@
|
|||||||
x:Class="Wabbajack.InstallationView"
|
x:Class="Wabbajack.InstallationView"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:common="clr-namespace:Wabbajack.Common;assembly=Wabbajack.Common"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:icon="http://metro.mahapps.com/winfx/xaml/iconpacks"
|
xmlns:icon="http://metro.mahapps.com/winfx/xaml/iconpacks"
|
||||||
|
xmlns:lib="clr-namespace:Wabbajack.Lib;assembly=Wabbajack.Lib"
|
||||||
xmlns:local="clr-namespace:Wabbajack"
|
xmlns:local="clr-namespace:Wabbajack"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
d:DataContext="{d:DesignInstance local:InstallerVM}"
|
d:DataContext="{d:DesignInstance local:InstallerVM}"
|
||||||
@ -24,7 +26,7 @@
|
|||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="47" />
|
<RowDefinition Height="47" />
|
||||||
<RowDefinition Height="4*" />
|
<RowDefinition Height="4*" />
|
||||||
<RowDefinition Height="*" MinHeight="150" />
|
<RowDefinition Height="*" MinHeight="175" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Rectangle
|
<Rectangle
|
||||||
x:Name="BorderEdgeFadeDown"
|
x:Name="BorderEdgeFadeDown"
|
||||||
@ -287,49 +289,19 @@
|
|||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
VerticalScrollBarVisibility="Auto"
|
VerticalScrollBarVisibility="Auto"
|
||||||
Visibility="{Binding InstallingMode, Converter={StaticResource bool2VisibilityConverter}, ConverterParameter=False}">
|
Visibility="{Binding InstallingMode, Converter={StaticResource bool2VisibilityConverter}, ConverterParameter=False}">
|
||||||
<Grid>
|
<ContentPresenter
|
||||||
<Grid.ColumnDefinitions>
|
Margin="0"
|
||||||
<ColumnDefinition Width="Auto" MinWidth="120" />
|
VerticalAlignment="Center"
|
||||||
<ColumnDefinition Width="20" />
|
Content="{Binding Installer}">
|
||||||
<ColumnDefinition Width="*" />
|
<ContentPresenter.Resources>
|
||||||
</Grid.ColumnDefinitions>
|
<DataTemplate DataType="{x:Type local:MO2InstallerVM}">
|
||||||
<Grid.RowDefinitions>
|
<local:MO2InstallerConfigView />
|
||||||
<RowDefinition Height="*" />
|
</DataTemplate>
|
||||||
<RowDefinition Height="40" />
|
<DataTemplate DataType="{x:Type local:VortexInstallerVM}">
|
||||||
<RowDefinition Height="80" />
|
<local:VortexInstallerConfigView />
|
||||||
<RowDefinition Height="*" />
|
</DataTemplate>
|
||||||
</Grid.RowDefinitions>
|
</ContentPresenter.Resources>
|
||||||
<TextBlock
|
</ContentPresenter>
|
||||||
Grid.Row="1"
|
|
||||||
Grid.Column="0"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
FontSize="14"
|
|
||||||
Text="Target Modlist"
|
|
||||||
TextAlignment="Center" />
|
|
||||||
<local:FilePicker
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.Column="2"
|
|
||||||
Height="30"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
DataContext="{Binding ModListLocation}"
|
|
||||||
FontSize="14" />
|
|
||||||
<ContentPresenter
|
|
||||||
Grid.Row="2"
|
|
||||||
Grid.Column="0"
|
|
||||||
Grid.ColumnSpan="3"
|
|
||||||
Margin="0"
|
|
||||||
Content="{Binding Installer}">
|
|
||||||
<ContentPresenter.Resources>
|
|
||||||
<DataTemplate DataType="{x:Type local:MO2InstallerVM}">
|
|
||||||
<local:MO2InstallerConfigView />
|
|
||||||
</DataTemplate>
|
|
||||||
<DataTemplate DataType="{x:Type local:VortexInstallerVM}">
|
|
||||||
<local:VortexInstallerConfigView />
|
|
||||||
</DataTemplate>
|
|
||||||
</ContentPresenter.Resources>
|
|
||||||
</ContentPresenter>
|
|
||||||
</Grid>
|
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
<local:BeginButton
|
<local:BeginButton
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
@ -343,7 +315,26 @@
|
|||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
Margin="5,0,5,5"
|
Margin="5,0,5,5"
|
||||||
Visibility="{Binding InstallingMode, Converter={StaticResource bool2VisibilityConverter}, FallbackValue=Hidden}">
|
Visibility="{Binding InstallingMode, Converter={StaticResource bool2VisibilityConverter}, FallbackValue=Hidden}">
|
||||||
<local:LogCpuView ProgressPercent="{Binding PercentCompleted, Mode=OneWay}" />
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="3*" />
|
||||||
|
<ColumnDefinition Width="5" />
|
||||||
|
<ColumnDefinition Width="2*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<local:LogView Grid.Column="0" ProgressPercent="{Binding PercentCompleted, Mode=OneWay}" />
|
||||||
|
<local:CpuView
|
||||||
|
Grid.Column="2"
|
||||||
|
ProgressPercent="{Binding PercentCompleted, Mode=OneWay}"
|
||||||
|
Visibility="{Binding ActiveGlobalUserIntervention, Converter={StaticResource IsNotNullVisibilityConverter}, ConverterParameter=False}" />
|
||||||
|
<Border
|
||||||
|
Grid.Column="2"
|
||||||
|
Style="{StaticResource AttentionBorderStyle}"
|
||||||
|
Visibility="{Binding ActiveGlobalUserIntervention, Converter={StaticResource IsNotNullVisibilityConverter}}">
|
||||||
|
<Grid>
|
||||||
|
<local:ConfirmationInterventionView DataContext="{Binding ActiveGlobalUserIntervention}" Visibility="{Binding ActiveGlobalUserIntervention, Converter={StaticResource IsTypeVisibilityConverter}, ConverterParameter={x:Type common:ConfirmationIntervention}}" />
|
||||||
|
<local:ConfirmUpdateOfExistingInstallView Visibility="{Binding ActiveGlobalUserIntervention, Converter={StaticResource IsTypeVisibilityConverter}, ConverterParameter={x:Type lib:ConfirmUpdateOfExistingInstall}}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
<local:InstallationCompleteView Grid.Column="2" Visibility="{Binding Completed, Converter={StaticResource bool2VisibilityConverter}, FallbackValue=Collapsed}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:icon="http://metro.mahapps.com/winfx/xaml/iconpacks"
|
||||||
xmlns:local="clr-namespace:Wabbajack"
|
xmlns:local="clr-namespace:Wabbajack"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
@ -15,11 +16,29 @@
|
|||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="20" />
|
||||||
<RowDefinition Height="40" />
|
<RowDefinition Height="40" />
|
||||||
<RowDefinition Height="40" />
|
<RowDefinition Height="40" />
|
||||||
|
<RowDefinition Height="40" />
|
||||||
|
<RowDefinition Height="20" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="0"
|
Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontSize="14"
|
||||||
|
Text="Target Modlist"
|
||||||
|
TextAlignment="Center" />
|
||||||
|
<local:FilePicker
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="2"
|
||||||
|
Height="30"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
DataContext="{Binding Parent.ModListLocation}"
|
||||||
|
FontSize="14" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="2"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
@ -27,14 +46,14 @@
|
|||||||
Text="Installation Location"
|
Text="Installation Location"
|
||||||
TextAlignment="Center" />
|
TextAlignment="Center" />
|
||||||
<local:FilePicker
|
<local:FilePicker
|
||||||
Grid.Row="0"
|
Grid.Row="2"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Height="30"
|
Height="30"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
DataContext="{Binding Location}"
|
DataContext="{Binding Location}"
|
||||||
FontSize="14" />
|
FontSize="14" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="1"
|
Grid.Row="3"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
@ -42,11 +61,29 @@
|
|||||||
Text="Download Location"
|
Text="Download Location"
|
||||||
TextAlignment="Center" />
|
TextAlignment="Center" />
|
||||||
<local:FilePicker
|
<local:FilePicker
|
||||||
Grid.Row="1"
|
Grid.Row="3"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Height="30"
|
Height="30"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
DataContext="{Binding DownloadLocation}"
|
DataContext="{Binding DownloadLocation}"
|
||||||
FontSize="14" />
|
FontSize="14" />
|
||||||
|
<CheckBox
|
||||||
|
Grid.Row="4"
|
||||||
|
Grid.Column="2"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Content="Overwrite Installation"
|
||||||
|
IsChecked="{Binding AutomaticallyOverwrite}"
|
||||||
|
ToolTip="If installing over an existing installation, automatically replace it without asking permission.">
|
||||||
|
<CheckBox.Style>
|
||||||
|
<Style TargetType="CheckBox">
|
||||||
|
<Setter Property="Opacity" Value="0.6" />
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True">
|
||||||
|
<Setter Property="Opacity" Value="1" />
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</CheckBox.Style>
|
||||||
|
</CheckBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
@ -9,6 +9,28 @@
|
|||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<Grid>
|
<Grid>
|
||||||
<!-- Nothing so far -->
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" MinWidth="120" />
|
||||||
|
<ColumnDefinition Width="20" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="40" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontSize="14"
|
||||||
|
Text="Target Modlist"
|
||||||
|
TextAlignment="Center" />
|
||||||
|
<local:FilePicker
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="2"
|
||||||
|
Height="30"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
DataContext="{Binding Parent.ModListLocation}"
|
||||||
|
FontSize="14" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
<UserControl
|
||||||
|
x:Class="Wabbajack.ConfirmUpdateOfExistingInstallView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="clr-namespace:Wabbajack"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
d:DesignHeight="450"
|
||||||
|
d:DesignWidth="800"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
<Grid Margin="10">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="10" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="5*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="2*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.ColumnSpan="3"
|
||||||
|
Margin="0,0,0,5"
|
||||||
|
FontFamily="Lucida Sans"
|
||||||
|
FontSize="14"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Text="{Binding ActiveGlobalUserIntervention.ShortDescription}"
|
||||||
|
TextWrapping="WrapWithOverflow" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.ColumnSpan="3"
|
||||||
|
Text="{Binding ActiveGlobalUserIntervention.ExtendedDescription}"
|
||||||
|
TextWrapping="WrapWithOverflow" />
|
||||||
|
<CheckBox
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="4"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Content="Remember"
|
||||||
|
IsChecked="{Binding Installer.AutomaticallyOverwrite}"
|
||||||
|
ToolTip="If installing over an existing installation next time, automatically replace it without asking permission." />
|
||||||
|
<Button
|
||||||
|
Grid.Row="3"
|
||||||
|
Grid.Column="0"
|
||||||
|
Command="{Binding ActiveGlobalUserIntervention.CancelCommand}"
|
||||||
|
Content="Cancel" />
|
||||||
|
<Button
|
||||||
|
Grid.Row="3"
|
||||||
|
Grid.Column="2"
|
||||||
|
Command="{Binding ActiveGlobalUserIntervention.ConfirmCommand}"
|
||||||
|
Content="Confirm" />
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
@ -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 ConfirmUpdateOfExistingInstallView.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class ConfirmUpdateOfExistingInstallView : UserControl
|
||||||
|
{
|
||||||
|
public ConfirmUpdateOfExistingInstallView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
<UserControl
|
||||||
|
x:Class="Wabbajack.ConfirmationInterventionView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="clr-namespace:Wabbajack"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
d:DesignHeight="450"
|
||||||
|
d:DesignWidth="800"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
<Grid Margin="10">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="10" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="4*" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.ColumnSpan="3"
|
||||||
|
Margin="0,0,0,5"
|
||||||
|
FontFamily="Lucida Sans"
|
||||||
|
FontSize="14"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Text="{Binding ShortDescription}"
|
||||||
|
TextWrapping="WrapWithOverflow" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.ColumnSpan="3"
|
||||||
|
Text="{Binding ExtendedDescription}"
|
||||||
|
TextWrapping="WrapWithOverflow" />
|
||||||
|
<Button
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="0"
|
||||||
|
Command="{Binding CancelCommand}"
|
||||||
|
Content="Cancel" />
|
||||||
|
<Button
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="2"
|
||||||
|
Command="{Binding ConfirmCommand}"
|
||||||
|
Content="Confirm" />
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
@ -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 ConfirmationInterventionView.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class ConfirmationInterventionView : UserControl
|
||||||
|
{
|
||||||
|
public ConfirmationInterventionView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,8 +15,6 @@ namespace Wabbajack
|
|||||||
private MainWindowVM _mwvm;
|
private MainWindowVM _mwvm;
|
||||||
private MainSettings _settings;
|
private MainSettings _settings;
|
||||||
|
|
||||||
internal bool ExitWhenClosing = true;
|
|
||||||
|
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
// Wire any unhandled crashing exceptions to log before exiting
|
// Wire any unhandled crashing exceptions to log before exiting
|
||||||
@ -93,16 +91,7 @@ namespace Wabbajack
|
|||||||
|
|
||||||
private void Window_Closing(object sender, CancelEventArgs e)
|
private void Window_Closing(object sender, CancelEventArgs e)
|
||||||
{
|
{
|
||||||
_mwvm.Dispose();
|
_mwvm.ShutdownApplication();
|
||||||
_settings.PosX = Left;
|
|
||||||
_settings.PosY = Top;
|
|
||||||
_settings.Width = Width;
|
|
||||||
_settings.Height = Height;
|
|
||||||
MainSettings.SaveSettings(_settings);
|
|
||||||
if (ExitWhenClosing)
|
|
||||||
{
|
|
||||||
Application.Current.Shutdown();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,7 +172,21 @@
|
|||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</ApplicationDefinition>
|
</ApplicationDefinition>
|
||||||
|
<Compile Include="Converters\IsTypeVisibilityConverter.cs" />
|
||||||
|
<Compile Include="Views\Installers\InstallationCompleteView.xaml.cs">
|
||||||
|
<DependentUpon>InstallationCompleteView.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Views\Interventions\ConfirmationInterventionView.xaml.cs">
|
||||||
|
<DependentUpon>ConfirmationInterventionView.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Converters\EqualsToBoolConverter.cs" />
|
<Compile Include="Converters\EqualsToBoolConverter.cs" />
|
||||||
|
<Compile Include="Views\Common\CpuView.xaml.cs">
|
||||||
|
<DependentUpon>CpuView.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Views\Common\LogView.xaml.cs">
|
||||||
|
<DependentUpon>LogView.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="View Models\UserInterventionHandlers.cs" />
|
||||||
<Compile Include="View Models\WebBrowserVM.cs" />
|
<Compile Include="View Models\WebBrowserVM.cs" />
|
||||||
<Compile Include="Views\Installers\MO2InstallerConfigView.xaml.cs">
|
<Compile Include="Views\Installers\MO2InstallerConfigView.xaml.cs">
|
||||||
<DependentUpon>MO2InstallerConfigView.xaml</DependentUpon>
|
<DependentUpon>MO2InstallerConfigView.xaml</DependentUpon>
|
||||||
@ -185,6 +199,9 @@
|
|||||||
<Compile Include="Views\Common\HeatedBackgroundView.xaml.cs">
|
<Compile Include="Views\Common\HeatedBackgroundView.xaml.cs">
|
||||||
<DependentUpon>HeatedBackgroundView.xaml</DependentUpon>
|
<DependentUpon>HeatedBackgroundView.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Views\Interventions\ConfirmUpdateOfExistingInstallView.xaml.cs">
|
||||||
|
<DependentUpon>ConfirmUpdateOfExistingInstallView.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Views\LinksView.xaml.cs">
|
<Compile Include="Views\LinksView.xaml.cs">
|
||||||
<DependentUpon>LinksView.xaml</DependentUpon>
|
<DependentUpon>LinksView.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
@ -233,9 +250,6 @@
|
|||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="View Models\Compilers\CompilerVM.cs" />
|
<Compile Include="View Models\Compilers\CompilerVM.cs" />
|
||||||
<Compile Include="View Models\MainWindowVM.cs" />
|
<Compile Include="View Models\MainWindowVM.cs" />
|
||||||
<Compile Include="Views\Common\LogCpuView.xaml.cs">
|
|
||||||
<DependentUpon>LogCpuView.xaml</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Converters\LeftMarginMultiplierConverter.cs" />
|
<Compile Include="Converters\LeftMarginMultiplierConverter.cs" />
|
||||||
<Compile Include="Util\TreeViewItemExtensions.cs" />
|
<Compile Include="Util\TreeViewItemExtensions.cs" />
|
||||||
<Compile Include="View Models\SlideShow.cs" />
|
<Compile Include="View Models\SlideShow.cs" />
|
||||||
@ -255,6 +269,22 @@
|
|||||||
<Compile Include="Views\WebBrowserView.xaml.cs">
|
<Compile Include="Views\WebBrowserView.xaml.cs">
|
||||||
<DependentUpon>WebBrowserView.xaml</DependentUpon>
|
<DependentUpon>WebBrowserView.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Page Include="Views\Installers\InstallationCompleteView.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
<Page Include="Views\Interventions\ConfirmationInterventionView.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
<Page Include="Views\Common\CpuView.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
<Page Include="Views\Common\LogView.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
<Page Include="Views\Installers\MO2InstallerConfigView.xaml">
|
<Page Include="Views\Installers\MO2InstallerConfigView.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
@ -263,6 +293,10 @@
|
|||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
|
<Page Include="Views\Interventions\ConfirmUpdateOfExistingInstallView.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
<Page Include="Views\LinksView.xaml">
|
<Page Include="Views\LinksView.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
@ -303,10 +337,6 @@
|
|||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
<Page Include="Views\Common\LogCpuView.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
</Page>
|
|
||||||
<Page Include="Views\MainWindow.xaml">
|
<Page Include="Views\MainWindow.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
@ -433,7 +463,7 @@
|
|||||||
<Version>4.1.0</Version>
|
<Version>4.1.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="DynamicData">
|
<PackageReference Include="DynamicData">
|
||||||
<Version>6.13.21</Version>
|
<Version>6.14.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Fody">
|
<PackageReference Include="Fody">
|
||||||
<Version>6.0.5</Version>
|
<Version>6.0.5</Version>
|
||||||
@ -470,13 +500,13 @@
|
|||||||
<Version>2.4.4</Version>
|
<Version>2.4.4</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="ReactiveUI.Events.WPF">
|
<PackageReference Include="ReactiveUI.Events.WPF">
|
||||||
<Version>10.5.30</Version>
|
<Version>11.0.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="ReactiveUI.Fody">
|
<PackageReference Include="ReactiveUI.Fody">
|
||||||
<Version>10.5.30</Version>
|
<Version>11.0.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="ReactiveUI.WPF">
|
<PackageReference Include="ReactiveUI.WPF">
|
||||||
<Version>10.5.30</Version>
|
<Version>11.0.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="SharpCompress">
|
<PackageReference Include="SharpCompress">
|
||||||
<Version>0.24.0</Version>
|
<Version>0.24.0</Version>
|
||||||
@ -488,7 +518,7 @@
|
|||||||
<Version>1.2.1</Version>
|
<Version>1.2.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="System.Reactive">
|
<PackageReference Include="System.Reactive">
|
||||||
<Version>4.2.0</Version>
|
<Version>4.3.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="WebSocketSharpFork">
|
<PackageReference Include="WebSocketSharpFork">
|
||||||
<Version>1.0.4</Version>
|
<Version>1.0.4</Version>
|
||||||
@ -521,5 +551,8 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Resource Include="Resources\Wabba_Ded.png" />
|
<Resource Include="Resources\Wabba_Ded.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<SplashScreen Include="Resources\Wabba_Mouth_Small.png" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
Loading…
x
Reference in New Issue
Block a user