Compare commits

...

46 Commits

Author SHA1 Message Date
Timothy Baldridge
1fad0f1d73
nice-to-haves (#2609)
* Manual install report

* 3.7.2.0
2024-08-25 15:21:16 -06:00
halgari
c5dae97060 3.7.1.1 2024-08-13 15:46:32 -06:00
halgari
931de94230 3.7.1.0 release 2024-08-13 14:43:16 -06:00
trawzified
4cc08cfc0a
Merge pull request #2596 from wabbajack-tools/AidanMaskelyne-pr-branch
Use the default file manager when opening a folder.
2024-08-13 22:33:35 +02:00
trawzified
f58b6190c6 Update Changelog 2024-08-13 22:27:48 +02:00
trawzified
dee84004f5
Merge branch 'main' into AidanMaskelyne-pr-branch 2024-08-13 22:24:22 +02:00
trawzified
75a07ea050
Merge pull request #2606 from wabbajack-tools/clickyfix
Fix for crash when right clicking or doubler clicking browser url or window bar.
2024-08-13 22:23:53 +02:00
trawzified
8281e77247 Update changelog 2024-08-13 22:13:56 +02:00
trawzified
fa4d9ff60e
Merge branch 'main' into AidanMaskelyne-pr-branch 2024-08-13 22:10:49 +02:00
trawzified
278e06139e
Merge branch 'main' into clickyfix 2024-08-13 21:57:58 +02:00
trawzified
ec36b8ab4a
Merge pull request #2604 from wabbajack-tools/fo3-bsa-fix
Fix FO3 / FNV / Skyrim LE files with special characters in the path corrupting when being packed
2024-08-11 23:28:03 +02:00
trawzified
0ffe0ca8f4 Update changelog 2024-08-11 23:22:30 +02:00
trawzified
dfeec6b235 Fix FO3/FNV/LE BSA file paths with special characters getting corrupted when packed into BSAs 2024-08-11 23:13:43 +02:00
trawzified
c3e8621de6 Add logging to determine faulty file on hashing errors 2024-07-20 11:58:04 +02:00
JanuarySnow
d911eab6d8 redundant includes 2024-07-17 14:04:02 +01:00
JanuarySnow
1c5726245a added sanity checks for dragmove for actual button being pressed 2024-07-17 14:03:05 +01:00
Luca
a4f3782eb9
Merge pull request #2592 from AidanMaskelyne/main
Use the default file manager when opening a folder.
2024-07-08 22:30:05 +02:00
Aidan M
c8bce3a849 Change String to string to align more with overall style 2024-07-05 12:13:31 +01:00
Aidan M
2d72370c32 Remove accidentally added import 2024-07-03 20:55:24 +01:00
Aidan M
22fcd4b5c8 Add check to ensure it is definitely a folder being opened, and modify other hard-coded explorer.exe reference 2024-07-03 20:53:35 +01:00
Aidan M
fdce8e2f1b Use default Windows file handler 2024-07-03 20:24:50 +01:00
halgari
5cc6d866ea 3.7.0.0 2024-06-21 13:15:22 -06:00
trawzified
a545cb375a
Add Starfield support (#2589)
* Add support for new Starfield BA2 versions

* Add Starfield BA2 LZ4 texture compression support (WIP, not finished)

* Work on replacing DDS.cs with DirectXTexUtility

* Fix reading Starfield BA2s with new DirectXTexUtil

* Fix builder not exporting new Starfield header options

* Fix writing LZ4 chunks in frame format instead of block format

* Rename FO4Archive to BA2Archive, merge SFArchive and FO4Archive

* Clean up testing code & CLI launch settings

* Update changelog
2024-06-17 12:10:53 -06:00
Luca
59b2f1a7a1
Merge pull request #2586 from wabbajack-tools/EzioTheDeadPoet-patch-1
Update tests.yaml
2024-06-11 15:15:28 +02:00
Luca
81d92e6d7b
Update tests.yaml 2024-06-11 15:10:15 +02:00
Luca
d41940aada
Merge pull request #2574 from wabbajack-tools/webview2-dep-update
Update webview 2
2024-06-06 17:23:57 +02:00
EzioTheDeadPoet
ef22b2c389 update CHANGELOG.md 2024-06-06 17:13:17 +02:00
EzioTheDeadPoet
94f780c6a7 webview update 2024-06-06 17:11:57 +02:00
Luca
ae640f48b2
Merge pull request #2573 from wabbajack-tools/dependency-updates
update a number of dependencies
2024-06-06 17:03:02 +02:00
EzioTheDeadPoet
a96aef7edd update CHANGELOG.md 2024-06-06 16:51:39 +02:00
EzioTheDeadPoet
5e0d6ccb82 update multiple dependencies 2024-06-06 16:48:29 +02:00
Luca
781cdb3848
Merge pull request #2572 from wabbajack-tools/gamefinder-update
Update GameFinder to the latest version
2024-06-06 16:36:06 +02:00
EzioTheDeadPoet
8544a6b2e8 update other required packages 2024-06-06 16:18:56 +02:00
EzioTheDeadPoet
09457d6cb7 update GameFinder dependency 2024-06-06 16:10:23 +02:00
halgari
d9920fefdc 3.6.1.1 2024-05-30 17:07:34 -06:00
halgari
749baf1bdf Merge remote-tracking branch 'origin/main'
# Conflicts:
#	Wabbajack.CLI/Verbs/SetNexusApiKey.cs
2024-05-30 17:04:17 -06:00
trawzified
cd03d2991f
OAuth Nexus fixes (#2569)
* Missing a lock in the validate method that could cause refresh to trigger a lot of times asynchronously

* Fix refreshing Nexus OAuth token not working properly

* Fix accidental deadlock on NexusApi.Validate(), add extra validation logging
2024-05-30 17:03:31 -06:00
Luca
f956943f45
Merge pull request #2512 from wabbajack-tools/dependabot/nuget/Orc.FileAssociation-5.0.0
Bump Orc.FileAssociation from 5.0.0-alpha0061 to 5.0.0
2024-05-28 19:50:06 +02:00
Luca
a6e6758333
Merge branch 'main' into dependabot/nuget/Orc.FileAssociation-5.0.0 2024-05-28 19:35:07 +02:00
Luca
9aac41cf69
Merge pull request #2566 from wabbajack-tools/oauth-related-fixes
Fix CLI login command
2024-05-28 19:30:33 +02:00
EzioTheDeadPoet
720a3b1f45 update changelog 2024-05-28 19:20:09 +02:00
EzioTheDeadPoet
1b949b538a fix SetNexusApiKey CLI command 2024-05-28 18:46:47 +02:00
EzioTheDeadPoet
12409ababb refactor interface name to the naming convention for interfaces 2024-05-28 18:45:03 +02:00
EzioTheDeadPoet
a1da63984b update dependency 2024-05-28 18:41:18 +02:00
halgari
37a5b7ce28 Revert a few changes that broke the build 2024-05-26 21:33:31 -06:00
dependabot[bot]
7cf03886b7
Bump Orc.FileAssociation from 5.0.0-alpha0061 to 5.0.0
Bumps [Orc.FileAssociation](https://github.com/wildgums/orc.FileAssociation) from 5.0.0-alpha0061 to 5.0.0.
- [Changelog](https://github.com/WildGums/Orc.FileAssociation/blob/develop/GitReleaseManager.yaml)
- [Commits](https://github.com/wildgums/orc.FileAssociation/commits/5.0.0)

---
updated-dependencies:
- dependency-name: Orc.FileAssociation
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-22 03:54:21 +00:00
66 changed files with 1791 additions and 694 deletions

View File

@ -7,7 +7,7 @@ on:
branches: [ main ] branches: [ main ]
env: env:
VERSION: 3.6.0.0 VERSION: 3.6.1.1
jobs: jobs:
build: build:

View File

@ -1,5 +1,33 @@
### Changelog ### 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
#### Version - 3.7.1.0 - 8/13/2024
* Fixed file paths with special characters corrupting when packed into BSAs
* This issue only affected Fallout 3, Fallout NV and Skyrim LE
* Added logging to determine which downloaded files cannot be hashed
* This could occur in the downloading phase when installing a modlist when there are broken/corrupted files in the downloads folder
* Fixed Wabbajack crashing when double-clicking the browser window titlebar or URL
* Fixed Wabbajack always using explorer.exe instead of the default file browser
#### Version - 3.7.0.0 - 6/21/2024
* Added Starfield support
* Note: Hashes were added earlier, but the earlier version was not fully compatible due to Wabbajack extracting the BA2 archives incorrectly. This has been fixed.
* Updated GameFinder dependency
* Updated WebView dependency
* Updated other dependencies
#### Version - 3.6.1.1 - 5/30/2024
* Fixed `set-nexus-api-key` CLI command
* Fixed other issues related to OAuth
#### Version - 3.6.1.0 - 5/26/2024 #### Version - 3.6.1.0 - 5/26/2024
* Fixed a race condition on renewing Nexus Mods OAuth2 tokens * Fixed a race condition on renewing Nexus Mods OAuth2 tokens
* Added `set-nexus-api-key` CLI command * Added `set-nexus-api-key` CLI command

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\Publish</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net8.0-windows</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>false</PublishSingleFile>
<PublishReadyToRun>false</PublishReadyToRun>
</PropertyGroup>
</Project>

View File

@ -56,7 +56,6 @@ namespace Wabbajack
} }
} }
public static void OpenWebsite(Uri url) public static void OpenWebsite(Uri url)
{ {
Process.Start(new ProcessStartInfo("cmd.exe", $"/c start {url}") Process.Start(new ProcessStartInfo("cmd.exe", $"/c start {url}")
@ -64,16 +63,23 @@ namespace Wabbajack
CreateNoWindow = true, CreateNoWindow = true,
}); });
} }
public static void OpenFolder(AbsolutePath path) public static void OpenFolder(AbsolutePath path)
{ {
Process.Start(new ProcessStartInfo(KnownFolders.Windows.Combine("explorer.exe").ToString(), path.ToString()) string folderPath = path.ToString();
if (!folderPath.EndsWith(Path.DirectorySeparatorChar.ToString()))
{ {
CreateNoWindow = true, folderPath += Path.DirectorySeparatorChar.ToString();
}
Process.Start(new ProcessStartInfo()
{
FileName = folderPath,
UseShellExecute = true,
Verb = "open"
}); });
} }
public static AbsolutePath OpenFileDialog(string filter, string initialDirectory = null) public static AbsolutePath OpenFileDialog(string filter, string initialDirectory = null)
{ {
OpenFileDialog ofd = new OpenFileDialog(); OpenFileDialog ofd = new OpenFileDialog();

View File

@ -35,6 +35,9 @@ using Wabbajack.Paths.IO;
using Wabbajack.Services.OSIntegrated; using Wabbajack.Services.OSIntegrated;
using Wabbajack.Util; using Wabbajack.Util;
using System.Windows.Forms; using System.Windows.Forms;
using Microsoft.Extensions.DependencyInjection;
using Wabbajack.CLI.Verbs;
using Wabbajack.VFS;
namespace Wabbajack; namespace Wabbajack;
@ -151,6 +154,8 @@ public class InstallerVM : BackNavigatingVM, IBackNavigatingVM, ICpuStatusVM
public ReactiveCommand<Unit, Unit> GoToInstallCommand { get; } public ReactiveCommand<Unit, Unit> GoToInstallCommand { get; }
public ReactiveCommand<Unit, Unit> BeginCommand { get; } public ReactiveCommand<Unit, Unit> BeginCommand { get; }
public ReactiveCommand<Unit, Unit> VerifyCommand { get; }
public InstallerVM(ILogger<InstallerVM> logger, DTOSerializer dtos, SettingsManager settingsManager, IServiceProvider serviceProvider, public InstallerVM(ILogger<InstallerVM> logger, DTOSerializer dtos, SettingsManager settingsManager, IServiceProvider serviceProvider,
SystemParametersConstructor parametersConstructor, IGameLocator gameLocator, LogStream loggerProvider, ResourceMonitor resourceMonitor, SystemParametersConstructor parametersConstructor, IGameLocator gameLocator, LogStream loggerProvider, ResourceMonitor resourceMonitor,
Wabbajack.Services.OSIntegrated.Configuration configuration, HttpClient client, DownloadDispatcher dispatcher, IEnumerable<INeedsLogin> logins, Wabbajack.Services.OSIntegrated.Configuration configuration, HttpClient client, DownloadDispatcher dispatcher, IEnumerable<INeedsLogin> logins,
@ -175,6 +180,8 @@ public class InstallerVM : BackNavigatingVM, IBackNavigatingVM, ICpuStatusVM
BackCommand = ReactiveCommand.Create(() => NavigateToGlobal.Send(NavigateToGlobal.ScreenType.ModeSelectionView)); BackCommand = ReactiveCommand.Create(() => NavigateToGlobal.Send(NavigateToGlobal.ScreenType.ModeSelectionView));
BeginCommand = ReactiveCommand.Create(() => BeginInstall().FireAndForget()); BeginCommand = ReactiveCommand.Create(() => BeginInstall().FireAndForget());
VerifyCommand = ReactiveCommand.Create(() => Verify().FireAndForget());
OpenReadmeCommand = ReactiveCommand.Create(() => OpenReadmeCommand = ReactiveCommand.Create(() =>
{ {
@ -455,6 +462,37 @@ public class InstallerVM : BackNavigatingVM, IBackNavigatingVM, ICpuStatusVM
Installer.Location.TargetPath = prev; 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() private async Task BeginInstall()
{ {
await Task.Run(async () => await Task.Run(async () =>

View File

@ -77,7 +77,7 @@ namespace Wabbajack
public void AfterInstallNavigation() public void AfterInstallNavigation()
{ {
Process.Start("explorer.exe", Location.TargetPath.ToString()); UIUtils.OpenFolder(Location.TargetPath);
} }
public async Task<bool> Install() public async Task<bool> Install()

View File

@ -25,7 +25,6 @@ public partial class BrowserWindow : MetroWindow
{ {
InitializeComponent(); InitializeComponent();
_disposable = new CompositeDisposable(); _disposable = new CompositeDisposable();
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
Browser = _serviceProvider.GetRequiredService<WebView2>(); Browser = _serviceProvider.GetRequiredService<WebView2>();
@ -43,7 +42,10 @@ public partial class BrowserWindow : MetroWindow
private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e) private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{ {
base.DragMove(); if (e.LeftButton == MouseButtonState.Pressed)
{
base.DragMove();
}
} }
private void BrowserWindow_OnActivated(object sender, EventArgs e) private void BrowserWindow_OnActivated(object sender, EventArgs e)

View File

@ -82,6 +82,7 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<local:BeginButton Grid.Row="1" <local:BeginButton Grid.Row="1"
x:Name="BeginButton" x:Name="BeginButton"
@ -104,12 +105,12 @@
Foreground="{StaticResource WarningBrush}" Foreground="{StaticResource WarningBrush}"
Kind="ExclamationTriangleSolid" /> Kind="ExclamationTriangleSolid" />
<CheckBox Grid.Row="2" Grid.Column="2" <CheckBox Grid.Row="2" Grid.Column="2"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
x:Name="OverwriteCheckBox" x:Name="OverwriteCheckBox"
Content="Overwrite Installation" Content="Overwrite Installation"
IsChecked="False" IsChecked="False"
ToolTip="Confirm to overwrite files in install folder."> ToolTip="Confirm to overwrite files in install folder.">
<CheckBox.Style> <CheckBox.Style>
<Style TargetType="CheckBox"> <Style TargetType="CheckBox">
<Setter Property="Opacity" Value="0.6" /> <Setter Property="Opacity" Value="0.6" />
@ -121,6 +122,13 @@
</Style> </Style>
</CheckBox.Style> </CheckBox.Style>
</CheckBox> </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>
</Grid> </Grid>
</rxui:ReactiveUserControl> </rxui:ReactiveUserControl>

View File

@ -39,6 +39,9 @@ namespace Wabbajack
this.WhenAny(x => x.ViewModel.BeginCommand) this.WhenAny(x => x.ViewModel.BeginCommand)
.BindToStrict(this, x => x.BeginButton.Command) .BindToStrict(this, x => x.BeginButton.Command)
.DisposeWith(dispose); .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) this.BindStrict(ViewModel, vm => vm.OverwriteFiles, x => x.OverwriteCheckBox.IsChecked)
.DisposeWith(dispose); .DisposeWith(dispose);
@ -48,6 +51,11 @@ namespace Wabbajack
.Select(v => !v.Failed) .Select(v => !v.Failed)
.BindToStrict(this, view => view.BeginButton.IsEnabled) .BindToStrict(this, view => view.BeginButton.IsEnabled)
.DisposeWith(dispose); .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) this.WhenAnyValue(x => x.ViewModel.ErrorState)
.Select(v => v.Reason) .Select(v => v.Reason)

View File

@ -144,7 +144,10 @@ namespace Wabbajack
private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e) private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{ {
this.DragMove(); if (e.LeftButton == MouseButtonState.Pressed)
{
this.DragMove();
}
} }
} }

View File

@ -90,22 +90,23 @@
<PackageReference Include="MahApps.Metro" Version="2.4.10" /> <PackageReference Include="MahApps.Metro" Version="2.4.10" />
<PackageReference Include="MahApps.Metro.IconPacks" Version="4.11.0" /> <PackageReference Include="MahApps.Metro.IconPacks" Version="4.11.0" />
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.5" /> <PackageReference Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.5" />
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="8.0.5" /> <PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="8.0.6" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2478.35" /> <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2535.41" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.3.5" /> <PackageReference Include="NLog.Extensions.Logging" Version="5.3.5" />
<PackageReference Include="Orc.FileAssociation" Version="5.0.0-alpha0061" /> <PackageReference Include="Orc.FileAssociation" Version="5.0.0" />
<PackageReference Include="PInvoke.User32" Version="0.7.124" /> <PackageReference Include="PInvoke.User32" Version="0.7.124" />
<PackageReference Include="ReactiveUI" Version="19.5.1" /> <PackageReference Include="ReactiveUI" Version="19.5.1" />
<PackageReference Include="ReactiveUI.Fody" Version="19.5.1" /> <PackageReference Include="ReactiveUI.Fody" Version="19.5.1" />
<PackageReference Include="ReactiveUI.WPF" Version="19.5.1" /> <PackageReference Include="ReactiveUI.WPF" Version="19.5.1" />
<PackageReference Include="Silk.NET.DXGI" Version="2.18.0" /> <PackageReference Include="Silk.NET.DXGI" Version="2.21.0" />
<PackageReference Include="System.Reactive" Version="6.0.1-preview.1" /> <PackageReference Include="System.Reactive" Version="6.0.1-preview.1" />
<PackageReference Include="WPFThemes.DarkBlend" Version="1.0.8" /> <PackageReference Include="WPFThemes.DarkBlend" Version="1.0.8" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Wabbajack.CLI.Builder\Wabbajack.CLI.Builder.csproj" /> <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" /> <ProjectReference Include="..\Wabbajack.Services.OSIntegrated\Wabbajack.Services.OSIntegrated.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -8,7 +8,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />

View File

@ -0,0 +1,7 @@
{
"profiles": {
"Wabbajack.CLI": {
"commandName": "Project"
}
}
}

View File

@ -1,29 +1,23 @@
using System.CommandLine;
using System.CommandLine.Invocation;
using System.CommandLine.NamingConventionBinder;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Wabbajack.CLI.Builder; using Wabbajack.CLI.Builder;
using Wabbajack.DTOs.Logins; using Wabbajack.DTOs.Logins;
using Wabbajack.Paths;
using Wabbajack.Paths.IO;
using Wabbajack.Services.OSIntegrated; using Wabbajack.Services.OSIntegrated;
namespace Wabbajack.CLI.Verbs; namespace Wabbajack.CLI.Verbs;
public class SetNexusApiKey public class SetNexusApiKey
{ {
private readonly EncryptedJsonTokenProvider<NexusApiState> _tokenProvider; private readonly EncryptedJsonTokenProvider<NexusOAuthState> _tokenProvider;
private readonly ILogger<SetNexusApiKey> _logger; private readonly ILogger<SetNexusApiKey> _logger;
public SetNexusApiKey(EncryptedJsonTokenProvider<NexusApiState> tokenProvider, ILogger<SetNexusApiKey> logger) public SetNexusApiKey(EncryptedJsonTokenProvider<NexusOAuthState> tokenProvider, ILogger<SetNexusApiKey> logger)
{ {
_tokenProvider = tokenProvider; _tokenProvider = tokenProvider;
_logger = logger; _logger = logger;
} }
public static VerbDefinition Definition = new VerbDefinition("set-nexus-api-key", public static VerbDefinition Definition = new("set-nexus-api-key",
"Sets the Nexus API key to the specified value", "Sets the Nexus API key to the specified value",
[ [
new OptionDefinition(typeof(string), "k", "key", "The Nexus API key") new OptionDefinition(typeof(string), "k", "key", "The Nexus API key")
@ -38,7 +32,7 @@ public class SetNexusApiKey
} }
else else
{ {
await _tokenProvider.SetToken(new NexusApiState { ApiKey = key }); await _tokenProvider.SetToken(new() { ApiKey = key });
_logger.LogInformation("Set Nexus API Key to {key}", key); _logger.LogInformation("Set Nexus API Key to {key}", key);
return 0; return 0;
} }

View File

@ -1,5 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -26,11 +28,12 @@ public class VerifyModlistInstall
private readonly DTOSerializer _dtos; private readonly DTOSerializer _dtos;
private readonly ILogger<VerifyModlistInstall> _logger; 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; _limiter = limiter;
_logger = logger; _logger = logger;
_dtos = dtos; _dtos = dtos;
_temporaryFileManager = temporaryFileManager;
} }
public static VerbDefinition Definition = new("verify-modlist-install", "Verify a modlist installed correctly", 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 IResource<FileHashCache> _limiter;
private readonly TemporaryFileManager _temporaryFileManager;
public async Task<int> Run(AbsolutePath modlistLocation, AbsolutePath installFolder, CancellationToken token) public async Task<int> Run(AbsolutePath modlistLocation, AbsolutePath installFolder, CancellationToken token)
@ -52,7 +56,9 @@ public class VerifyModlistInstall
_logger.LogInformation("Indexing files"); _logger.LogInformation("Indexing files");
var byTo = list.Directives.ToDictionary(d => d.To); var byTo = list.Directives.ToDictionary(d => d.To);
var reportFile = _temporaryFileManager.CreateFile(Ext.Html);
_logger.LogInformation("Scanning files"); _logger.LogInformation("Scanning files");
var errors = await list.Directives.PMapAllBatchedAsync(_limiter, async directive => var errors = await list.Directives.PMapAllBatchedAsync(_limiter, async directive =>
{ {
@ -68,7 +74,7 @@ public class VerifyModlistInstall
return new Result return new Result
{ {
Path = directive.To, 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); _logger.LogInformation("Found {Count} errors", errors.Count);
foreach (var error in errors)
{ {
_logger.LogError("{File} | {Message}", error.Path, error.Message); 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; return 0;
} }

View File

@ -8,24 +8,23 @@
<PackageLicenseExpression>GPL-3.0-or-later</PackageLicenseExpression> <PackageLicenseExpression>GPL-3.0-or-later</PackageLicenseExpression>
<Version>$(VERSION)</Version> <Version>$(VERSION)</Version>
<AssemblyName>wabbajack-cli</AssemblyName> <AssemblyName>wabbajack-cli</AssemblyName>
<PublishTrimmed>false</PublishTrimmed> <PublishTrimmed>true</PublishTrimmed>
<TimeMode>linked</TimeMode> <TimeMode>linked</TimeMode>
<NoWarn>CS8600</NoWarn> <NoWarn>CS8600</NoWarn>
<NoWarn>CS8601</NoWarn> <NoWarn>CS8601</NoWarn>
<NoWarn>CS8618</NoWarn> <NoWarn>CS8618</NoWarn>
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault> <JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageReference Include="NLog" Version="5.2.5" /> <PackageReference Include="NLog" Version="5.2.5" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.3.5" /> <PackageReference Include="NLog.Extensions.Logging" Version="5.3.5" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.2" /> <PackageReference Include="SixLabors.ImageSharp" Version="3.1.4" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" /> <PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageReference Include="System.CommandLine.NamingConventionBinder" Version="2.0.0-beta4.22272.1" /> <PackageReference Include="System.CommandLine.NamingConventionBinder" Version="2.0.0-beta4.22272.1" />
</ItemGroup> </ItemGroup>

View File

@ -14,13 +14,13 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.6.1" /> <PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

View File

@ -55,6 +55,7 @@ public class CompressionTests
{ {
if (name == "tes4.bsa") return; // not sure why is is failing if (name == "tes4.bsa") return; // not sure why is is failing
var reader = await BSADispatch.Open(path); var reader = await BSADispatch.Open(path);
var dataStates = await reader.Files var dataStates = await reader.Files
@ -78,7 +79,6 @@ public class CompressionTests
var rebuiltStream = new MemoryStream(); var rebuiltStream = new MemoryStream();
await build.Build(rebuiltStream, CancellationToken.None); await build.Build(rebuiltStream, CancellationToken.None);
rebuiltStream.Position = 0;
var reader2 = await BSADispatch.Open(new MemoryStreamFactory(rebuiltStream, path, path.LastModifiedUtc())); var reader2 = await BSADispatch.Open(new MemoryStreamFactory(rebuiltStream, path, path.LastModifiedUtc()));
await reader.Files.Zip(reader2.Files) await reader.Files.Zip(reader2.Files)

View File

@ -12,15 +12,15 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.6.1" /> <PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="Xunit.DependencyInjection" Version="8.9.0" /> <PackageReference Include="Xunit.DependencyInjection" Version="9.3.0" />
<PackageReference Include="Xunit.DependencyInjection.Logging" Version="8.1.0" /> <PackageReference Include="Xunit.DependencyInjection.Logging" Version="8.1.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

View File

@ -10,7 +10,7 @@ using Wabbajack.DTOs.BSA.ArchiveStates;
using Wabbajack.DTOs.BSA.FileStates; using Wabbajack.DTOs.BSA.FileStates;
using Wabbajack.Paths.IO; using Wabbajack.Paths.IO;
namespace Wabbajack.Compression.BSA.FO4Archive; namespace Wabbajack.Compression.BSA.BA2Archive;
public class Builder : IBuilder public class Builder : IBuilder
{ {
@ -33,7 +33,7 @@ public class Builder : IBuilder
break; break;
case BA2EntryType.DX10: case BA2EntryType.DX10:
var resultdx10 = await DX10FileEntryBuilder.Create((BA2DX10File)state, src, _slab, token); var resultdx10 = await DX10FileEntryBuilder.Create((BA2DX10File)state, src, _slab, _state.Compression == 3, token);
lock (_entries) lock (_entries)
{ {
_entries.Add(resultdx10); _entries.Add(resultdx10);
@ -59,6 +59,13 @@ public class Builder : IBuilder
bw.Write((uint) _entries.Count); bw.Write((uint) _entries.Count);
var tableOffsetLoc = bw.BaseStream.Position; var tableOffsetLoc = bw.BaseStream.Position;
bw.Write((ulong) 0); bw.Write((ulong) 0);
if(_state.Version == 2 || _state.Version == 3)
{
bw.Write(_state.Unknown1);
bw.Write(_state.Unknown2);
if (_state.Version == 3)
bw.Write(_state.Compression);
}
foreach (var entry in _entries) entry.WriteHeader(bw, token); foreach (var entry in _entries) entry.WriteHeader(bw, token);

View File

@ -0,0 +1,93 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using K4os.Compression.LZ4;
using K4os.Compression.LZ4.Encoders;
using Wabbajack.Common;
using Wabbajack.DTOs.BSA.FileStates;
using Wabbajack.DTOs.GitHub;
namespace Wabbajack.Compression.BSA.BA2Archive;
public class ChunkBuilder
{
private BA2Chunk _chunk;
private Stream _dataSlab;
private long _offsetOffset;
private uint _packSize;
public static async Task<ChunkBuilder> Create(BA2DX10File state, BA2Chunk chunk, Stream src,
DiskSlabAllocator slab, bool useLz4Compression, CancellationToken token)
{
var builder = new ChunkBuilder {_chunk = chunk};
if (!chunk.Compressed)
{
builder._dataSlab = slab.Allocate(chunk.FullSz);
await src.CopyToLimitAsync(builder._dataSlab, (int) chunk.FullSz, token);
}
else
{
if (!useLz4Compression)
{
var deflater = new Deflater(Deflater.BEST_COMPRESSION);
await using var ms = new MemoryStream();
await using (var ds = new DeflaterOutputStream(ms, deflater))
{
ds.IsStreamOwner = false;
await src.CopyToLimitAsync(ds, (int)chunk.FullSz, token);
}
builder._dataSlab = slab.Allocate(ms.Length);
ms.Position = 0;
await ms.CopyToLimitAsync(builder._dataSlab, (int)ms.Length, token);
builder._packSize = (uint)ms.Length;
}
else
{
byte[] full = new byte[chunk.FullSz];
await using (var copyStream = new MemoryStream())
{
await src.CopyToLimitAsync(copyStream, (int)chunk.FullSz, token);
full = copyStream.ToArray();
}
var maxOutput = LZ4Codec.MaximumOutputSize((int)chunk.FullSz);
byte[] compressed = new byte[maxOutput];
int compressedSize = LZ4Codec.Encode(full, 0, full.Length, compressed, 0, compressed.Length, LZ4Level.L12_MAX);
var ms = new MemoryStream(compressed, 0, compressedSize);
builder._dataSlab = slab.Allocate(compressedSize);
ms.Position = 0;
await ms.CopyToLimitAsync(builder._dataSlab, compressedSize, token);
builder._packSize = (uint)compressedSize;
}
}
builder._dataSlab.Position = 0;
return builder;
}
public void WriteHeader(BinaryWriter bw)
{
_offsetOffset = bw.BaseStream.Position;
bw.Write((ulong) 0);
bw.Write(_packSize);
bw.Write(_chunk.FullSz);
bw.Write(_chunk.StartMip);
bw.Write(_chunk.EndMip);
bw.Write(_chunk.Align);
}
public async ValueTask WriteData(BinaryWriter bw, CancellationToken token)
{
var pos = bw.BaseStream.Position;
bw.BaseStream.Position = _offsetOffset;
bw.Write((ulong) pos);
bw.BaseStream.Position = pos;
await _dataSlab.CopyToLimitAsync(bw.BaseStream, (int) _dataSlab.Length, token);
await _dataSlab.DisposeAsync();
}
}

View File

@ -0,0 +1,179 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using DirectXTex;
using ICSharpCode.SharpZipLib.Zip.Compression;
using K4os.Compression.LZ4;
using K4os.Compression.LZ4.Streams;
using Wabbajack.Common;
using Wabbajack.Compression.BSA.BA2Archive;
using Wabbajack.DTOs.BSA.FileStates;
using Wabbajack.DTOs.Streams;
using Wabbajack.Paths;
namespace Wabbajack.Compression.BSA.BA2Archive;
public class DX10Entry : IBA2FileEntry
{
private readonly Reader _bsa;
private ushort _chunkHdrLen;
private List<TextureChunk> _chunks;
private uint _dirHash;
private string _extension;
private byte _format;
private ushort _height;
private int _index;
private uint _nameHash;
private byte _numChunks;
private byte _numMips;
private ushort _unk16;
private byte _unk8;
private ushort _width;
private readonly byte _isCubemap;
private readonly byte _tileMode;
public DX10Entry(Reader ba2Reader, int idx)
{
_bsa = ba2Reader;
var _rdr = ba2Reader._rdr;
_nameHash = _rdr.ReadUInt32();
FullPath = _nameHash.ToString("X");
_extension = Encoding.UTF8.GetString(_rdr.ReadBytes(4));
_dirHash = _rdr.ReadUInt32();
_unk8 = _rdr.ReadByte();
_numChunks = _rdr.ReadByte();
_chunkHdrLen = _rdr.ReadUInt16();
_height = _rdr.ReadUInt16();
_width = _rdr.ReadUInt16();
_numMips = _rdr.ReadByte();
_format = _rdr.ReadByte();
_isCubemap = _rdr.ReadByte();
_tileMode = _rdr.ReadByte();
_index = idx;
_chunks = Enumerable.Range(0, _numChunks)
.Select(_ => new TextureChunk(_rdr))
.ToList();
}
private DirectXTexUtility.TexMetadata? _metadata = null;
public DirectXTexUtility.TexMetadata Metadata
{
get
{
if (_metadata == null)
_metadata = DirectXTexUtility.GenerateMetadata(_width, _height, _numMips, (DirectXTexUtility.DXGIFormat)_format, _isCubemap == 1);
return (DirectXTexUtility.TexMetadata)_metadata;
}
}
private uint _headerSize = 0;
public uint HeaderSize
{
get
{
if (_headerSize > 0)
return _headerSize;
uint size = 0;
size += (uint)Marshal.SizeOf(DirectXTexUtility.DDSHeader.DDSMagic);
size += (uint)Marshal.SizeOf<DirectXTexUtility.DDSHeader>();
var pixelFormat = DirectXTexUtility.GetPixelFormat(Metadata);
var hasDx10Header = DirectXTexUtility.HasDx10Header(pixelFormat);
if (hasDx10Header)
size += (uint)Marshal.SizeOf<DirectXTexUtility.DX10Header>();
return _headerSize = size;
}
}
public string FullPath { get; set; }
public RelativePath Path => FullPath.ToRelativePath();
public uint Size => (uint)_chunks.Sum(f => f._fullSz) + HeaderSize;
public AFile State => new BA2DX10File
{
Path = Path,
NameHash = _nameHash,
Extension = _extension,
DirHash = _dirHash,
Unk8 = _unk8,
ChunkHdrLen = _chunkHdrLen,
Height = _height,
Width = _width,
NumMips = _numMips,
PixelFormat = _format,
IsCubeMap = _isCubemap,
TileMode = _tileMode,
Index = _index,
Chunks = _chunks.Select(ch => new BA2Chunk
{
FullSz = ch._fullSz,
StartMip = ch._startMip,
EndMip = ch._endMip,
Align = ch._align,
Compressed = ch._packSz != 0
}).ToArray()
};
public async ValueTask CopyDataTo(Stream output, CancellationToken token)
{
var bw = new BinaryWriter(output);
WriteHeader(bw);
await using var fs = await _bsa._streamFactory.GetStream();
using var br = new BinaryReader(fs);
foreach (var chunk in _chunks)
{
var full = new byte[chunk._fullSz];
var isCompressed = chunk._packSz != 0;
br.BaseStream.Seek((long)chunk._offset, SeekOrigin.Begin);
if (!isCompressed)
{
await br.BaseStream.ReadAsync(full, token);
}
else
{
var compressed = new byte[chunk._packSz];
await br.BaseStream.ReadAsync(compressed, token);
if (_bsa._compression == 3)
{
LZ4Codec.PartialDecode(compressed, full);
}
else
{
var inflater = new Inflater();
inflater.SetInput(compressed);
inflater.Inflate(full);
}
}
await bw.BaseStream.WriteAsync(full, token);
}
}
public async ValueTask<IStreamFactory> GetStreamFactory(CancellationToken token)
{
var ms = new MemoryStream();
await CopyDataTo(ms, token);
ms.Position = 0;
return new MemoryStreamFactory(ms, Path, _bsa._streamFactory.LastModifiedUtc);
}
private void WriteHeader(BinaryWriter bw)
{
DirectXTexUtility.GenerateDDSHeader(Metadata, DirectXTexUtility.DDSFlags.FORCEDX10EXTMISC2, out var header, out var header10);
var headerBytes = DirectXTexUtility.EncodeDDSHeader(header, header10);
bw.Write(headerBytes);
}
}

View File

@ -1,18 +1,20 @@
using DirectXTex;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Compression.BSA;
using Wabbajack.DTOs.BSA.FileStates; using Wabbajack.DTOs.BSA.FileStates;
using Wabbajack.DTOs.Texture; using Wabbajack.DTOs.Texture;
namespace Wabbajack.Compression.BSA.FO4Archive; namespace Wabbajack.Compression.BSA.BA2Archive;
public class DX10FileEntryBuilder : IFileBuilder public class DX10FileEntryBuilder : IFileBuilder
{ {
private List<ChunkBuilder> _chunks; private List<ChunkBuilder> _chunks;
private BA2DX10File _state; private BA2DX10File _state;
private uint _headerSize = 0;
public uint FileHash => _state.NameHash; public uint FileHash => _state.NameHash;
public uint DirHash => _state.DirHash; public uint DirHash => _state.DirHash;
@ -43,20 +45,36 @@ public class DX10FileEntryBuilder : IFileBuilder
foreach (var chunk in _chunks) foreach (var chunk in _chunks)
await chunk.WriteData(wtr, token); await chunk.WriteData(wtr, token);
} }
public uint GetHeaderSize(BA2DX10File state)
{
if (_headerSize > 0)
return _headerSize;
public static async Task<DX10FileEntryBuilder> Create(BA2DX10File state, Stream src, DiskSlabAllocator slab, uint size = 0;
size += (uint)Marshal.SizeOf(DirectXTexUtility.DDSHeader.DDSMagic);
size += (uint)Marshal.SizeOf<DirectXTexUtility.DDSHeader>();
var metadata = DirectXTexUtility.GenerateMetadata(state.Width, state.Height, state.NumMips, (DirectXTexUtility.DXGIFormat)state.PixelFormat, state.IsCubeMap == 1);
var pixelFormat = DirectXTexUtility.GetPixelFormat(metadata);
var hasDx10Header = DirectXTexUtility.HasDx10Header(pixelFormat);
if (hasDx10Header)
size += (uint)Marshal.SizeOf<DirectXTexUtility.DX10Header>();
return _headerSize = size;
}
public static async Task<DX10FileEntryBuilder> Create(BA2DX10File state, Stream src, DiskSlabAllocator slab, bool useLz4Compression,
CancellationToken token) CancellationToken token)
{ {
var builder = new DX10FileEntryBuilder {_state = state}; var builder = new DX10FileEntryBuilder {_state = state};
var headerSize = DDS.HeaderSizeForFormat((DXGI_FORMAT) state.PixelFormat) + 4; var headerSize = builder.GetHeaderSize(state);
new BinaryReader(src).ReadBytes((int) headerSize); new BinaryReader(src).ReadBytes((int) headerSize);
// This can't be parallel because it all runs off the same base IO stream. // This can't be parallel because it all runs off the same base IO stream.
builder._chunks = new List<ChunkBuilder>(); builder._chunks = new List<ChunkBuilder>();
foreach (var chunk in state.Chunks) foreach (var chunk in state.Chunks)
builder._chunks.Add(await ChunkBuilder.Create(state, chunk, src, slab, token)); builder._chunks.Add(await ChunkBuilder.Create(state, chunk, src, slab, useLz4Compression, token));
return builder; return builder;
} }

View File

@ -9,7 +9,7 @@ using Wabbajack.DTOs.BSA.FileStates;
using Wabbajack.DTOs.Streams; using Wabbajack.DTOs.Streams;
using Wabbajack.Paths; using Wabbajack.Paths;
namespace Wabbajack.Compression.BSA.FO4Archive; namespace Wabbajack.Compression.BSA.BA2Archive;
public class FileEntry : IBA2FileEntry public class FileEntry : IBA2FileEntry
{ {

View File

@ -6,7 +6,7 @@ using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using Wabbajack.Common; using Wabbajack.Common;
using Wabbajack.DTOs.BSA.FileStates; using Wabbajack.DTOs.BSA.FileStates;
namespace Wabbajack.Compression.BSA.FO4Archive; namespace Wabbajack.Compression.BSA.BA2Archive;
public class FileEntryBuilder : IFileBuilder public class FileEntryBuilder : IFileBuilder
{ {

View File

@ -1,6 +1,6 @@
using Wabbajack.Compression.BSA.Interfaces; using Wabbajack.Compression.BSA.Interfaces;
namespace Wabbajack.Compression.BSA.FO4Archive; namespace Wabbajack.Compression.BSA.BA2Archive;
internal interface IBA2FileEntry : IFile internal interface IBA2FileEntry : IFile
{ {

View File

@ -2,7 +2,7 @@ using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Wabbajack.Compression.BSA.FO4Archive; namespace Wabbajack.Compression.BSA.BA2Archive;
internal interface IFileBuilder internal interface IFileBuilder
{ {

View File

@ -7,7 +7,7 @@ using Wabbajack.Compression.BSA.Interfaces;
using Wabbajack.DTOs.BSA.ArchiveStates; using Wabbajack.DTOs.BSA.ArchiveStates;
using Wabbajack.DTOs.Streams; using Wabbajack.DTOs.Streams;
namespace Wabbajack.Compression.BSA.FO4Archive; namespace Wabbajack.Compression.BSA.BA2Archive;
public class Reader : IReader public class Reader : IReader
{ {
@ -15,9 +15,17 @@ public class Reader : IReader
internal string _headerMagic; internal string _headerMagic;
internal ulong _nameTableOffset; internal ulong _nameTableOffset;
internal uint _numFiles; internal uint _numFiles;
internal uint _unknown1;
internal uint _unknown2;
internal uint _compression;
internal BinaryReader _rdr; internal BinaryReader _rdr;
public IStreamFactory _streamFactory; public IStreamFactory _streamFactory;
internal BA2EntryType _type; internal BA2EntryType _type;
/// <summary>
/// Fallout 4 - Version 1, 7 or 8
/// Starfield - Version 2 or 3
/// </summary>
internal uint _version; internal uint _version;
private Reader(Stream stream) private Reader(Stream stream)
@ -37,7 +45,10 @@ public class Reader : IReader
Version = _version, Version = _version,
HeaderMagic = _headerMagic, HeaderMagic = _headerMagic,
Type = _type, Type = _type,
HasNameTable = HasNameTable HasNameTable = HasNameTable,
Unknown1 = _unknown1,
Unknown2 = _unknown2,
Compression = _compression,
}; };
@ -67,6 +78,10 @@ public class Reader : IReader
_numFiles = _rdr.ReadUInt32(); _numFiles = _rdr.ReadUInt32();
_nameTableOffset = _rdr.ReadUInt64(); _nameTableOffset = _rdr.ReadUInt64();
_unknown1 = (_version == 2 || _version == 3) ? _rdr.ReadUInt32() : 0;
_unknown2 = (_version == 2 || _version == 3) ? _rdr.ReadUInt32() : 0;
_compression = (_version == 3) ? _rdr.ReadUInt32() : 0;
var files = new List<IBA2FileEntry>(); var files = new List<IBA2FileEntry>();
for (var idx = 0; idx < _numFiles; idx += 1) for (var idx = 0; idx < _numFiles; idx += 1)
switch (_type) switch (_type)
@ -94,4 +109,4 @@ public class Reader : IReader
return Task.CompletedTask; return Task.CompletedTask;
} }
} }

View File

@ -1,6 +1,6 @@
using System.IO; using System.IO;
namespace Wabbajack.Compression.BSA.FO4Archive; namespace Wabbajack.Compression.BSA.BA2Archive;
public class TextureChunk public class TextureChunk
{ {

View File

@ -22,7 +22,7 @@ public static class BSADispatch
{ {
FileType.TES3 => await Reader.Load(new NativeFileStreamFactory(filename)), FileType.TES3 => await Reader.Load(new NativeFileStreamFactory(filename)),
FileType.BSA => await TES5Archive.Reader.Load(new NativeFileStreamFactory(filename)), FileType.BSA => await TES5Archive.Reader.Load(new NativeFileStreamFactory(filename)),
FileType.BA2 => await FO4Archive.Reader.Load(new NativeFileStreamFactory(filename)), FileType.BA2 => await BA2Archive.Reader.Load(new NativeFileStreamFactory(filename)),
_ => throw new InvalidDataException("Filename is not a .bsa or .ba2") _ => throw new InvalidDataException("Filename is not a .bsa or .ba2")
}; };
} }
@ -34,7 +34,7 @@ public static class BSADispatch
{ {
FileType.TES3 => await Reader.Load(factory), FileType.TES3 => await Reader.Load(factory),
FileType.BSA => await TES5Archive.Reader.Load(factory), FileType.BSA => await TES5Archive.Reader.Load(factory),
FileType.BA2 => await FO4Archive.Reader.Load(factory), FileType.BA2 => await BA2Archive.Reader.Load(factory),
_ => throw new InvalidDataException("Filename is not a .bsa or .ba2") _ => throw new InvalidDataException("Filename is not a .bsa or .ba2")
}; };
} }
@ -46,7 +46,7 @@ public static class BSADispatch
{ {
FileType.TES3 => await Reader.Load(factory), FileType.TES3 => await Reader.Load(factory),
FileType.BSA => await TES5Archive.Reader.Load(factory), FileType.BSA => await TES5Archive.Reader.Load(factory),
FileType.BA2 => await FO4Archive.Reader.Load(factory), FileType.BA2 => await BA2Archive.Reader.Load(factory),
_ => throw new InvalidDataException("Filename is not a .bsa or .ba2") _ => throw new InvalidDataException("Filename is not a .bsa or .ba2")
}; };
} }
@ -57,7 +57,7 @@ public static class BSADispatch
{ {
TES3State tes3 => new Builder(tes3), TES3State tes3 => new Builder(tes3),
BSAState bsa => TES5Archive.Builder.Create(bsa, manager), BSAState bsa => TES5Archive.Builder.Create(bsa, manager),
BA2State ba2 => FO4Archive.Builder.Create(ba2, manager), BA2State ba2 => BA2Archive.Builder.Create(ba2, manager),
_ => throw new NotImplementedException() _ => throw new NotImplementedException()
}; };
} }

View File

@ -23,6 +23,7 @@ public static class BinaryHelperExtensions
return version switch return version switch
{ {
VersionType.TES3 => Encoding.ASCII, VersionType.TES3 => Encoding.ASCII,
VersionType.FO3 => Encoding.UTF8,
VersionType.SSE => Windows1252, VersionType.SSE => Windows1252,
_ => Encoding.UTF7 _ => Encoding.UTF7
}; };

View File

@ -1,215 +0,0 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using Wabbajack.DTOs.Texture;
namespace Compression.BSA;
/*
* Copied from https://raw.githubusercontent.com/AlexxEG/BSA_Browser/master/Sharp.BSA.BA2/BA2Util/DDS.cs
* which is also GPL3 code. Modified slightly for Wabbajack
*
*/
/*
* Copied from dds.h. Includes (almost) only stuff I need in this project.
*
* Link: https://github.com/digitalutopia1/BA2Lib/blob/master/BA2Lib/dds.h
*
*/
public class DDS
{
public const int DDS_MAGIC = 0x20534444; // "DDS "
public const int DDS_FOURCC = 0x00000004; // DDPF_FOURCC
public const int DDS_RGB = 0x00000040; // DDPF_RGB
public const int DDS_RGBA = 0x00000041; // DDPF_RGB | DDPF_ALPHAPIXELS
public const int
DDS_HEADER_FLAGS_TEXTURE = 0x00001007; // DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT
public const int DDS_HEADER_FLAGS_MIPMAP = 0x00020000; // DDSD_MIPMAPCOUNT
public const int DDS_HEADER_FLAGS_LINEARSIZE = 0x00080000; // DDSD_LINEARSIZE
public const int DDS_SURFACE_FLAGS_TEXTURE = 0x00001000; // DDSCAPS_TEXTURE
public const int DDS_SURFACE_FLAGS_MIPMAP = 0x00400008; // DDSCAPS_COMPLEX | DDSCAPS_MIPMAP
public const int DDS_ALPHA_MODE_UNKNOWN = 0x0;
public static uint HeaderSizeForFormat(DXGI_FORMAT fmt)
{
switch (fmt)
{
case DXGI_FORMAT.BC1_UNORM_SRGB:
case DXGI_FORMAT.BC3_UNORM_SRGB:
case DXGI_FORMAT.BC4_UNORM:
case DXGI_FORMAT.BC5_SNORM:
case DXGI_FORMAT.BC6H_UF16:
case DXGI_FORMAT.BC7_UNORM:
case DXGI_FORMAT.BC7_UNORM_SRGB:
return DDS_HEADER_DXT10.Size + DDS_HEADER.Size;
default:
return DDS_HEADER.Size;
}
}
public static uint MAKEFOURCC(char ch0, char ch1, char ch2, char ch3)
{
// This is alien to me...
return (byte) ch0 | ((uint) (byte) ch1 << 8) | ((uint) (byte) ch2 << 16) | ((uint) (byte) ch3 << 24);
}
}
public enum DXT10_RESOURCE_DIMENSION
{
DIMENSION_TEXTURE1D = 2,
DIMENSION_TEXTURE2D = 3,
DIMENSION_TEXTURE3D = 4
}
[Flags]
public enum DDSCAPS2 : uint
{
CUBEMAP = 0x200,
CUBEMAP_POSITIVEX = 0x400,
CUBEMAP_NEGATIVEX = 0x800,
CUBEMAP_POSITIVEY = 0x1000,
CUBEMAP_NEGATIVEY = 0x2000,
CUBEMAP_POSITIVEZ = 0x4000,
CUBEMAP_NEGATIVEZ = 0x8000,
CUBEMAP_ALLFACES = 0xFC00
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct DDS_HEADER
{
public uint dwSize;
public uint dwHeaderFlags;
public uint dwHeight;
public uint dwWidth;
public uint dwPitchOrLinearSize;
public uint dwDepth; // only if DDS_HEADER_FLAGS_VOLUME is set in dwHeaderFlags
public uint dwMipMapCount;
public uint dwReserved1; // [11]
public DDS_PIXELFORMAT PixelFormat; // ddspf
public uint dwSurfaceFlags;
public uint dwCubemapFlags;
public uint dwReserved2; // [3]
public uint GetSize()
{
// 9 uint + DDS_PIXELFORMAT uints + 2 uint arrays with 14 uints total
// each uint 4 bytes each
return 9 * 4 + PixelFormat.GetSize() + 14 * 4;
}
public void Write(BinaryWriter bw)
{
bw.Write(dwSize);
bw.Write(dwHeaderFlags);
bw.Write(dwHeight);
bw.Write(dwWidth);
bw.Write(dwPitchOrLinearSize);
bw.Write(dwDepth);
bw.Write(dwMipMapCount);
// Just write it multiple times, since it's never assigned a value anyway
for (var i = 0; i < 11; i++)
bw.Write(dwReserved1);
// DDS_PIXELFORMAT
bw.Write(PixelFormat.dwSize);
bw.Write(PixelFormat.dwFlags);
bw.Write(PixelFormat.dwFourCC);
bw.Write(PixelFormat.dwRGBBitCount);
bw.Write(PixelFormat.dwRBitMask);
bw.Write(PixelFormat.dwGBitMask);
bw.Write(PixelFormat.dwBBitMask);
bw.Write(PixelFormat.dwABitMask);
bw.Write(dwSurfaceFlags);
bw.Write(dwCubemapFlags);
// Just write it multiple times, since it's never assigned a value anyway
for (var i = 0; i < 3; i++)
bw.Write(dwReserved2);
}
public static uint Size
{
get
{
unsafe
{
return (uint) (sizeof(DDS_HEADER) + sizeof(int) * 10 + sizeof(int) * 2);
}
;
}
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct DDS_HEADER_DXT10
{
public uint dxgiFormat;
public uint resourceDimension;
public uint miscFlag;
public uint arraySize;
public uint miscFlags2;
public void Write(BinaryWriter bw)
{
bw.Write(dxgiFormat);
bw.Write(resourceDimension);
bw.Write(miscFlag);
bw.Write(arraySize);
bw.Write(miscFlags2);
}
public static uint Size
{
get
{
unsafe
{
return (uint) sizeof(DDS_HEADER_DXT10);
}
;
}
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct DDS_PIXELFORMAT
{
public uint dwSize;
public uint dwFlags;
public uint dwFourCC;
public uint dwRGBBitCount;
public uint dwRBitMask;
public uint dwGBitMask;
public uint dwBBitMask;
public uint dwABitMask;
public DDS_PIXELFORMAT(uint size, uint flags, uint fourCC, uint rgbBitCount, uint rBitMask, uint gBitMask,
uint bBitMask, uint aBitMask)
{
dwSize = size;
dwFlags = flags;
dwFourCC = fourCC;
dwRGBBitCount = rgbBitCount;
dwRBitMask = rBitMask;
dwGBitMask = gBitMask;
dwBBitMask = bBitMask;
dwABitMask = aBitMask;
}
public uint GetSize()
{
// 8 uints, each 4 bytes each
return 8 * 4;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,69 +0,0 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using Wabbajack.Common;
using Wabbajack.DTOs.BSA.FileStates;
namespace Wabbajack.Compression.BSA.FO4Archive;
public class ChunkBuilder
{
private BA2Chunk _chunk;
private Stream _dataSlab;
private long _offsetOffset;
private uint _packSize;
public static async Task<ChunkBuilder> Create(BA2DX10File state, BA2Chunk chunk, Stream src,
DiskSlabAllocator slab, CancellationToken token)
{
var builder = new ChunkBuilder {_chunk = chunk};
if (!chunk.Compressed)
{
builder._dataSlab = slab.Allocate(chunk.FullSz);
await src.CopyToLimitAsync(builder._dataSlab, (int) chunk.FullSz, token);
}
else
{
var deflater = new Deflater(Deflater.BEST_COMPRESSION);
await using var ms = new MemoryStream();
await using (var ds = new DeflaterOutputStream(ms, deflater))
{
ds.IsStreamOwner = false;
await src.CopyToLimitAsync(ds, (int) chunk.FullSz, token);
}
builder._dataSlab = slab.Allocate(ms.Length);
ms.Position = 0;
await ms.CopyToLimitAsync(builder._dataSlab, (int) ms.Length, token);
builder._packSize = (uint) ms.Length;
}
builder._dataSlab.Position = 0;
return builder;
}
public void WriteHeader(BinaryWriter bw)
{
_offsetOffset = bw.BaseStream.Position;
bw.Write((ulong) 0);
bw.Write(_packSize);
bw.Write(_chunk.FullSz);
bw.Write(_chunk.StartMip);
bw.Write(_chunk.EndMip);
bw.Write(_chunk.Align);
}
public async ValueTask WriteData(BinaryWriter bw, CancellationToken token)
{
var pos = bw.BaseStream.Position;
bw.BaseStream.Position = _offsetOffset;
bw.Write((ulong) pos);
bw.BaseStream.Position = pos;
await _dataSlab.CopyToLimitAsync(bw.BaseStream, (int) _dataSlab.Length, token);
await _dataSlab.DisposeAsync();
}
}

View File

@ -1,249 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Compression.BSA;
using ICSharpCode.SharpZipLib.Zip.Compression;
using Wabbajack.Common;
using Wabbajack.DTOs.BSA.FileStates;
using Wabbajack.DTOs.Streams;
using Wabbajack.DTOs.Texture;
using Wabbajack.Paths;
namespace Wabbajack.Compression.BSA.FO4Archive;
public class DX10Entry : IBA2FileEntry
{
private readonly Reader _bsa;
private ushort _chunkHdrLen;
private List<TextureChunk> _chunks;
private uint _dirHash;
private string _extension;
private byte _format;
private ushort _height;
private int _index;
private uint _nameHash;
private byte _numChunks;
private byte _numMips;
private ushort _unk16;
private byte _unk8;
private ushort _width;
private readonly byte _isCubemap;
private readonly byte _tileMode;
public DX10Entry(Reader ba2Reader, int idx)
{
_bsa = ba2Reader;
var _rdr = ba2Reader._rdr;
_nameHash = _rdr.ReadUInt32();
FullPath = _nameHash.ToString("X");
_extension = Encoding.UTF8.GetString(_rdr.ReadBytes(4));
_dirHash = _rdr.ReadUInt32();
_unk8 = _rdr.ReadByte();
_numChunks = _rdr.ReadByte();
_chunkHdrLen = _rdr.ReadUInt16();
_height = _rdr.ReadUInt16();
_width = _rdr.ReadUInt16();
_numMips = _rdr.ReadByte();
_format = _rdr.ReadByte();
_isCubemap = _rdr.ReadByte();
_tileMode = _rdr.ReadByte();
_index = idx;
_chunks = Enumerable.Range(0, _numChunks)
.Select(_ => new TextureChunk(_rdr))
.ToList();
}
public uint HeaderSize => DDS.HeaderSizeForFormat((DXGI_FORMAT) _format);
public string FullPath { get; set; }
public RelativePath Path => FullPath.ToRelativePath();
public uint Size => (uint) _chunks.Sum(f => f._fullSz) + HeaderSize + sizeof(uint);
public AFile State => new BA2DX10File
{
Path = Path,
NameHash = _nameHash,
Extension = _extension,
DirHash = _dirHash,
Unk8 = _unk8,
ChunkHdrLen = _chunkHdrLen,
Height = _height,
Width = _width,
NumMips = _numMips,
PixelFormat = _format,
IsCubeMap = _isCubemap,
TileMode = _tileMode,
Index = _index,
Chunks = _chunks.Select(ch => new BA2Chunk
{
FullSz = ch._fullSz,
StartMip = ch._startMip,
EndMip = ch._endMip,
Align = ch._align,
Compressed = ch._packSz != 0
}).ToArray()
};
public async ValueTask CopyDataTo(Stream output, CancellationToken token)
{
var bw = new BinaryWriter(output);
WriteHeader(bw);
await using var fs = await _bsa._streamFactory.GetStream();
using var br = new BinaryReader(fs);
foreach (var chunk in _chunks)
{
var full = new byte[chunk._fullSz];
var isCompressed = chunk._packSz != 0;
br.BaseStream.Seek((long) chunk._offset, SeekOrigin.Begin);
if (!isCompressed)
{
await br.BaseStream.ReadAsync(full, token);
}
else
{
var compressed = new byte[chunk._packSz];
await br.BaseStream.ReadAsync(compressed, token);
var inflater = new Inflater();
inflater.SetInput(compressed);
inflater.Inflate(full);
}
await bw.BaseStream.WriteAsync(full, token);
}
}
public async ValueTask<IStreamFactory> GetStreamFactory(CancellationToken token)
{
var ms = new MemoryStream();
await CopyDataTo(ms, token);
ms.Position = 0;
return new MemoryStreamFactory(ms, Path, _bsa._streamFactory.LastModifiedUtc);
}
private void WriteHeader(BinaryWriter bw)
{
var ddsHeader = new DDS_HEADER();
ddsHeader.dwSize = ddsHeader.GetSize();
ddsHeader.dwHeaderFlags = DDS.DDS_HEADER_FLAGS_TEXTURE | DDS.DDS_HEADER_FLAGS_LINEARSIZE |
DDS.DDS_HEADER_FLAGS_MIPMAP;
ddsHeader.dwHeight = _height;
ddsHeader.dwWidth = _width;
ddsHeader.dwMipMapCount = _numMips;
ddsHeader.PixelFormat.dwSize = ddsHeader.PixelFormat.GetSize();
ddsHeader.dwDepth = 1;
ddsHeader.dwSurfaceFlags = DDS.DDS_SURFACE_FLAGS_TEXTURE | DDS.DDS_SURFACE_FLAGS_MIPMAP;
ddsHeader.dwCubemapFlags = _isCubemap == 1 ? (uint)(DDSCAPS2.CUBEMAP
| DDSCAPS2.CUBEMAP_NEGATIVEX | DDSCAPS2.CUBEMAP_POSITIVEX
| DDSCAPS2.CUBEMAP_NEGATIVEY | DDSCAPS2.CUBEMAP_POSITIVEY
| DDSCAPS2.CUBEMAP_NEGATIVEZ | DDSCAPS2.CUBEMAP_POSITIVEZ
| DDSCAPS2.CUBEMAP_ALLFACES) : 0u;
switch ((DXGI_FORMAT) _format)
{
case DXGI_FORMAT.BC1_UNORM:
ddsHeader.PixelFormat.dwFlags = DDS.DDS_FOURCC;
ddsHeader.PixelFormat.dwFourCC = DDS.MAKEFOURCC('D', 'X', 'T', '1');
ddsHeader.dwPitchOrLinearSize = (uint) (_width * _height / 2); // 4bpp
break;
case DXGI_FORMAT.BC2_UNORM:
ddsHeader.PixelFormat.dwFlags = DDS.DDS_FOURCC;
ddsHeader.PixelFormat.dwFourCC = DDS.MAKEFOURCC('D', 'X', 'T', '3');
ddsHeader.dwPitchOrLinearSize = (uint) (_width * _height); // 8bpp
break;
case DXGI_FORMAT.BC3_UNORM:
ddsHeader.PixelFormat.dwFlags = DDS.DDS_FOURCC;
ddsHeader.PixelFormat.dwFourCC = DDS.MAKEFOURCC('D', 'X', 'T', '5');
ddsHeader.dwPitchOrLinearSize = (uint) (_width * _height); // 8bpp
break;
case DXGI_FORMAT.BC5_UNORM:
ddsHeader.PixelFormat.dwFlags = DDS.DDS_FOURCC;
if (_bsa.UseATIFourCC)
ddsHeader.PixelFormat.dwFourCC =
DDS.MAKEFOURCC('A', 'T', 'I',
'2'); // this is more correct but the only thing I have found that supports it is the nvidia photoshop plugin
else
ddsHeader.PixelFormat.dwFourCC = DDS.MAKEFOURCC('B', 'C', '5', 'U');
ddsHeader.dwPitchOrLinearSize = (uint) (_width * _height); // 8bpp
break;
case DXGI_FORMAT.BC1_UNORM_SRGB:
ddsHeader.PixelFormat.dwFlags = DDS.DDS_FOURCC;
ddsHeader.PixelFormat.dwFourCC = DDS.MAKEFOURCC('D', 'X', '1', '0');
ddsHeader.dwPitchOrLinearSize = (uint) (_width * _height / 2); // 4bpp
break;
case DXGI_FORMAT.BC3_UNORM_SRGB:
case DXGI_FORMAT.BC6H_UF16:
case DXGI_FORMAT.BC4_UNORM:
case DXGI_FORMAT.BC5_SNORM:
case DXGI_FORMAT.BC7_UNORM:
case DXGI_FORMAT.BC7_UNORM_SRGB:
ddsHeader.PixelFormat.dwFlags = DDS.DDS_FOURCC;
ddsHeader.PixelFormat.dwFourCC = DDS.MAKEFOURCC('D', 'X', '1', '0');
ddsHeader.dwPitchOrLinearSize = (uint) (_width * _height); // 8bpp
break;
case DXGI_FORMAT.R8G8B8A8_UNORM:
case DXGI_FORMAT.R8G8B8A8_UNORM_SRGB:
ddsHeader.PixelFormat.dwFlags = DDS.DDS_RGBA;
ddsHeader.PixelFormat.dwRGBBitCount = 32;
ddsHeader.PixelFormat.dwRBitMask = 0x000000FF;
ddsHeader.PixelFormat.dwGBitMask = 0x0000FF00;
ddsHeader.PixelFormat.dwBBitMask = 0x00FF0000;
ddsHeader.PixelFormat.dwABitMask = 0xFF000000;
ddsHeader.dwPitchOrLinearSize = (uint) (_width * _height * 4); // 32bpp
break;
case DXGI_FORMAT.B8G8R8A8_UNORM:
case DXGI_FORMAT.B8G8R8X8_UNORM:
ddsHeader.PixelFormat.dwFlags = DDS.DDS_RGBA;
ddsHeader.PixelFormat.dwRGBBitCount = 32;
ddsHeader.PixelFormat.dwRBitMask = 0x00FF0000;
ddsHeader.PixelFormat.dwGBitMask = 0x0000FF00;
ddsHeader.PixelFormat.dwBBitMask = 0x000000FF;
ddsHeader.PixelFormat.dwABitMask = 0xFF000000;
ddsHeader.dwPitchOrLinearSize = (uint) (_width * _height * 4); // 32bpp
break;
case DXGI_FORMAT.R8_UNORM:
ddsHeader.PixelFormat.dwFlags = DDS.DDS_RGB;
ddsHeader.PixelFormat.dwRGBBitCount = 8;
ddsHeader.PixelFormat.dwRBitMask = 0xFF;
ddsHeader.dwPitchOrLinearSize = (uint) (_width * _height); // 8bpp
break;
default:
throw new Exception("Unsupported DDS header format. File: " + FullPath);
}
bw.Write((uint) DDS.DDS_MAGIC);
ddsHeader.Write(bw);
switch ((DXGI_FORMAT) _format)
{
case DXGI_FORMAT.BC1_UNORM_SRGB:
case DXGI_FORMAT.BC3_UNORM_SRGB:
case DXGI_FORMAT.BC4_UNORM:
case DXGI_FORMAT.BC5_SNORM:
case DXGI_FORMAT.BC6H_UF16:
case DXGI_FORMAT.BC7_UNORM:
case DXGI_FORMAT.BC7_UNORM_SRGB:
var dxt10 = new DDS_HEADER_DXT10
{
dxgiFormat = _format,
resourceDimension = (uint) DXT10_RESOURCE_DIMENSION.DIMENSION_TEXTURE2D,
miscFlag = 0,
arraySize = 1,
miscFlags2 = DDS.DDS_ALPHA_MODE_UNKNOWN
};
dxt10.Write(bw);
break;
}
}
}

View File

@ -12,13 +12,13 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.6.1" /> <PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

View File

@ -11,18 +11,18 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.6.1" /> <PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="Xunit.DependencyInjection" Version="8.9.0" /> <PackageReference Include="Xunit.DependencyInjection" Version="9.3.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="YamlDotNet" Version="13.7.1" /> <PackageReference Include="YamlDotNet" Version="15.1.6" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -17,4 +17,7 @@ public class BA2State : IArchive
public BA2EntryType Type { get; set; } public BA2EntryType Type { get; set; }
public string HeaderMagic { get; set; } public string HeaderMagic { get; set; }
public uint Version { get; set; } public uint Version { get; set; }
public uint Unknown1 { get; set; }
public uint Unknown2 { get; set; }
public uint Compression { get; set; }
} }

View File

@ -11,15 +11,15 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.6.1" /> <PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="Xunit.DependencyInjection.Logging" Version="8.1.0" /> <PackageReference Include="Xunit.DependencyInjection.Logging" Version="8.1.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

View File

@ -148,8 +148,9 @@ public class DownloadDispatcher
return result; return result;
} }
catch (HttpException) catch (HttpException ex)
{ {
_logger.LogError($"Failed verifying {a.State.PrimaryKeyString}: {ex}");
await _verificationCache.Put(a.State, false); await _verificationCache.Put(a.State, false);
return false; return false;
} }

View File

@ -18,11 +18,11 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="GameFinder.StoreHandlers.EADesktop" Version="4.1.0" /> <PackageReference Include="GameFinder.StoreHandlers.EADesktop" Version="4.2.2" />
<PackageReference Include="GameFinder.StoreHandlers.EGS" Version="4.1.0" /> <PackageReference Include="GameFinder.StoreHandlers.EGS" Version="4.2.2" />
<PackageReference Include="GameFinder.StoreHandlers.GOG" Version="4.1.0" /> <PackageReference Include="GameFinder.StoreHandlers.GOG" Version="4.2.2" />
<PackageReference Include="GameFinder.StoreHandlers.Origin" Version="4.1.0" /> <PackageReference Include="GameFinder.StoreHandlers.Origin" Version="4.2.2" />
<PackageReference Include="GameFinder.StoreHandlers.Steam" Version="4.1.0" /> <PackageReference Include="GameFinder.StoreHandlers.Steam" Version="4.2.2" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -12,7 +12,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="HtmlAgilityPack" Version="1.11.54" /> <PackageReference Include="HtmlAgilityPack" Version="1.11.61" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
</ItemGroup> </ItemGroup>

View File

@ -6,8 +6,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="HtmlAgilityPack" Version="1.11.54" /> <PackageReference Include="HtmlAgilityPack" Version="1.11.61" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
</ItemGroup> </ItemGroup>

View File

@ -13,7 +13,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="HtmlAgilityPack" Version="1.11.54" /> <PackageReference Include="HtmlAgilityPack" Version="1.11.61" />
<PackageReference Include="MegaApiClient" Version="1.10.3" /> <PackageReference Include="MegaApiClient" Version="1.10.3" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
</ItemGroup> </ItemGroup>

View File

@ -217,8 +217,9 @@ public class NexusDownloader : ADownloader<Nexus>, IUrlDownloader
return fileInfo.info.FileId == state.FileID; return fileInfo.info.FileId == state.FileID;
} }
catch (HttpException) catch (HttpException ex)
{ {
_logger.LogError($"HttpException: {ex} on {archive.Name}");
return false; return false;
} }
} }

View File

@ -11,17 +11,17 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.6.1" /> <PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="Xunit.DependencyInjection" Version="8.9.0" /> <PackageReference Include="Xunit.DependencyInjection" Version="9.3.0" />
<PackageReference Include="Xunit.DependencyInjection.Logging" Version="8.1.0" /> <PackageReference Include="Xunit.DependencyInjection.Logging" Version="8.1.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

View File

@ -12,15 +12,15 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="FluentAssertions" Version="6.12.0" /> <PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="Shipwreck.Phash" Version="0.5.0" /> <PackageReference Include="Shipwreck.Phash" Version="0.5.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.2" /> <PackageReference Include="SixLabors.ImageSharp" Version="3.1.4" />
<PackageReference Include="xunit" Version="2.6.1" /> <PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

View File

@ -11,14 +11,14 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="System.Data.HashFunction.xxHash" Version="2.0.0" /> <PackageReference Include="System.Data.HashFunction.xxHash" Version="2.0.0" />
<PackageReference Include="xunit" Version="2.6.1" /> <PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

View File

@ -11,14 +11,14 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.6.1" /> <PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -22,6 +23,7 @@ using Wabbajack.DTOs;
using Wabbajack.DTOs.BSA.FileStates; using Wabbajack.DTOs.BSA.FileStates;
using Wabbajack.DTOs.Directives; using Wabbajack.DTOs.Directives;
using Wabbajack.DTOs.DownloadStates; using Wabbajack.DTOs.DownloadStates;
using Wabbajack.DTOs.Interventions;
using Wabbajack.DTOs.JsonConverters; using Wabbajack.DTOs.JsonConverters;
using Wabbajack.Hashing.PHash; using Wabbajack.Hashing.PHash;
using Wabbajack.Hashing.xxHash64; using Wabbajack.Hashing.xxHash64;
@ -123,6 +125,12 @@ public class StandardInstaller : AInstaller<StandardInstaller>
var missing = ModList.Archives.Where(a => !HashedArchives.ContainsKey(a.Hash)).ToList(); var missing = ModList.Archives.Where(a => !HashedArchives.ContainsKey(a.Hash)).ToList();
if (missing.Count > 0) 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) foreach (var a in missing)
_logger.LogCritical("Unable to download {name} ({primaryKeyString})", a.Name, _logger.LogCritical("Unable to download {name} ({primaryKeyString})", a.Name,
a.State.PrimaryKeyString); a.State.PrimaryKeyString);
@ -168,6 +176,46 @@ public class StandardInstaller : AInstaller<StandardInstaller>
return true; 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() private Task RemapMO2File()
{ {
var iniFile = _configuration.Install.Combine("ModOrganizer.ini"); var iniFile = _configuration.Install.Combine("ModOrganizer.ini");
@ -247,7 +295,16 @@ public class StandardInstaller : AInstaller<StandardInstaller>
var metaFile = download.WithExtension(Ext.Meta); var metaFile = download.WithExtension(Ext.Meta);
var found = bySize[download.Size()]; var found = bySize[download.Size()];
var hash = await FileHashCache.FileHashCachedAsync(download, token); Hash hash = default;
try
{
hash = await FileHashCache.FileHashCachedAsync(download, token);
}
catch(Exception ex)
{
_logger.LogError($"Failed to get hash for file {download}!");
throw;
}
var archive = found.FirstOrDefault(f => f.Hash == hash); var archive = found.FirstOrDefault(f => f.Hash == hash);
IEnumerable<string> meta; IEnumerable<string> meta;

View File

@ -24,7 +24,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="ini-parser-netstandard" Version="2.5.2" /> <PackageReference Include="ini-parser-netstandard" Version="2.5.2" />
<PackageReference Include="Octopus.Octodiff" Version="2.0.468" /> <PackageReference Include="Octopus.Octodiff" Version="2.0.546" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -19,15 +19,15 @@
<TargetFramework>net8.0-windows</TargetFramework> <TargetFramework>net8.0-windows</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.5" /> <PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.10" />
<PackageReference Include="GitInfo" Version="3.3.3"> <PackageReference Include="GitInfo" Version="3.3.3">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Avalonia" Version="11.0.5" /> <PackageReference Include="Avalonia" Version="11.0.10" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.5" /> <PackageReference Include="Avalonia.Desktop" Version="11.0.10" />
<PackageReference Include="Avalonia.Diagnostics" Version="11.0.5" /> <PackageReference Include="Avalonia.Diagnostics" Version="11.0.10" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.5" /> <PackageReference Include="Avalonia.ReactiveUI" Version="11.0.10" />
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" /> <PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
<PackageReference Include="MessageBox.Avalonia" Version="3.1.5.1" /> <PackageReference Include="MessageBox.Avalonia" Version="3.1.5.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />

View File

@ -12,13 +12,13 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.6.1" /> <PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

View File

@ -11,15 +11,15 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.6.1" /> <PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="Xunit.DependencyInjection" Version="8.9.0" /> <PackageReference Include="Xunit.DependencyInjection" Version="9.3.0" />
<PackageReference Include="Xunit.DependencyInjection.Logging" Version="8.1.0" /> <PackageReference Include="Xunit.DependencyInjection.Logging" Version="8.1.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

View File

@ -3,6 +3,6 @@ using Wabbajack.Networking.Http.Interfaces;
namespace Wabbajack.Networking.NexusApi; namespace Wabbajack.Networking.NexusApi;
public interface AuthInfo : ITokenProvider<NexusOAuthState> public interface IAuthInfo : ITokenProvider<NexusOAuthState>
{ {
} }

View File

@ -188,7 +188,6 @@ public class NexusApi
protected virtual async ValueTask<HttpRequestMessage> GenerateMessage(HttpMethod method, string uri, protected virtual async ValueTask<HttpRequestMessage> GenerateMessage(HttpMethod method, string uri,
params object?[] parameters) params object?[] parameters)
{ {
using var _ = await _authLock.WaitAsync();
var msg = new HttpRequestMessage(); var msg = new HttpRequestMessage();
msg.Method = method; msg.Method = method;
@ -232,6 +231,7 @@ public class NexusApi
private async ValueTask<(bool IsApiKey, string code)> GetAuthInfo() private async ValueTask<(bool IsApiKey, string code)> GetAuthInfo()
{ {
using var _ = await _authLock.WaitAsync();
if (AuthInfo.HaveToken()) if (AuthInfo.HaveToken())
{ {
var info = await AuthInfo.Get(); var info = await AuthInfo.Get();
@ -272,6 +272,8 @@ public class NexusApi
var response = await _client.PostAsync($"https://users.nexusmods.com/oauth/token", content, cancel); var response = await _client.PostAsync($"https://users.nexusmods.com/oauth/token", content, cancel);
var responseString = await response.Content.ReadAsStringAsync(cancel); var responseString = await response.Content.ReadAsStringAsync(cancel);
var newJwt = JsonSerializer.Deserialize<JwtTokenReply>(responseString); var newJwt = JsonSerializer.Deserialize<JwtTokenReply>(responseString);
if (newJwt != null)
newJwt.ReceivedAt = DateTime.UtcNow.ToFileTimeUtc();
state.OAuth = newJwt; state.OAuth = newJwt;
await AuthInfo.SetToken(state); await AuthInfo.SetToken(state);

View File

@ -12,7 +12,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
</ItemGroup> </ItemGroup>

View File

@ -12,16 +12,16 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.6.1" /> <PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="Xunit.DependencyInjection" Version="8.9.0" /> <PackageReference Include="Xunit.DependencyInjection" Version="9.3.0" />
<PackageReference Include="Xunit.DependencyInjection.Logging" Version="8.1.0" /> <PackageReference Include="Xunit.DependencyInjection.Logging" Version="8.1.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

View File

@ -14,7 +14,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageReference Include="Octokit" Version="9.0.0" /> <PackageReference Include="Octokit" Version="9.0.0" />
<PackageReference Include="YamlDotNet" Version="13.7.1" /> <PackageReference Include="YamlDotNet" Version="15.1.6" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -12,13 +12,13 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="FsCheck.Xunit" Version="2.16.6" /> <PackageReference Include="FsCheck.Xunit" Version="2.16.6" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.6.1" /> <PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

View File

@ -11,13 +11,13 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.6.1" /> <PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

View File

@ -12,13 +12,13 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.6.1" /> <PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

View File

@ -5,7 +5,7 @@ using Wabbajack.Networking.NexusApi;
namespace Wabbajack.Services.OSIntegrated.TokenProviders; namespace Wabbajack.Services.OSIntegrated.TokenProviders;
public class NexusApiTokenProvider : EncryptedJsonTokenProvider<NexusOAuthState>, AuthInfo public class NexusApiTokenProvider : EncryptedJsonTokenProvider<NexusOAuthState>, IAuthInfo
{ {
public NexusApiTokenProvider(ILogger<NexusApiTokenProvider> logger, DTOSerializer dtos) : base(logger, dtos, public NexusApiTokenProvider(ILogger<NexusApiTokenProvider> logger, DTOSerializer dtos) : base(logger, dtos,
"nexus-oauth-info") "nexus-oauth-info")

View File

@ -17,7 +17,7 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -11,21 +11,21 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.118" /> <PackageReference Include="System.Data.SQLite.Core" Version="1.0.118" />
<PackageReference Include="Xunit.DependencyInjection" Version="8.9.0" /> <PackageReference Include="Xunit.DependencyInjection" Version="9.3.0" />
<PackageReference Include="Xunit.DependencyInjection.Logging" Version="8.1.0" /> <PackageReference Include="Xunit.DependencyInjection.Logging" Version="8.1.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.6.1" /> <PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0"> <PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

View File

@ -8,7 +8,7 @@ mkdir c:\tmp\publish-wj
dotnet clean dotnet clean
dotnet publish Wabbajack.App.Wpf\Wabbajack.App.Wpf.csproj --framework "net8.0-windows" --runtime win-x64 --configuration Release /p:Platform=x64 -o c:\tmp\publish-wj\app /p:IncludeNativeLibrariesForSelfExtract=true --self-contained /p:DebugType=embedded dotnet publish Wabbajack.App.Wpf\Wabbajack.App.Wpf.csproj --framework "net8.0-windows" --runtime win-x64 --configuration Release /p:Platform=x64 -o c:\tmp\publish-wj\app /p:IncludeNativeLibrariesForSelfExtract=true --self-contained /p:DebugType=embedded
dotnet publish Wabbajack.Launcher\Wabbajack.Launcher.csproj --framework "net8.0-windows" --runtime win-x64 --configuration Release /p:Platform=x64 -o c:\tmp\publish-wj\launcher /p:PublishSingleFile=true /p:PublishSingleFile=true /p:IncludeNativeLibrariesForSelfExtract=true --self-contained /p:DebugType=embedded dotnet publish Wabbajack.Launcher\Wabbajack.Launcher.csproj --framework "net8.0-windows" --runtime win-x64 --configuration Release /p:Platform=x64 -o c:\tmp\publish-wj\launcher /p:PublishSingleFile=true /p:PublishSingleFile=true /p:IncludeNativeLibrariesForSelfExtract=true --self-contained /p:DebugType=embedded
dotnet publish c:\oss\Wabbajack\Wabbajack.CLI\Wabbajack.CLI.csproj --framework "net8.0-windows" --runtime win-x64 --configuration Release /p:Platform=x64 -o c:\tmp\publish-wj\app\cli /p:IncludeNativeLibrariesForSelfExtract=true --self-contained /p:DebugType=embedded dotnet publish Wabbajack.CLI\Wabbajack.CLI.csproj --framework "net8.0-windows" --runtime win-x64 --configuration Release /p:Platform=x64 -o c:\tmp\publish-wj\app\cli /p:IncludeNativeLibrariesForSelfExtract=true --self-contained /p:DebugType=embedded
"C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\signtool.exe" sign /fd sha256 /tr http://ts.ssl.com /td sha256 /sha1 8c26a8e0bf3e70eb89721cc4d86a87137153ccba c:\tmp\publish-wj\app\Wabbajack.exe "C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\signtool.exe" sign /fd sha256 /tr http://ts.ssl.com /td sha256 /sha1 8c26a8e0bf3e70eb89721cc4d86a87137153ccba c:\tmp\publish-wj\app\Wabbajack.exe
"C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\signtool.exe" sign /fd sha256 /tr http://ts.ssl.com /td sha256 /sha1 8c26a8e0bf3e70eb89721cc4d86a87137153ccba c:\tmp\publish-wj\launcher\Wabbajack.exe "C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\signtool.exe" sign /fd sha256 /tr http://ts.ssl.com /td sha256 /sha1 8c26a8e0bf3e70eb89721cc4d86a87137153ccba c:\tmp\publish-wj\launcher\Wabbajack.exe
"C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\signtool.exe" sign /fd sha256 /tr http://ts.ssl.com /td sha256 /sha1 8c26a8e0bf3e70eb89721cc4d86a87137153ccba c:\tmp\publish-wj\app\cli\wabbajack-cli.exe "C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\signtool.exe" sign /fd sha256 /tr http://ts.ssl.com /td sha256 /sha1 8c26a8e0bf3e70eb89721cc4d86a87137153ccba c:\tmp\publish-wj\app\cli\wabbajack-cli.exe