mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
parent
c5dae97060
commit
1fad0f1d73
@ -1,5 +1,11 @@
|
||||
### Changelog
|
||||
|
||||
#### Version - 3.7.2.0 - 8/25/2024
|
||||
* Added a new button to the installer configuration window for verifying installs. This runs the same code as the verify CLI command, now it's in the UI for easier access. The output of this command
|
||||
is written to a `.html` file and opened in the default browser.
|
||||
* When a modlist install fails due to one or more missing non-nexus files, the installer will now write a `.html` file with all the links and instructions, and open it with the default browser. This data was
|
||||
previsoously only written to the log file.
|
||||
|
||||
#### Version - 3.7.1.1 - 8/13/2024
|
||||
* HOTFIX: buggy release pipeline caused some corruption in the files of 3.7.1.0
|
||||
|
||||
|
@ -35,6 +35,9 @@ using Wabbajack.Paths.IO;
|
||||
using Wabbajack.Services.OSIntegrated;
|
||||
using Wabbajack.Util;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Wabbajack.CLI.Verbs;
|
||||
using Wabbajack.VFS;
|
||||
|
||||
namespace Wabbajack;
|
||||
|
||||
@ -151,6 +154,8 @@ public class InstallerVM : BackNavigatingVM, IBackNavigatingVM, ICpuStatusVM
|
||||
public ReactiveCommand<Unit, Unit> GoToInstallCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> BeginCommand { get; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> VerifyCommand { get; }
|
||||
|
||||
public InstallerVM(ILogger<InstallerVM> logger, DTOSerializer dtos, SettingsManager settingsManager, IServiceProvider serviceProvider,
|
||||
SystemParametersConstructor parametersConstructor, IGameLocator gameLocator, LogStream loggerProvider, ResourceMonitor resourceMonitor,
|
||||
Wabbajack.Services.OSIntegrated.Configuration configuration, HttpClient client, DownloadDispatcher dispatcher, IEnumerable<INeedsLogin> logins,
|
||||
@ -176,6 +181,8 @@ public class InstallerVM : BackNavigatingVM, IBackNavigatingVM, ICpuStatusVM
|
||||
|
||||
BeginCommand = ReactiveCommand.Create(() => BeginInstall().FireAndForget());
|
||||
|
||||
VerifyCommand = ReactiveCommand.Create(() => Verify().FireAndForget());
|
||||
|
||||
OpenReadmeCommand = ReactiveCommand.Create(() =>
|
||||
{
|
||||
UIUtils.OpenWebsite(new Uri(ModList!.Readme));
|
||||
@ -455,6 +462,37 @@ public class InstallerVM : BackNavigatingVM, IBackNavigatingVM, ICpuStatusVM
|
||||
Installer.Location.TargetPath = prev;
|
||||
}
|
||||
|
||||
private async Task Verify()
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
InstallState = InstallState.Installing;
|
||||
|
||||
StatusText = $"Verifying {ModList.Name}";
|
||||
|
||||
|
||||
var cmd = new VerifyModlistInstall(_serviceProvider.GetRequiredService<ILogger<VerifyModlistInstall>>(), _dtos,
|
||||
_serviceProvider.GetRequiredService<IResource<FileHashCache>>(),
|
||||
_serviceProvider.GetRequiredService<TemporaryFileManager>());
|
||||
|
||||
var result = await cmd.Run(ModListLocation.TargetPath, Installer.Location.TargetPath, _cancellationToken);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
TaskBarUpdate.Send($"Error during verification of {ModList.Name}", TaskbarItemProgressState.Error);
|
||||
InstallState = InstallState.Failure;
|
||||
StatusText = $"Error during install of {ModList.Name}";
|
||||
StatusProgress = Percent.Zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskBarUpdate.Send($"Finished verification of {ModList.Name}", TaskbarItemProgressState.Normal);
|
||||
InstallState = InstallState.Success;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private async Task BeginInstall()
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
|
@ -82,6 +82,7 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<local:BeginButton Grid.Row="1"
|
||||
x:Name="BeginButton"
|
||||
@ -121,6 +122,13 @@
|
||||
</Style>
|
||||
</CheckBox.Style>
|
||||
</CheckBox>
|
||||
<Button Grid.Row="3" Grid.Column="2"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Bottom"
|
||||
x:Name="VerifyButton"
|
||||
Content="Verify Installation"
|
||||
ToolTip="Scan the output folders, creating a report on any corrupt or missing files.">
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</rxui:ReactiveUserControl>
|
||||
|
@ -39,6 +39,9 @@ namespace Wabbajack
|
||||
this.WhenAny(x => x.ViewModel.BeginCommand)
|
||||
.BindToStrict(this, x => x.BeginButton.Command)
|
||||
.DisposeWith(dispose);
|
||||
this.WhenAny(x => x.ViewModel.VerifyCommand)
|
||||
.BindToStrict(this, x => x.VerifyButton.Command)
|
||||
.DisposeWith(dispose);
|
||||
this.BindStrict(ViewModel, vm => vm.OverwriteFiles, x => x.OverwriteCheckBox.IsChecked)
|
||||
.DisposeWith(dispose);
|
||||
|
||||
@ -49,6 +52,11 @@ namespace Wabbajack
|
||||
.BindToStrict(this, view => view.BeginButton.IsEnabled)
|
||||
.DisposeWith(dispose);
|
||||
|
||||
this.WhenAnyValue(x => x.ViewModel.ErrorState)
|
||||
.Select(v => !v.Failed)
|
||||
.BindToStrict(this, view => view.VerifyButton.IsEnabled)
|
||||
.DisposeWith(dispose);
|
||||
|
||||
this.WhenAnyValue(x => x.ViewModel.ErrorState)
|
||||
.Select(v => v.Reason)
|
||||
.BindToStrict(this, view => view.errorTextBox.Text)
|
||||
|
@ -106,6 +106,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Wabbajack.CLI.Builder\Wabbajack.CLI.Builder.csproj" />
|
||||
<ProjectReference Include="..\Wabbajack.CLI\Wabbajack.CLI.csproj" />
|
||||
<ProjectReference Include="..\Wabbajack.Services.OSIntegrated\Wabbajack.Services.OSIntegrated.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -26,11 +28,12 @@ public class VerifyModlistInstall
|
||||
private readonly DTOSerializer _dtos;
|
||||
private readonly ILogger<VerifyModlistInstall> _logger;
|
||||
|
||||
public VerifyModlistInstall(ILogger<VerifyModlistInstall> logger, DTOSerializer dtos, IResource<FileHashCache> limiter)
|
||||
public VerifyModlistInstall(ILogger<VerifyModlistInstall> logger, DTOSerializer dtos, IResource<FileHashCache> limiter, TemporaryFileManager temporaryFileManager)
|
||||
{
|
||||
_limiter = limiter;
|
||||
_logger = logger;
|
||||
_dtos = dtos;
|
||||
_temporaryFileManager = temporaryFileManager;
|
||||
}
|
||||
|
||||
public static VerbDefinition Definition = new("verify-modlist-install", "Verify a modlist installed correctly",
|
||||
@ -42,6 +45,7 @@ public class VerifyModlistInstall
|
||||
});
|
||||
|
||||
private readonly IResource<FileHashCache> _limiter;
|
||||
private readonly TemporaryFileManager _temporaryFileManager;
|
||||
|
||||
|
||||
public async Task<int> Run(AbsolutePath modlistLocation, AbsolutePath installFolder, CancellationToken token)
|
||||
@ -52,6 +56,8 @@ public class VerifyModlistInstall
|
||||
_logger.LogInformation("Indexing files");
|
||||
var byTo = list.Directives.ToDictionary(d => d.To);
|
||||
|
||||
var reportFile = _temporaryFileManager.CreateFile(Ext.Html);
|
||||
|
||||
|
||||
_logger.LogInformation("Scanning files");
|
||||
var errors = await list.Directives.PMapAllBatchedAsync(_limiter, async directive =>
|
||||
@ -68,7 +74,7 @@ public class VerifyModlistInstall
|
||||
return new Result
|
||||
{
|
||||
Path = directive.To,
|
||||
Message = $"File does not exist directive {directive.GetType()}"
|
||||
Message = $"File does not exist"
|
||||
};
|
||||
}
|
||||
|
||||
@ -112,11 +118,30 @@ public class VerifyModlistInstall
|
||||
_logger.LogInformation("Found {Count} errors", errors.Count);
|
||||
|
||||
|
||||
|
||||
{
|
||||
await using var stream = new StreamWriter(reportFile.Path.Open(FileMode.Create, FileAccess.Write));
|
||||
await stream.WriteLineAsync(
|
||||
"<html><head></head><body>");
|
||||
await stream.WriteLineAsync($"<h1>Verification Report for {modlistLocation}</h1>");
|
||||
|
||||
foreach (var error in errors)
|
||||
{
|
||||
if (error == null) continue;
|
||||
await stream.WriteLineAsync($"<div class=\"error\">{error.Message} | {error.Path}</div>");
|
||||
_logger.LogError("{File} | {Message}", error.Path, error.Message);
|
||||
}
|
||||
|
||||
await stream.WriteLineAsync("</body></html>");
|
||||
}
|
||||
|
||||
_logger.LogInformation("Report written to {Report}", reportFile.Path);
|
||||
|
||||
Process.Start(new ProcessStartInfo("cmd.exe", $"/c start {reportFile}")
|
||||
{
|
||||
CreateNoWindow = true,
|
||||
});
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@ -22,6 +23,7 @@ using Wabbajack.DTOs;
|
||||
using Wabbajack.DTOs.BSA.FileStates;
|
||||
using Wabbajack.DTOs.Directives;
|
||||
using Wabbajack.DTOs.DownloadStates;
|
||||
using Wabbajack.DTOs.Interventions;
|
||||
using Wabbajack.DTOs.JsonConverters;
|
||||
using Wabbajack.Hashing.PHash;
|
||||
using Wabbajack.Hashing.xxHash64;
|
||||
@ -123,6 +125,12 @@ public class StandardInstaller : AInstaller<StandardInstaller>
|
||||
var missing = ModList.Archives.Where(a => !HashedArchives.ContainsKey(a.Hash)).ToList();
|
||||
if (missing.Count > 0)
|
||||
{
|
||||
if (missing.Any(m => m.State is not Nexus))
|
||||
{
|
||||
ShowMissingManualReport(missing.Where(m => m.State is not Nexus).ToArray());
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var a in missing)
|
||||
_logger.LogCritical("Unable to download {name} ({primaryKeyString})", a.Name,
|
||||
a.State.PrimaryKeyString);
|
||||
@ -168,6 +176,46 @@ public class StandardInstaller : AInstaller<StandardInstaller>
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ShowMissingManualReport(Archive[] toArray)
|
||||
{
|
||||
_logger.LogError("Writing Manual helper report");
|
||||
var report = _configuration.Downloads.Combine("MissingManuals.html");
|
||||
{
|
||||
using var writer = new StreamWriter(report.Open(FileMode.Create, FileAccess.Write, FileShare.None));
|
||||
writer.Write("<html><head><title>Missing Manual Downloads</title></head><body>");
|
||||
writer.Write("<h1>Missing Manual Downloads</h1>");
|
||||
writer.Write(
|
||||
"<p>Wabbajack was unable to download the following archives automaticall. Please download them manually and place them in the downloads folder you chose during the install setup.</p>");
|
||||
foreach (var archive in toArray)
|
||||
{
|
||||
switch (archive.State)
|
||||
{
|
||||
case Manual manual:
|
||||
writer.Write($"<h3>{archive.Name}</h1>");
|
||||
writer.Write($"<p>{manual.Prompt}</p>");
|
||||
writer.Write($"<p>Download URL: <a href=\"{manual.Url}\">{manual.Url}</a></p>");
|
||||
break;
|
||||
case MediaFire mediaFire:
|
||||
writer.Write($"<h3>{archive.Name}</h1>");
|
||||
writer.Write($"<p>Download URL: <a href=\"{mediaFire.Url}\">{mediaFire.Url}</a></p>");
|
||||
break;
|
||||
default:
|
||||
writer.Write($"<h3>{archive.Name}</h1>");
|
||||
writer.Write($"<p>Unknown download type</p>");
|
||||
writer.Write($"<p>Primary Key (may not be helpful): <a href=\"{archive.State.PrimaryKeyString}\">{archive.State.PrimaryKeyString}</a></p>");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
writer.Write("</body></html>");
|
||||
}
|
||||
|
||||
Process.Start(new ProcessStartInfo("cmd.exe", $"/c start {report}")
|
||||
{
|
||||
CreateNoWindow = true,
|
||||
});
|
||||
}
|
||||
|
||||
private Task RemapMO2File()
|
||||
{
|
||||
var iniFile = _configuration.Install.Combine("ModOrganizer.ini");
|
||||
|
Loading…
Reference in New Issue
Block a user