diff --git a/Credits/Credits.txt b/Credits/Credits.txt index 824661bce..ff9e376ea 100644 --- a/Credits/Credits.txt +++ b/Credits/Credits.txt @@ -28,7 +28,7 @@ license Type:MIT ######################### Package:AutoMapper -Version:12.0.1 +Version:13.0.1 project URL:https://automapper.org/ Description:A convention-based object-object mapper. licenseUrl:https://licenses.nuget.org/MIT @@ -126,10 +126,10 @@ license Type:MIT ######################### Package:CompareNETObjects -Version:4.79.0 +Version:4.83.0 project URL:https://github.com/GregFinzer/Compare-Net-Objects Description:What you have been waiting for. Perform a deep compare of any two .NET objects using reflection. Shows the differences between the two objects. -licenseUrl:https://www.nuget.org/packages/CompareNETObjects/4.77.0/License +licenseUrl:https://www.nuget.org/packages/CompareNETObjects/4.83.0/License license Type:Microsoft Public License (Ms-PL) ######################### @@ -142,7 +142,7 @@ license Type:MIT ######################### Package:DiffPlex -Version:1.7.1 +Version:1.7.2 project URL:https://github.com/mmanela/diffplex/ Description:DiffPlex is a diffing library that allows you to programatically create text diffs. DiffPlex is a fast and tested library. licenseUrl:https://licenses.nuget.org/Apache-2.0 @@ -158,7 +158,7 @@ license Type:BSD, Apache, zlib and MIT ######################### Package:FluentAssertions -Version:6.11.0 +Version:6.12.0 project URL:https://www.fluentassertions.com/ Description:A very extensive set of extension methods that allow you to more naturally specify the expected outcome of a TDD or BDD-style unit tests. Targets .NET Framework 4.7, .NET Core 2.1 and 3.0, .NET 6, as well as .NET Standard 2.0 and 2.1. @@ -170,11 +170,9 @@ license Type:Apache-2.0 ######################### Package:FSharp.Core -Version:7.0.300 +Version:8.0.200 project URL:https://github.com/dotnet/fsharp -Description:FSharp.Core redistributables from F# Tools version 12.4.0 For F# 7.0. Contains code from the F# Software Foundation. -licenseUrl:https://licenses.nuget.org/MIT -license Type:MIT +Description:FSharp.Core redistributables from F# Tools version 12.8.200 For F# 8.0. Contains code from the F# Software Foundation. licenseUrl:https://licenses.nuget.org/MIT license Type:MIT @@ -212,7 +210,7 @@ license Type:Apache-2.0 ######################### Package:Magick.NET-Q8-x64 -Version:13.1.3 +Version:13.6.0 project URL:https://github.com/dlemstra/Magick.NET Description:ImageMagick is a powerful image manipulation library that supports over 100 major file formats (not including sub-formats). With Magick.NET you can use ImageMagick without having to install ImageMagick on your server or desktop. Visit https://github.com/dlemstra/Magick.NET/tree/main/docs before installing to help you decide the best version. licenseUrl:https://licenses.nuget.org/Apache-2.0 @@ -252,7 +250,7 @@ license Type:MIT ######################### Package:Microsoft.Bcl.AsyncInterfaces -Version:7.0.0 +Version:8.0.0 project URL:https://dot.net/ Description:Provides the IAsyncEnumerable and IAsyncDisposable interfaces and helper types for .NET Standard 2.0. This package is not required starting with .NET Standard 2.1 and .NET Core 3.0. @@ -265,8 +263,8 @@ license Type:MIT ######################### Package:Microsoft.NET.Test.Sdk -Version:17.6.2 -project URL:https://github.com/microsoft/vstest/ +Version:17.9.0 +project URL:https://github.com/microsoft/vstest Description:The MSbuild targets and properties for building .NET test projects. licenseUrl:https://www.nuget.org/packages/Microsoft.NET.Test.Sdk/17.3.0/License license Type:MICROSOFT SOFTWARE LICENSE TERMS @@ -306,13 +304,19 @@ license Type:Apache-2.0 ######################### Package:Moq -Version:4.18.4 -project URL:https://github.com/moq/moq4 +Version:4.20.70 +project URL:https://github.com/moq/moq Description:Moq is the most popular and friendly mocking framework for .NET. +licenseUrl:https://licenses.nuget.org/BSD-3-Clause +license Type:BSD-3-Clause -Built from https://github.com/moq/moq4/tree/042a2ebbe -licenseUrl:https://raw.githubusercontent.com/moq/moq4/main/License.txt -license Type:BSD 3-Clause License +######################### +Package:NaturalSort.Extension +Version:4.2.0+build.231 +project URL:https://github.com/tompazourek/NaturalSort.Extension +Description:Extension method for StringComparison that adds support for natural sorting (e.g. "abc1", "abc2", "abc10" instead of "abc1", "abc10", "abc2"). +licenseUrl:https://licenses.nuget.org/MIT +license Type:MIT ######################### Package:Nerdbank.GitVersioning @@ -324,10 +328,10 @@ license Type:MIT ######################### Package:NetSparkleUpdater.SparkleUpdater -Version:2.2.3 +Version:2.3.0 project URL:https://github.com/NetSparkleUpdater/NetSparkle Description:NetSparkleUpdater/NetSparkle is a C# .NET software update framework that allows you to easily download installer files and update your C# .NET Framework or .NET Core software. Built-in UIs are available for WinForms, WPF, and Avalonia; if you want a built-in UI, please reference a NetSparkleUpdater.UI package. You provide, somewhere on the internet, an XML appcast with software version information along with release notes in Markdown or HTML format. The NetSparkle framework then checks for an update in the background, displays the release notes to the user, and lets users download or skip the software update. The framework can also perform silent downloads so that you can present all of the UI yourself or set up your own silent software update system, as allowed by your software architecture. It was inspired by the Sparkle (https://sparkle-project.org/) project for Cocoa developers and the WinSparkle (https://winsparkle.org/) project (a Win32 port). -licenseUrl:https://www.nuget.org/packages/NetSparkleUpdater.SparkleUpdater/2.2.3/License +licenseUrl:https://www.nuget.org/packages/NetSparkleUpdater.SparkleUpdater/2.3.0/License license Type:MIT ######################### @@ -348,14 +352,14 @@ license Type:MIT ######################### Package:NLog -Version:5.2.0 +Version:5.2.8 project URL:https://nlog-project.org/ Description:NLog is a logging platform for .NET with rich log routing and management capabilities. NLog supports traditional logging, structured logging and the combination of both. Supported platforms: -- .NET 5, 6 and 7 +- .NET 5, 6, 7 and 8 - .NET Core 1, 2 and 3 - .NET Standard 1.3+ and 2.0+ - .NET Framework 3.5 - 4.8 @@ -376,7 +380,7 @@ license Type:MIT ######################### Package:PommaLabs.MimeTypes -Version:2.8.3+5c6b155 +Version:2.9.1+63b5125a project URL:https://gitlab.com/pommalabs/mime-types Description:MIME content type definitions mapped with file extensions and file signatures. licenseUrl:https://licenses.nuget.org/MIT @@ -392,7 +396,7 @@ license Type:Adapted MIT License ######################### Package:ReactiveUI -Version:19.2.1 +Version:19.5.41 project URL:https://reactiveui.net/ Description:A MVVM framework that integrates with the Reactive Extensions for .NET to create elegant, testable User Interfaces that run on any mobile or desktop platform. This is the base package with the base platform implementations licenseUrl:https://licenses.nuget.org/MIT @@ -403,7 +407,7 @@ Package:RepoDb Version:1.13.1 project URL:https://repodb.net/ Description:A hybrid ORM library for .NET. -licenseUrl:https://www.nuget.org/packages/RepoDb/1.12.10/License +licenseUrl:https://www.nuget.org/packages/RepoDb/1.13.1/License license Type:Apache License 2.0 ######################### @@ -424,7 +428,7 @@ license Type:MIT ######################### Package:SharpCompress -Version:0.33.0 +Version:0.36.0 project URL:https://github.com/adamhathcock/sharpcompress Description:SharpCompress is a compression library for NET Standard 2.0/2.1/NET 6.0/NET 7.0 that can unrar, decompress 7zip, decompress xz, zip/unzip, tar/untar lzip/unlzip, bzip2/unbzip2 and gzip/ungzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip is implemented. licenseUrl:https://licenses.nuget.org/MIT @@ -432,7 +436,7 @@ license Type:MIT ######################### Package:SimpleInjector -Version:5.4.1 +Version:5.4.4 project URL:https://simpleinjector.org/ Description:Simple Injector is an easy, flexible and fast dependency injection library that uses best practice to guide your solutions toward the pit of success. licenseUrl:https://licenses.nuget.org/MIT @@ -462,9 +466,18 @@ Description:An extension to ImageSharp that allows the drawing of images, paths, licenseUrl:https://licenses.nuget.org/Apache-2.0 license Type:Apache-2.0 +######################### +Package:SkiaSharp +Version:2.88.7 +project URL:https://go.microsoft.com/fwlink/?linkid=868515 +Description:SkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Google's Skia Graphics Library. +It provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images. +licenseUrl:https://go.microsoft.com/fwlink/?linkid=868514 +license Type:MIT License + ######################### Package:SlimMessageBus.Host.Memory -Version:2.1.8 +Version:2.2.3 project URL:https://github.com/zarusz/SlimMessageBus Description:Simple provider for SlimMessageBus for in process message passing. Messages are stored in memory (state is transient). licenseUrl:https://licenses.nuget.org/Apache-2.0 @@ -472,7 +485,7 @@ license Type:Apache-2.0 ######################### Package:SmartFormat -Version:3.2.1 +Version:3.3.2 project URL:https://github.com/axuno/SmartFormat Description:This package contains the core SmartFormat assemblies with core extensions built-in. @@ -484,7 +497,7 @@ license Type:MIT ######################### Package:Splat.NLog -Version:14.6.37 +Version:14.8.12 project URL:https://github.com/reactiveui/splat/ Description:A library to make things cross-platform that should be. licenseUrl:https://licenses.nuget.org/MIT @@ -492,7 +505,7 @@ license Type:MIT ######################### Package:Splat.SimpleInjector -Version:14.6.37 +Version:14.8.12 project URL:https://github.com/reactiveui/splat/ Description:A library to make things cross-platform that should be. licenseUrl:https://licenses.nuget.org/MIT @@ -583,7 +596,7 @@ license Type:MIT ######################### Package:xunit -Version:2.4.2 +Version:2.7.0 project URL:https://github.com/xunit/xunit Description:xUnit.net is a developer testing framework, built to support Test Driven Development, with a design goal of extreme simplicity and alignment with framework features. diff --git a/Directory.Build.props b/Directory.Build.props index 3556203ce..f5d251dc2 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -18,18 +18,21 @@ $(MSBuildProjectDirectory)=$(MSBuildProjectName) - [7.0.0] - [7.0.3] - [7.0.4] - [7.0.0] + [8.0.0] + [8.0.0-preview.7.23375.6] + [8.0.1] + [8.0.0] + [2.1.17] + + [2.5.1] [2.1.0] [0.10.22] [0.10.12.2] - [2.1.16] - + [1.1.10] + [2.4.5] - + [1.0.4] [1.0.0-beta13.15] [0.0.0-alpha.0.132] @@ -40,4 +43,8 @@ all + + + + \ No newline at end of file diff --git a/References/Direct/LiteDB.dll b/References/Direct/LiteDB.dll index daa25ae78..6ee13bbfa 100644 Binary files a/References/Direct/LiteDB.dll and b/References/Direct/LiteDB.dll differ diff --git a/Test.runsettings b/Test.runsettings index 310535c6d..a143ed742 100644 --- a/Test.runsettings +++ b/Test.runsettings @@ -4,7 +4,7 @@ 2 x64 .\TestResults - .NETCoreApp,Version=v3.1 + net8.0 diff --git a/Tools/LocalizationResourceGenerator/src/LocalizationResourceGenerator/LocalizationResourceGenerator.csproj b/Tools/LocalizationResourceGenerator/src/LocalizationResourceGenerator/LocalizationResourceGenerator.csproj index ae664eeb8..73b7f3371 100644 --- a/Tools/LocalizationResourceGenerator/src/LocalizationResourceGenerator/LocalizationResourceGenerator.csproj +++ b/Tools/LocalizationResourceGenerator/src/LocalizationResourceGenerator/LocalizationResourceGenerator.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 diff --git a/cmd/build-tools.bat b/cmd/build-tools.bat index b58fcb8d9..a28eb3030 100644 --- a/cmd/build-tools.bat +++ b/cmd/build-tools.bat @@ -5,6 +5,6 @@ dotnet build --configuration Release cd .. cd .. cd .. -xcopy "Tools\LocalizationResourceGenerator\src\LocalizationResourceGenerator\bin\Release\net7.0\*.dll" "Tools\LocalizationResourceGenerator\" /Y /S /D -xcopy "Tools\LocalizationResourceGenerator\src\LocalizationResourceGenerator\bin\Release\net7.0\*.exe" "Tools\LocalizationResourceGenerator\" /Y /S /D -xcopy "Tools\LocalizationResourceGenerator\src\LocalizationResourceGenerator\bin\Release\net7.0\*.json" "Tools\LocalizationResourceGenerator\" /Y /S /D \ No newline at end of file +xcopy "Tools\LocalizationResourceGenerator\src\LocalizationResourceGenerator\bin\Release\net8.0\*.dll" "Tools\LocalizationResourceGenerator\" /Y /S /D +xcopy "Tools\LocalizationResourceGenerator\src\LocalizationResourceGenerator\bin\Release\net8.0\*.exe" "Tools\LocalizationResourceGenerator\" /Y /S /D +xcopy "Tools\LocalizationResourceGenerator\src\LocalizationResourceGenerator\bin\Release\net8.0\*.json" "Tools\LocalizationResourceGenerator\" /Y /S /D \ No newline at end of file diff --git a/publish/publish-linux-x64.bat b/publish/publish-linux-x64.bat index ed588618b..3781e206b 100644 --- a/publish/publish-linux-x64.bat +++ b/publish/publish-linux-x64.bat @@ -17,16 +17,16 @@ dotnet publish src\IronyModManager.Common\IronyModManager.Common.csproj /p:Publ dotnet publish src\IronyModManager.Updater\IronyModManager.Updater.csproj /p:PublishProfile=src\IronyModManager.Updater\Properties\PublishProfiles\linux-x64.pubxml --configuration linux-x64 dotnet publish src\IronyModManager.GameHandler\IronyModManager.GameHandler.csproj /p:PublishProfile=src\IronyModManager.GameHandler\Properties\PublishProfiles\linux-x64.pubxml --configuration linux-x64 dotnet publish src\IronyModManager\IronyModManager.csproj /p:PublishProfile=src\IronyModManager\Properties\PublishProfiles\linux-x64.pubxml --configuration linux-x64 -xcopy "src\IronyModManager\bin\linux-x64\net7.0\linux-x64\*.dll" "src\IronyModManager\bin\x64\linux-x64\net7.0\publish\linux-x64\" /Y /S /D -xcopy "src\IronyModManager\bin\linux-x64\net7.0\linux-x64\*.json" "src\IronyModManager\bin\x64\linux-x64\net7.0\publish\linux-x64\" /Y /S /D -xcopy "src\IronyModManager\bin\linux-x64\net7.0\linux-x64\*.pdb" "src\IronyModManager\bin\x64\linux-x64\net7.0\publish\linux-x64\" /Y /S /D -xcopy "src\IronyModManager.Updater\bin\x64\linux-x64\net7.0\publish\linux-x64\*.*" "src\IronyModManager\bin\x64\linux-x64\net7.0\publish\linux-x64\" /Y /S /D -xcopy "src\IronyModManager.GameHandler\bin\x64\linux-x64\net7.0\publish\linux-x64\*.*" "src\IronyModManager\bin\x64\linux-x64\net7.0\publish\linux-x64\" /Y /S /D -del "src\IronyModManager\bin\x64\linux-x64\net7.0\publish\linux-x64\IronyModManager.runtimeconfig.dev.json" /S /Q -del "src\IronyModManager\bin\x64\linux-x64\net7.0\publish\linux-x64\IronyModManager.Updater.runtimeconfig.dev.json" /S /Q -del "src\IronyModManager\bin\x64\linux-x64\net7.0\publish\linux-x64\IronyModManager.GameHandler.runtimeconfig.dev.json" /S /Q -del "src\IronyModManager\bin\x64\linux-x64\net7.0\publish\linux-x64\steam_api64.dll" /S /Q -xcopy "References\CopyAll\*.*" "src\IronyModManager\bin\x64\linux-x64\net7.0\publish\linux-x64\" /Y /S /D +xcopy "src\IronyModManager\bin\linux-x64\net8.0\linux-x64\*.dll" "src\IronyModManager\bin\x64\linux-x64\net8.0\publish\linux-x64\" /Y /S /D +xcopy "src\IronyModManager\bin\linux-x64\net8.0\linux-x64\*.json" "src\IronyModManager\bin\x64\linux-x64\net8.0\publish\linux-x64\" /Y /S /D +xcopy "src\IronyModManager\bin\linux-x64\net8.0\linux-x64\*.pdb" "src\IronyModManager\bin\x64\linux-x64\net8.0\publish\linux-x64\" /Y /S /D +xcopy "src\IronyModManager.Updater\bin\x64\linux-x64\net8.0\publish\linux-x64\*.*" "src\IronyModManager\bin\x64\linux-x64\net8.0\publish\linux-x64\" /Y /S /D +xcopy "src\IronyModManager.GameHandler\bin\x64\linux-x64\net8.0\publish\linux-x64\*.*" "src\IronyModManager\bin\x64\linux-x64\net8.0\publish\linux-x64\" /Y /S /D +del "src\IronyModManager\bin\x64\linux-x64\net8.0\publish\linux-x64\IronyModManager.runtimeconfig.dev.json" /S /Q +del "src\IronyModManager\bin\x64\linux-x64\net8.0\publish\linux-x64\IronyModManager.Updater.runtimeconfig.dev.json" /S /Q +del "src\IronyModManager\bin\x64\linux-x64\net8.0\publish\linux-x64\IronyModManager.GameHandler.runtimeconfig.dev.json" /S /Q +del "src\IronyModManager\bin\x64\linux-x64\net8.0\publish\linux-x64\steam_api64.dll" /S /Q +xcopy "References\CopyAll\*.*" "src\IronyModManager\bin\x64\linux-x64\net8.0\publish\linux-x64\" /Y /S /D REM Why on earth cannot nuget include these? Also the documentation sucks in this regard -xcopy "References\Conditional\Steamworks\OSX-Linux-x64\libsteam_api.so" "src\IronyModManager\bin\x64\linux-x64\net7.0\publish\linux-x64\" /Y /S /D +xcopy "References\Conditional\Steamworks\OSX-Linux-x64\libsteam_api.so" "src\IronyModManager\bin\x64\linux-x64\net8.0\publish\linux-x64\" /Y /S /D cd publish \ No newline at end of file diff --git a/publish/publish-osx-x64.bat b/publish/publish-osx-x64.bat index 28cc408b3..cb1dd7d64 100644 --- a/publish/publish-osx-x64.bat +++ b/publish/publish-osx-x64.bat @@ -17,16 +17,16 @@ dotnet publish src\IronyModManager.Common\IronyModManager.Common.csproj /p:Publ dotnet publish src\IronyModManager.Updater\IronyModManager.Updater.csproj /p:PublishProfile=src\IronyModManager.Updater\Properties\PublishProfiles\osx-x64.pubxml --configuration osx-x64 dotnet publish src\IronyModManager.GameHandler\IronyModManager.GameHandler.csproj /p:PublishProfile=src\IronyModManager.GameHandler\Properties\PublishProfiles\osx-x64.pubxml --configuration osx-x64 dotnet publish src\IronyModManager\IronyModManager.csproj /p:PublishProfile=src\IronyModManager\Properties\PublishProfiles\osx-x64.pubxml --configuration osx-x64 -xcopy "src\IronyModManager\bin\osx-x64\net7.0\osx-x64\*.dll" "src\IronyModManager\bin\x64\osx-x64\net7.0\publish\osx-x64\" /Y /S /D -xcopy "src\IronyModManager\bin\osx-x64\net7.0\osx-x64\*.json" "src\IronyModManager\bin\x64\osx-x64\net7.0\publish\osx-x64\" /Y /S /D -xcopy "src\IronyModManager\bin\osx-x64\net7.0\osx-x64\*.pdb" "src\IronyModManager\bin\x64\osx-x64\net7.0\publish\osx-x64\" /Y /S /D -xcopy "src\IronyModManager.Updater\bin\x64\osx-x64\net7.0\publish\osx-x64\*.*" "src\IronyModManager\bin\x64\osx-x64\net7.0\publish\osx-x64\" /Y /S /D -xcopy "src\IronyModManager.GameHandler\bin\x64\osx-x64\net7.0\publish\osx-x64\*.*" "src\IronyModManager\bin\x64\osx-x64\net7.0\publish\osx-x64\" /Y /S /D -del "src\IronyModManager\bin\x64\osx-x64\net7.0\publish\osx-x64\IronyModManager.runtimeconfig.dev.json" /S /Q -del "src\IronyModManager\bin\x64\osx-x64\net7.0\publish\osx-x64\IronyModManager.Updater.runtimeconfig.dev.json" /S /Q -del "src\IronyModManager\bin\x64\osx-x64\net7.0\publish\osx-x64\IronyModManager.GameHandler.runtimeconfig.dev.json" /S /Q -del "src\IronyModManager\bin\x64\osx-x64\net7.0\publish\osx-x64\steam_api64.dll" /S /Q -xcopy "References\CopyAll\*.*" "src\IronyModManager\bin\x64\osx-x64\net7.0\publish\osx-x64\" /Y /S /D +xcopy "src\IronyModManager\bin\osx-x64\net8.0\osx-x64\*.dll" "src\IronyModManager\bin\x64\osx-x64\net8.0\publish\osx-x64\" /Y /S /D +xcopy "src\IronyModManager\bin\osx-x64\net8.0\osx-x64\*.json" "src\IronyModManager\bin\x64\osx-x64\net8.0\publish\osx-x64\" /Y /S /D +xcopy "src\IronyModManager\bin\osx-x64\net8.0\osx-x64\*.pdb" "src\IronyModManager\bin\x64\osx-x64\net8.0\publish\osx-x64\" /Y /S /D +xcopy "src\IronyModManager.Updater\bin\x64\osx-x64\net8.0\publish\osx-x64\*.*" "src\IronyModManager\bin\x64\osx-x64\net8.0\publish\osx-x64\" /Y /S /D +xcopy "src\IronyModManager.GameHandler\bin\x64\osx-x64\net8.0\publish\osx-x64\*.*" "src\IronyModManager\bin\x64\osx-x64\net8.0\publish\osx-x64\" /Y /S /D +del "src\IronyModManager\bin\x64\osx-x64\net8.0\publish\osx-x64\IronyModManager.runtimeconfig.dev.json" /S /Q +del "src\IronyModManager\bin\x64\osx-x64\net8.0\publish\osx-x64\IronyModManager.Updater.runtimeconfig.dev.json" /S /Q +del "src\IronyModManager\bin\x64\osx-x64\net8.0\publish\osx-x64\IronyModManager.GameHandler.runtimeconfig.dev.json" /S /Q +del "src\IronyModManager\bin\x64\osx-x64\net8.0\publish\osx-x64\steam_api64.dll" /S /Q +xcopy "References\CopyAll\*.*" "src\IronyModManager\bin\x64\osx-x64\net8.0\publish\osx-x64\" /Y /S /D REM Why on earth cannot nuget include these? Also the documentation sucks in this regard -xcopy "References\Conditional\Steamworks\OSX-Linux-x64\steam_api.bundle\Contents\MacOS\*.*" "src\IronyModManager\bin\x64\osx-x64\net7.0\publish\osx-x64\" /Y /S /D +xcopy "References\Conditional\Steamworks\OSX-Linux-x64\steam_api.bundle\Contents\MacOS\*.*" "src\IronyModManager\bin\x64\osx-x64\net8.0\publish\osx-x64\" /Y /S /D cd publish \ No newline at end of file diff --git a/publish/publish-win-x64.bat b/publish/publish-win-x64.bat index 2b5e6bb8e..66a6321f9 100644 --- a/publish/publish-win-x64.bat +++ b/publish/publish-win-x64.bat @@ -17,17 +17,17 @@ dotnet publish src\IronyModManager.Common\IronyModManager.Common.csproj /p:Publ dotnet publish src\IronyModManager.Updater\IronyModManager.Updater.csproj /p:PublishProfile=src\IronyModManager.Updater\Properties\PublishProfiles\win-x64.pubxml --configuration win-x64 dotnet publish src\IronyModManager.GameHandler\IronyModManager.GameHandler.csproj /p:PublishProfile=src\IronyModManager.GameHandler\Properties\PublishProfiles\win-x64.pubxml --configuration win-x64 dotnet publish src\IronyModManager\IronyModManager.csproj /p:PublishProfile=src\IronyModManager\Properties\PublishProfiles\win-x64.pubxml --configuration win-x64 -xcopy "src\IronyModManager\bin\win-x64\net7.0\win-x64\*.dll" "src\IronyModManager\bin\x64\win-x64\net7.0\publish\win-x64\" /Y /S /D -xcopy "src\IronyModManager\bin\win-x64\net7.0\win-x64\*.json" "src\IronyModManager\bin\x64\win-x64\net7.0\publish\win-x64\" /Y /S /D -xcopy "src\IronyModManager\bin\win-x64\net7.0\win-x64\*.pdb" "src\IronyModManager\bin\x64\win-x64\net7.0\publish\win-x64\" /Y /S /D -xcopy "src\IronyModManager.Updater\bin\x64\win-x64\net7.0\publish\win-x64\*.*" "src\IronyModManager\bin\x64\win-x64\net7.0\publish\win-x64\" /Y /S /D -xcopy "src\IronyModManager.GameHandler\bin\x64\win-x64\net7.0\publish\win-x64\*.*" "src\IronyModManager\bin\x64\win-x64\net7.0\publish\win-x64\" /Y /S /D +xcopy "src\IronyModManager\bin\win-x64\net8.0\win-x64\*.dll" "src\IronyModManager\bin\x64\win-x64\net8.0\publish\win-x64\" /Y /S /D +xcopy "src\IronyModManager\bin\win-x64\net8.0\win-x64\*.json" "src\IronyModManager\bin\x64\win-x64\net8.0\publish\win-x64\" /Y /S /D +xcopy "src\IronyModManager\bin\win-x64\net8.0\win-x64\*.pdb" "src\IronyModManager\bin\x64\win-x64\net8.0\publish\win-x64\" /Y /S /D +xcopy "src\IronyModManager.Updater\bin\x64\win-x64\net8.0\publish\win-x64\*.*" "src\IronyModManager\bin\x64\win-x64\net8.0\publish\win-x64\" /Y /S /D +xcopy "src\IronyModManager.GameHandler\bin\x64\win-x64\net8.0\publish\win-x64\*.*" "src\IronyModManager\bin\x64\win-x64\net8.0\publish\win-x64\" /Y /S /D REM Temp fix due to avalonia bug -xcopy "%userprofile%\.nuget\packages\avalonia.angle.windows.natives\2.1.0.2020091801\runtimes\win7-x64\native\av_libglesv2.dll" "src\IronyModManager\bin\x64\win-x64\net7.0\publish\win-x64\" /Y /S /D -del "src\IronyModManager\bin\x64\win-x64\net7.0\publish\win-x64\IronyModManager.runtimeconfig.dev.json" /S /Q -del "src\IronyModManager\bin\x64\win-x64\net7.0\publish\win-x64\IronyModManager.Updater.runtimeconfig.dev.json" /S /Q -del "src\IronyModManager\bin\x64\win-x64\net7.0\publish\win-x64\IronyModManager.GameHandler.runtimeconfig.dev.json" /S /Q -del "src\IronyModManager\bin\x64\win-x64\net7.0\publish\win-x64\steam_api64.dll" /S /Q -xcopy "References\CopyAll\*.*" "src\IronyModManager\bin\x64\win-x64\net7.0\publish\win-x64\" /Y /S /D -xcopy "References\Conditional\Steamworks\Windows-x64\*.*" "src\IronyModManager\bin\x64\win-x64\net7.0\publish\win-x64\" /Y /S /D +xcopy "%userprofile%\.nuget\packages\avalonia.angle.windows.natives\2.1.0.2020091801\runtimes\win7-x64\native\av_libglesv2.dll" "src\IronyModManager\bin\x64\win-x64\net8.0\publish\win-x64\" /Y /S /D +del "src\IronyModManager\bin\x64\win-x64\net8.0\publish\win-x64\IronyModManager.runtimeconfig.dev.json" /S /Q +del "src\IronyModManager\bin\x64\win-x64\net8.0\publish\win-x64\IronyModManager.Updater.runtimeconfig.dev.json" /S /Q +del "src\IronyModManager\bin\x64\win-x64\net8.0\publish\win-x64\IronyModManager.GameHandler.runtimeconfig.dev.json" /S /Q +del "src\IronyModManager\bin\x64\win-x64\net8.0\publish\win-x64\steam_api64.dll" /S /Q +xcopy "References\CopyAll\*.*" "src\IronyModManager\bin\x64\win-x64\net8.0\publish\win-x64\" /Y /S /D +xcopy "References\Conditional\Steamworks\Windows-x64\*.*" "src\IronyModManager\bin\x64\win-x64\net8.0\publish\win-x64\" /Y /S /D cd publish \ No newline at end of file diff --git a/publish/setup/win-installer.iss b/publish/setup/win-installer.iss index b2aeda10e..1b85a5357 100644 --- a/publish/setup/win-installer.iss +++ b/publish/setup/win-installer.iss @@ -5,8 +5,8 @@ #define MyAppPublisher "Mario" #define MyAppURL "https://bcssov.github.io/IronyModManager/" #define MyAppExeName "IronyModManager.exe" -#define PublishPath "..\..\src\IronyModManager\bin\x64\win-x64\net7.0\publish" -#define SourcePath "..\..\src\IronyModManager\bin\x64\win-x64\net7.0\publish\win-x64" +#define PublishPath "..\..\src\IronyModManager\bin\x64\win-x64\net8.0\publish" +#define SourcePath "..\..\src\IronyModManager\bin\x64\win-x64\net8.0\publish\win-x64" #define MyAppVersion GetStringFileInfo(SourcePath + "\" + MyAppExeName, "ProductVersion") [Code] diff --git a/src/Irony.AppCastGenerator/Irony.AppCastGenerator.csproj b/src/Irony.AppCastGenerator/Irony.AppCastGenerator.csproj index 55dabd059..3dd742930 100644 --- a/src/Irony.AppCastGenerator/Irony.AppCastGenerator.csproj +++ b/src/Irony.AppCastGenerator/Irony.AppCastGenerator.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 ../IronyModManager/Assets/logo.ico Irony App Cast Generator Component LICENSE @@ -28,7 +28,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/IronyModManager.Common/Extensions.cs b/src/IronyModManager.Common/Extensions.cs index 2659faaf6..5490f2ebf 100644 --- a/src/IronyModManager.Common/Extensions.cs +++ b/src/IronyModManager.Common/Extensions.cs @@ -4,17 +4,19 @@ // Created : 01-14-2020 // // Last Modified By : Mario -// Last Modified On : 11-29-2022 +// Last Modified On : 02-20-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; using Avalonia; @@ -25,6 +27,7 @@ using IronyModManager.DI; using IronyModManager.Platform.Configuration; using IronyModManager.Shared; +using ReactiveUI; namespace IronyModManager.Common { @@ -52,7 +55,23 @@ public static void EnsureTitlebarSpacing(this Window window) } /// - /// Safes the invoke. + /// Observables WhenAnyValue. + /// + /// The type of the TSender. + /// The type of the TRet. + /// The type of the T1. + /// The sender. + /// The Property. + /// The selector. + /// an IObservable with TRets . + public static IObservable ObservableWhenAnyValue(this TSender sender, Expression> property, Func selector) + { + var innerSelector = selector; + return sender.WhenAny(property, c1 => innerSelector(c1.Value)); + } + + /// + /// Safe invoke. /// /// The dispatcher. /// The action. @@ -64,12 +83,12 @@ public static void SafeInvoke(this Dispatcher dispatcher, Action action) } else { - dispatcher.InvokeAsync(() => action()); + dispatcher.InvokeAsync(action); } } /// - /// Safes the invoke asynchronous. + /// Safes invoke asynchronous. /// /// /// The dispatcher. @@ -83,12 +102,12 @@ public static Task SafeInvokeAsync(this Dispatcher dispatcher, Func action()); + return dispatcher.InvokeAsync(action); } } /// - /// Safes the invoke asynchronous. + /// Safes invoke asynchronous. /// /// The dispatcher. /// The action. @@ -101,7 +120,7 @@ public static async Task SafeInvokeAsync(this Dispatcher dispatcher, Action acti } else { - await dispatcher.InvokeAsync(() => action()); + await dispatcher.InvokeAsync(action); } } @@ -113,7 +132,7 @@ public static async Task SafeInvokeAsync(this Dispatcher dispatcher, Action acti /// IDisposable. public static IDisposable SubscribeObservable(this IObservable source) { - return ObservableExtensions.Subscribe(source); + return source.Subscribe(); } /// @@ -125,7 +144,7 @@ public static IDisposable SubscribeObservable(this IObservable source) /// IDisposable. public static IDisposable SubscribeObservable(this IObservable source, Action onNext) { - return ObservableExtensions.Subscribe(source, onNext); + return source.Subscribe(onNext); } /// @@ -139,7 +158,7 @@ public static IDisposable SubscribeObservable(this IObservable source, Act public static IDisposable SubscribeObservable(this IObservable source, Action onNext, Action onError) { // Seriously annoyed with conflicting namespaces - return ObservableExtensions.Subscribe(source, onNext, onError); + return source.Subscribe(onNext, onError); } /// @@ -153,7 +172,7 @@ public static IDisposable SubscribeObservable(this IObservable source, Act public static IDisposable SubscribeObservable(this IObservable source, Action onNext, Action onCompleted) { // Seriously annoyed with conflicting namespaces - return ObservableExtensions.Subscribe(source, onNext, onCompleted); + return source.Subscribe(onNext, onCompleted); } /// @@ -167,7 +186,7 @@ public static IDisposable SubscribeObservable(this IObservable source, Act /// IDisposable. public static IDisposable SubscribeObservable(this IObservable source, Action onNext, Action onError, Action onCompleted) { - return ObservableExtensions.Subscribe(source, onNext, onError, onCompleted); + return source.Subscribe(onNext, onError, onCompleted); } /// @@ -179,7 +198,7 @@ public static IDisposable SubscribeObservable(this IObservable source, Act /// The token. public static void SubscribeObservable(this IObservable source, IObserver observer, CancellationToken token) { - ObservableExtensions.Subscribe(source, observer, token); + source.Subscribe(observer, token); } /// @@ -190,7 +209,7 @@ public static void SubscribeObservable(this IObservable source, IObserver< /// The token. public static void SubscribeObservable(this IObservable source, CancellationToken token) { - ObservableExtensions.Subscribe(source, token); + source.Subscribe(token); } /// @@ -202,7 +221,7 @@ public static void SubscribeObservable(this IObservable source, Cancellati /// The token. public static void SubscribeObservable(this IObservable source, Action onNext, CancellationToken token) { - ObservableExtensions.Subscribe(source, onNext, token); + source.Subscribe(onNext, token); } /// @@ -215,7 +234,7 @@ public static void SubscribeObservable(this IObservable source, Action /// The token. public static void SubscribeObservable(this IObservable source, Action onNext, Action onError, CancellationToken token) { - ObservableExtensions.Subscribe(source, onNext, onError, token); + source.Subscribe(onNext, onError, token); } /// @@ -228,7 +247,7 @@ public static void SubscribeObservable(this IObservable source, Action /// The token. public static void SubscribeObservable(this IObservable source, Action onNext, Action onCompleted, CancellationToken token) { - ObservableExtensions.Subscribe(source, onNext, onCompleted, token); + source.Subscribe(onNext, onCompleted, token); } /// @@ -242,7 +261,7 @@ public static void SubscribeObservable(this IObservable source, Action /// The token. public static void SubscribeObservable(this IObservable source, Action onNext, Action onError, Action onCompleted, CancellationToken token) { - ObservableExtensions.Subscribe(source, onNext, onError, onCompleted, token); + source.Subscribe(onNext, onError, onCompleted, token); } /// @@ -254,11 +273,11 @@ public static void SubscribeObservable(this IObservable source, Action /// IDisposable. public static IDisposable SubscribeObservableSafe(this IObservable source, IObserver observer) { - return ObservableExtensions.SubscribeSafe(source, observer); + return source.SubscribeSafe(observer); } /// - /// Converts to avalonialist. + /// Converts to AvaloniaList. /// /// /// The col. @@ -269,7 +288,7 @@ public static AvaloniaList ToAvaloniaList(this IEnumerable col) } /// - /// Converts to localizedpercentage. + /// Converts to LocalizedPercentage. /// /// The number. /// System.String. @@ -279,7 +298,7 @@ public static string ToLocalizedPercentage(this int number) } /// - /// Converts to localizedpercentage. + /// Converts to LocalizedPercentage. /// /// The number. /// System.String. @@ -289,7 +308,7 @@ public static string ToLocalizedPercentage(this double number) } /// - /// Converts to observablecollection. + /// Converts to ObservableCollection. /// /// /// The col. @@ -300,7 +319,7 @@ public static ObservableCollection ToObservableCollection(this IEnumerable } /// - /// Converts to sourcelist. + /// Converts to SourceList. /// /// /// The col. diff --git a/src/IronyModManager.Common/IronyModManager.Common.csproj b/src/IronyModManager.Common/IronyModManager.Common.csproj index 8a2262dc5..8493abf97 100644 --- a/src/IronyModManager.Common/IronyModManager.Common.csproj +++ b/src/IronyModManager.Common/IronyModManager.Common.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 Irony Mod Manager Common Component LICENSE logo.png @@ -50,9 +50,9 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + diff --git a/src/IronyModManager.Common/Properties/PublishProfiles/linux-x64.pubxml b/src/IronyModManager.Common/Properties/PublishProfiles/linux-x64.pubxml index 04545ee0d..b8f7f6db0 100644 --- a/src/IronyModManager.Common/Properties/PublishProfiles/linux-x64.pubxml +++ b/src/IronyModManager.Common/Properties/PublishProfiles/linux-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\linux-x64\net7.0\publish\linux-x64 + net8.0 + bin\x64\linux-x64\net8.0\publish\linux-x64 linux-x64 true False diff --git a/src/IronyModManager.Common/Properties/PublishProfiles/osx-x64.pubxml b/src/IronyModManager.Common/Properties/PublishProfiles/osx-x64.pubxml index df6bd9f9f..ff92dad82 100644 --- a/src/IronyModManager.Common/Properties/PublishProfiles/osx-x64.pubxml +++ b/src/IronyModManager.Common/Properties/PublishProfiles/osx-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\osx-x64\net7.0\publish\osx-x64 + net8.0 + bin\x64\osx-x64\net8.0\publish\osx-x64 osx-x64 true False diff --git a/src/IronyModManager.Common/Properties/PublishProfiles/win-x64.pubxml b/src/IronyModManager.Common/Properties/PublishProfiles/win-x64.pubxml index 1cd122792..cbd6468db 100644 --- a/src/IronyModManager.Common/Properties/PublishProfiles/win-x64.pubxml +++ b/src/IronyModManager.Common/Properties/PublishProfiles/win-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\win-x64\net7.0\publish\win-x64 + net8.0 + bin\x64\win-x64\net8.0\publish\win-x64 win-x64 true False diff --git a/src/IronyModManager.DI/IronyModManager.DI.csproj b/src/IronyModManager.DI/IronyModManager.DI.csproj index 0707df5f6..dfe8e5742 100644 --- a/src/IronyModManager.DI/IronyModManager.DI.csproj +++ b/src/IronyModManager.DI/IronyModManager.DI.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 Mario Mario Irony Mod Manager DI Component @@ -44,7 +44,7 @@ - + @@ -53,7 +53,7 @@ - + diff --git a/src/IronyModManager.DI/MessageBus/MessageBusMemoryProvider.cs b/src/IronyModManager.DI/MessageBus/MessageBusMemoryProvider.cs index f2081b95b..f92625324 100644 --- a/src/IronyModManager.DI/MessageBus/MessageBusMemoryProvider.cs +++ b/src/IronyModManager.DI/MessageBus/MessageBusMemoryProvider.cs @@ -5,7 +5,7 @@ // Created : 06-25-2023 // // Last Modified By : Mario -// Last Modified On : 06-25-2023 +// Last Modified On : 02-09-2024 // *********************************************************************** // // Mario @@ -18,6 +18,7 @@ using System.Threading.Tasks; using SlimMessageBus.Host; using SlimMessageBus.Host.Memory; +using SlimMessageBus.Host.Services; namespace IronyModManager.DI.MessageBus { @@ -32,7 +33,7 @@ internal class MessageBusMemoryProvider : MemoryMessageBus #region Constructors /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The settings. public MessageBusMemoryProvider(MessageBusSettings settings) : base(settings, new MemoryMessageBusSettings() { EnableMessageSerialization = false }) @@ -41,24 +42,15 @@ internal class MessageBusMemoryProvider : MemoryMessageBus #endregion Constructors - #region Methods + #region Properties /// - /// Asserts the settings. + /// Gets the validation service. /// - protected override void AssertSettings() - { - // Sigh, let's fix the validation mess - foreach (var consumerSettings in Settings.Consumers) - { - if (consumerSettings.ConsumerMethodInfo != null && consumerSettings.ConsumerMethod == null) - { - consumerSettings.ConsumerMethod = ReflectionUtils.GenerateMethodCallToFunc>(consumerSettings.ConsumerMethodInfo, consumerSettings.ConsumerType, typeof(Task), consumerSettings.MessageType); - } - } - base.AssertSettings(); - } + /// The validation service. + // Will you make up your mind where you want AssertSettings to be + protected override IMessageBusSettingsValidationService ValidationService => new MessageBusValidationService(Settings); - #endregion Methods + #endregion Properties } } diff --git a/src/IronyModManager.DI/MessageBus/MessageBusValidationService.cs b/src/IronyModManager.DI/MessageBus/MessageBusValidationService.cs new file mode 100644 index 000000000..2069a2586 --- /dev/null +++ b/src/IronyModManager.DI/MessageBus/MessageBusValidationService.cs @@ -0,0 +1,65 @@ + +// *********************************************************************** +// Assembly : IronyModManager.DI +// Author : Mario +// Created : 02-09-2024 +// +// Last Modified By : Mario +// Last Modified On : 02-09-2024 +// *********************************************************************** +// +// Mario +// +// +// *********************************************************************** +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SlimMessageBus.Host; +using SlimMessageBus.Host.Services; + +namespace IronyModManager.DI.MessageBus +{ + + /// + /// Class MessageBusValidationService. + /// Implements the + /// + /// + internal class MessageBusValidationService : DefaultMessageBusSettingsValidationService + { + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The settings. + public MessageBusValidationService(MessageBusSettings settings) : base(settings) + { + } + + #endregion Constructors + + #region Methods + + /// + /// Asserts the settings. + /// + public override void AssertSettings() + { + // Sigh, let's fix the validation mess + foreach (var consumerSettings in Settings.Consumers) + { + if (consumerSettings.ConsumerMethodInfo != null && consumerSettings.ConsumerMethod == null) + { + consumerSettings.ConsumerMethod = ReflectionUtils.GenerateMethodCallToFunc>(consumerSettings.ConsumerMethodInfo, consumerSettings.ConsumerType, typeof(Task), consumerSettings.MessageType); + } + } + base.AssertSettings(); + } + + #endregion Methods + } +} diff --git a/src/IronyModManager.DI/Properties/PublishProfiles/linux-x64.pubxml b/src/IronyModManager.DI/Properties/PublishProfiles/linux-x64.pubxml index 04545ee0d..b8f7f6db0 100644 --- a/src/IronyModManager.DI/Properties/PublishProfiles/linux-x64.pubxml +++ b/src/IronyModManager.DI/Properties/PublishProfiles/linux-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\linux-x64\net7.0\publish\linux-x64 + net8.0 + bin\x64\linux-x64\net8.0\publish\linux-x64 linux-x64 true False diff --git a/src/IronyModManager.DI/Properties/PublishProfiles/osx-x64.pubxml b/src/IronyModManager.DI/Properties/PublishProfiles/osx-x64.pubxml index df6bd9f9f..ff92dad82 100644 --- a/src/IronyModManager.DI/Properties/PublishProfiles/osx-x64.pubxml +++ b/src/IronyModManager.DI/Properties/PublishProfiles/osx-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\osx-x64\net7.0\publish\osx-x64 + net8.0 + bin\x64\osx-x64\net8.0\publish\osx-x64 osx-x64 true False diff --git a/src/IronyModManager.DI/Properties/PublishProfiles/win-x64.pubxml b/src/IronyModManager.DI/Properties/PublishProfiles/win-x64.pubxml index 1cd122792..cbd6468db 100644 --- a/src/IronyModManager.DI/Properties/PublishProfiles/win-x64.pubxml +++ b/src/IronyModManager.DI/Properties/PublishProfiles/win-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\win-x64\net7.0\publish\win-x64 + net8.0 + bin\x64\win-x64\net8.0\publish\win-x64 win-x64 true False diff --git a/src/IronyModManager.GameHandler/IronyModManager.GameHandler.csproj b/src/IronyModManager.GameHandler/IronyModManager.GameHandler.csproj index 79928b561..43338297b 100644 --- a/src/IronyModManager.GameHandler/IronyModManager.GameHandler.csproj +++ b/src/IronyModManager.GameHandler/IronyModManager.GameHandler.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 ../IronyModManager/Assets/logo.ico IronyModManager Game Launcher Component LICENSE @@ -47,7 +47,7 @@ - + diff --git a/src/IronyModManager.GameHandler/Properties/PublishProfiles/linux-x64.pubxml b/src/IronyModManager.GameHandler/Properties/PublishProfiles/linux-x64.pubxml index 04545ee0d..b8f7f6db0 100644 --- a/src/IronyModManager.GameHandler/Properties/PublishProfiles/linux-x64.pubxml +++ b/src/IronyModManager.GameHandler/Properties/PublishProfiles/linux-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\linux-x64\net7.0\publish\linux-x64 + net8.0 + bin\x64\linux-x64\net8.0\publish\linux-x64 linux-x64 true False diff --git a/src/IronyModManager.GameHandler/Properties/PublishProfiles/osx-x64.pubxml b/src/IronyModManager.GameHandler/Properties/PublishProfiles/osx-x64.pubxml index df6bd9f9f..ff92dad82 100644 --- a/src/IronyModManager.GameHandler/Properties/PublishProfiles/osx-x64.pubxml +++ b/src/IronyModManager.GameHandler/Properties/PublishProfiles/osx-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\osx-x64\net7.0\publish\osx-x64 + net8.0 + bin\x64\osx-x64\net8.0\publish\osx-x64 osx-x64 true False diff --git a/src/IronyModManager.GameHandler/Properties/PublishProfiles/win-x64.pubxml b/src/IronyModManager.GameHandler/Properties/PublishProfiles/win-x64.pubxml index 1cd122792..cbd6468db 100644 --- a/src/IronyModManager.GameHandler/Properties/PublishProfiles/win-x64.pubxml +++ b/src/IronyModManager.GameHandler/Properties/PublishProfiles/win-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\win-x64\net7.0\publish\win-x64 + net8.0 + bin\x64\win-x64\net8.0\publish\win-x64 win-x64 true False diff --git a/src/IronyModManager.IO.Common/IronyModManager.IO.Common.csproj b/src/IronyModManager.IO.Common/IronyModManager.IO.Common.csproj index a1b6970e5..1c4bb3cbc 100644 --- a/src/IronyModManager.IO.Common/IronyModManager.IO.Common.csproj +++ b/src/IronyModManager.IO.Common/IronyModManager.IO.Common.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 true ../../keys/Irony-Main.snk true diff --git a/src/IronyModManager.IO.Common/Mods/IModPatchExporter.cs b/src/IronyModManager.IO.Common/Mods/IModPatchExporter.cs index 1298a8f92..fe63da257 100644 --- a/src/IronyModManager.IO.Common/Mods/IModPatchExporter.cs +++ b/src/IronyModManager.IO.Common/Mods/IModPatchExporter.cs @@ -4,15 +4,17 @@ // Created : 03-31-2020 // // Last Modified By : Mario -// Last Modified On : 03-06-2022 +// Last Modified On : 02-25-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using IronyModManager.IO.Common.Mods.Models; @@ -39,6 +41,13 @@ public interface IModPatchExporter /// Task<System.Boolean>. Task ExportDefinitionAsync(ModPatchExporterParameters parameters); + /// + /// Gets an allowed languages async. + /// + /// The parameters. + /// A Task containing IReadOnlyCollection of strings. + Task> GetAllowedLanguagesAsync(ModPatchExporterParameters parameters); + /// /// Gets the patch files. /// diff --git a/src/IronyModManager.IO.Common/Mods/ModPatchExporterParameters.cs b/src/IronyModManager.IO.Common/Mods/ModPatchExporterParameters.cs index 4aece4bd5..28baa7a1c 100644 --- a/src/IronyModManager.IO.Common/Mods/ModPatchExporterParameters.cs +++ b/src/IronyModManager.IO.Common/Mods/ModPatchExporterParameters.cs @@ -4,15 +4,17 @@ // Created : 04-02-2020 // // Last Modified By : Mario -// Last Modified On : 11-01-2021 +// Last Modified On : 02-25-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; +using System.Linq; using IronyModManager.Shared; using IronyModManager.Shared.Models; @@ -26,6 +28,14 @@ public class ModPatchExporterParameters { #region Properties + /// + /// Gets or sets a value representing the allowed languages. + /// + /// + /// The allowed languages. + /// + public IEnumerable AllowedLanguages { get; set; } + /// /// Gets or sets the conflicts. /// diff --git a/src/IronyModManager.IO.Common/Mods/Models/IPatchState.cs b/src/IronyModManager.IO.Common/Mods/Models/IPatchState.cs index a4b41cc02..eb89d4e18 100644 --- a/src/IronyModManager.IO.Common/Mods/Models/IPatchState.cs +++ b/src/IronyModManager.IO.Common/Mods/Models/IPatchState.cs @@ -1,4 +1,5 @@ -// *********************************************************************** + +// *********************************************************************** // Assembly : IronyModManager.IO.Common // Author : Mario // Created : 04-06-2020 @@ -11,12 +12,15 @@ // // // *********************************************************************** + using System; using System.Collections.Generic; +using System.Linq; using IronyModManager.Shared.Models; namespace IronyModManager.IO.Common.Mods.Models { + /// /// Interface IPatchState /// @@ -24,6 +28,14 @@ public interface IPatchState { #region Properties + /// + /// Gets or sets a value representing the allowed languages. + /// + /// + /// The allowed languages. + /// + IEnumerable AllowedLanguages { get; set; } + /// /// Gets or sets the conflict history. /// diff --git a/src/IronyModManager.IO.Common/Properties/PublishProfiles/linux-x64.pubxml b/src/IronyModManager.IO.Common/Properties/PublishProfiles/linux-x64.pubxml index 04545ee0d..b8f7f6db0 100644 --- a/src/IronyModManager.IO.Common/Properties/PublishProfiles/linux-x64.pubxml +++ b/src/IronyModManager.IO.Common/Properties/PublishProfiles/linux-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\linux-x64\net7.0\publish\linux-x64 + net8.0 + bin\x64\linux-x64\net8.0\publish\linux-x64 linux-x64 true False diff --git a/src/IronyModManager.IO.Common/Properties/PublishProfiles/osx-x64.pubxml b/src/IronyModManager.IO.Common/Properties/PublishProfiles/osx-x64.pubxml index df6bd9f9f..ff92dad82 100644 --- a/src/IronyModManager.IO.Common/Properties/PublishProfiles/osx-x64.pubxml +++ b/src/IronyModManager.IO.Common/Properties/PublishProfiles/osx-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\osx-x64\net7.0\publish\osx-x64 + net8.0 + bin\x64\osx-x64\net8.0\publish\osx-x64 osx-x64 true False diff --git a/src/IronyModManager.IO.Common/Properties/PublishProfiles/win-x64.pubxml b/src/IronyModManager.IO.Common/Properties/PublishProfiles/win-x64.pubxml index 1cd122792..cbd6468db 100644 --- a/src/IronyModManager.IO.Common/Properties/PublishProfiles/win-x64.pubxml +++ b/src/IronyModManager.IO.Common/Properties/PublishProfiles/win-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\win-x64\net7.0\publish\win-x64 + net8.0 + bin\x64\win-x64\net8.0\publish\win-x64 win-x64 true False diff --git a/src/IronyModManager.IO.Tests/IronyModManager.IO.Tests.csproj b/src/IronyModManager.IO.Tests/IronyModManager.IO.Tests.csproj index b46cfc675..f63e38d16 100644 --- a/src/IronyModManager.IO.Tests/IronyModManager.IO.Tests.csproj +++ b/src/IronyModManager.IO.Tests/IronyModManager.IO.Tests.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 true ../../keys/Irony-Main.snk Debug;Release;Functional_Test;osx-x64;linux-x64;win-x64 @@ -27,17 +27,17 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/IronyModManager.IO/IronyModManager.IO.csproj b/src/IronyModManager.IO/IronyModManager.IO.csproj index 39e3f044a..ab408e547 100644 --- a/src/IronyModManager.IO/IronyModManager.IO.csproj +++ b/src/IronyModManager.IO/IronyModManager.IO.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 true ../../keys/Irony-Main.snk true @@ -43,7 +43,7 @@ - + @@ -53,7 +53,7 @@ - + diff --git a/src/IronyModManager.IO/Mods/Importers/ParadoxLauncherImporter.cs b/src/IronyModManager.IO/Mods/Importers/ParadoxLauncherImporter.cs index ca3f38260..19c0141a8 100644 --- a/src/IronyModManager.IO/Mods/Importers/ParadoxLauncherImporter.cs +++ b/src/IronyModManager.IO/Mods/Importers/ParadoxLauncherImporter.cs @@ -4,13 +4,14 @@ // Created : 08-12-2020 // // Last Modified By : Mario -// Last Modified On : 02-23-2023 +// Last Modified On : 03-04-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; using System.Data; @@ -28,39 +29,25 @@ namespace IronyModManager.IO.Mods.Importers { /// - /// Class ParadoxLauncherImporter. + /// The paradox launcher importer. /// [ExcludeFromCoverage("Skipping testing IO logic.")] - internal class ParadoxLauncherImporter + internal class ParadoxLauncherImporter(ILogger logger) { #region Fields /// /// The logger /// - private readonly ILogger logger; + private readonly ILogger logger = logger; /// /// The trace /// - private readonly SQLTraceLog trace; + private readonly SQLTraceLog trace = new(logger); #endregion Fields - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - public ParadoxLauncherImporter(ILogger logger) - { - this.logger = logger; - trace = new SQLTraceLog(logger); - } - - #endregion Constructors - #region Enums /// @@ -99,6 +86,7 @@ public async Task DatabaseImportAsync(ModCollectionExpo { return null; } + // Caching sucks in this ORM DbFieldCache.Flush(); FieldCache.Flush(); @@ -140,13 +128,15 @@ public virtual async Task JsonImportAsync(ModCollection { return (ex, null); } + if (!string.IsNullOrWhiteSpace(model.Game) && !string.IsNullOrWhiteSpace(model.Name)) { // Validate whether this really is v2 (execting length larger than 4 as a dumb best guess) - if (model.Mods.Any(p => p.Position.Length >= 4)) + if (model.Mods.Any(p => p.Position.Length > 4)) { var result = DIResolver.Get(); result.Name = model.Name; + // Will need to lookup the game and mod ids in the mod service result.Game = model.Game; var mods = model.Mods.Where(p => p.Enabled).OrderBy(p => p.Position); @@ -157,18 +147,22 @@ public virtual async Task JsonImportAsync(ModCollection { result.ParadoxId = pdxid; } + if (long.TryParse(p.SteamId, out var steamId)) { result.SteamId = steamId; } + return result; }).ToList(); return (null, result); } } } + return (null, null); } + async Task<(Exception, ICollectionImportResult)> parseV3() { var content = await File.ReadAllTextAsync(parameters.File); @@ -183,10 +177,12 @@ public virtual async Task JsonImportAsync(ModCollection { return (ex, null); } + if (!string.IsNullOrWhiteSpace(model.Game) && !string.IsNullOrWhiteSpace(model.Name)) { var result = DIResolver.Get(); result.Name = model.Name; + // Will need to lookup the game and mod ids in the mod service result.Game = model.Game; var mods = model.Mods.Where(p => p.Enabled).OrderBy(p => p.Position); @@ -197,15 +193,18 @@ public virtual async Task JsonImportAsync(ModCollection { result.ParadoxId = pdxid; } + if (long.TryParse(p.SteamId, out var steamId)) { result.SteamId = steamId; } + return result; }).ToList(); return (null, result); } } + return (null, null); } @@ -217,24 +216,29 @@ public virtual async Task JsonImportAsync(ModCollection { return result.Item2; } + if (result.Item1 != null) { exceptions.Add(result.Item1); } + result = await parseV3(); if (result.Item2 != null) { return result.Item2; } + if (result.Item1 != null) { exceptions.Add(result.Item1); } + if (exceptions.Any()) { throw new AggregateException(exceptions); } } + return null; } @@ -251,7 +255,7 @@ protected virtual async Task DatabaseImportv2Async(ModC var activeCollection = (await con.QueryAsync(p => p.IsActive == true, trace: trace)).FirstOrDefault(); if (activeCollection != null) { - var collectionMods = await con.QueryAsync(p => p.PlaysetId == activeCollection.Id.ToString(), trace: trace); + var collectionMods = await con.QueryAsync(p => p.PlaysetId == activeCollection.Id, trace: trace); if (collectionMods?.Count() > 0) { var mods = await con.QueryAllAsync(trace: trace); @@ -269,6 +273,7 @@ protected virtual async Task DatabaseImportv2Async(ModC { result.FullPaths = validMods.Select(p => p.DirPath.StandardizeDirectorySeparator()).ToList(); } + result.ModNames = validMods.Select(p => p.DisplayName).ToList(); return result; } @@ -279,8 +284,8 @@ protected virtual async Task DatabaseImportv2Async(ModC { logger.Error(ex); } + con.Close(); - con.Dispose(); return null; } @@ -297,7 +302,7 @@ protected virtual async Task DatabaseImportv3Async(ModC var activeCollection = (await con.QueryAsync(p => p.IsActive == true, trace: trace)).FirstOrDefault(); if (activeCollection != null) { - var collectionMods = await con.QueryAsync(p => p.PlaysetId == activeCollection.Id.ToString(), trace: trace); + var collectionMods = await con.QueryAsync(p => p.PlaysetId == activeCollection.Id, trace: trace); if (collectionMods?.Count() > 0) { var mods = await con.QueryAllAsync(trace: trace); @@ -315,6 +320,7 @@ protected virtual async Task DatabaseImportv3Async(ModC { result.FullPaths = validMods.Select(p => p.DirPath.StandardizeDirectorySeparator()).ToList(); } + result.ModNames = validMods.Select(p => p.DisplayName).ToList(); return result; } @@ -325,8 +331,8 @@ protected virtual async Task DatabaseImportv3Async(ModC { logger.Error(ex); } + con.Close(); - con.Dispose(); return null; } @@ -343,7 +349,7 @@ protected virtual async Task DatabaseImportv4Async(ModC var activeCollection = (await con.QueryAsync(p => p.IsActive == true, trace: trace)).FirstOrDefault(); if (activeCollection != null) { - var collectionMods = await con.QueryAsync(p => p.PlaysetId == activeCollection.Id.ToString(), trace: trace); + var collectionMods = await con.QueryAsync(p => p.PlaysetId == activeCollection.Id, trace: trace); if (collectionMods?.Count() > 0) { var mods = await con.QueryAllAsync(trace: trace); @@ -361,6 +367,7 @@ protected virtual async Task DatabaseImportv4Async(ModC { result.FullPaths = validMods.Select(p => p.DirPath.StandardizeDirectorySeparator()).ToList(); } + result.ModNames = validMods.Select(p => p.DisplayName).ToList(); return result; } @@ -371,8 +378,8 @@ protected virtual async Task DatabaseImportv4Async(ModC { logger.Error(ex); } + con.Close(); - con.Dispose(); return null; } @@ -383,7 +390,7 @@ protected virtual async Task DatabaseImportv4Async(ModC /// System.String. protected virtual string GetDbPath(ModCollectionExporterParams parameters) { - return Path.Combine(Path.GetDirectoryName(parameters.ModDirectory), Constants.Sql_db_path); + return Path.Combine(Path.GetDirectoryName(parameters.ModDirectory)!, Constants.Sql_db_path); } /// @@ -413,6 +420,7 @@ private async Task GetVersionAsync(ModCollectionExporterParams paramete { return Version.v5; } + if (changes.Any(c => c.Name.Equals(Constants.SqlV4Id.Name, StringComparison.OrdinalIgnoreCase))) { return Version.v4; @@ -422,8 +430,8 @@ private async Task GetVersionAsync(ModCollectionExporterParams paramete catch { } + con.Close(); - con.Dispose(); return Version.Default; } diff --git a/src/IronyModManager.IO/Mods/Importers/ParadoxLauncherImporterBeta.cs b/src/IronyModManager.IO/Mods/Importers/ParadoxLauncherImporterBeta.cs index 80dd3b170..f326d62c8 100644 --- a/src/IronyModManager.IO/Mods/Importers/ParadoxLauncherImporterBeta.cs +++ b/src/IronyModManager.IO/Mods/Importers/ParadoxLauncherImporterBeta.cs @@ -4,13 +4,14 @@ // Created : 11-16-2021 // // Last Modified By : Mario -// Last Modified On : 11-16-2021 +// Last Modified On : 03-04-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; using System.IO; @@ -27,21 +28,11 @@ namespace IronyModManager.IO.Mods.Importers /// Implements the /// /// + /// The logger. + /// Initializes a new instance of the class. [ExcludeFromCoverage("Skipping testing IO logic.")] - internal class ParadoxLauncherImporterBeta : ParadoxLauncherImporter + internal class ParadoxLauncherImporterBeta(ILogger logger) : ParadoxLauncherImporter(logger) { - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - public ParadoxLauncherImporterBeta(ILogger logger) : base(logger) - { - } - - #endregion Constructors - #region Methods /// @@ -62,7 +53,7 @@ public override Task JsonImportAsync(ModCollectionExpor /// System.String. protected override string GetDbPath(ModCollectionExporterParams parameters) { - return Path.Combine(Path.GetDirectoryName(parameters.ModDirectory), Constants.Sql_db_beta_path); + return Path.Combine(Path.GetDirectoryName(parameters.ModDirectory)!, Constants.Sql_db_beta_path); } #endregion Methods diff --git a/src/IronyModManager.IO/Mods/ModPatchExporter.cs b/src/IronyModManager.IO/Mods/ModPatchExporter.cs index b076b0829..35966244a 100644 --- a/src/IronyModManager.IO/Mods/ModPatchExporter.cs +++ b/src/IronyModManager.IO/Mods/ModPatchExporter.cs @@ -4,7 +4,7 @@ // Created : 03-31-2020 // // Last Modified By : Mario -// Last Modified On : 05-14-2023 +// Last Modified On : 02-25-2024 // *********************************************************************** // // Mario @@ -37,15 +37,19 @@ namespace IronyModManager.IO.Mods { /// - /// Class ModPatchExporter. - /// Implements the + /// The mod patch exporter. /// /// [ExcludeFromCoverage("Skipping testing IO logic.")] - public class ModPatchExporter : IModPatchExporter + public class ModPatchExporter(IObjectClone objectClone, ICache cache, IReader reader, IEnumerable definitionInfoProviders, IMessageBus messageBus) : IModPatchExporter { #region Fields + /// + /// A private const string named AllowedLanguagesFileName. + /// + private const string AllowedLanguagesFileName = "allowed_languages.txt"; + /// /// The cache external code key /// @@ -99,7 +103,7 @@ public class ModPatchExporter : IModPatchExporter /// /// The old format paths /// - private static readonly List OldFormatPaths = new() { JsonStateName, JsonStateName + ".bak", JsonStateName + ".tmp" }; + private static readonly List oldFormatPaths = [JsonStateName, JsonStateName + ".bak", JsonStateName + ".tmp"]; /// /// The write lock @@ -109,27 +113,27 @@ public class ModPatchExporter : IModPatchExporter /// /// The cache /// - private readonly ICache cache; + private readonly ICache cache = cache; /// /// The definition information providers /// - private readonly IEnumerable definitionInfoProviders; + private readonly IEnumerable definitionInfoProviders = definitionInfoProviders; /// /// The message bus /// - private readonly IMessageBus messageBus; + private readonly IMessageBus messageBus = messageBus; /// /// The object clone /// - private readonly IObjectClone objectClone; + private readonly IObjectClone objectClone = objectClone; /// /// The reader /// - private readonly IReader reader; + private readonly IReader reader = reader; /// /// The saving token @@ -139,31 +143,10 @@ public class ModPatchExporter : IModPatchExporter /// /// The write counter /// - private int writeCounter = 0; + private int writeCounter; #endregion Fields - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The object clone. - /// The cache. - /// The reader. - /// The definition information providers. - /// The message bus. - public ModPatchExporter(IObjectClone objectClone, ICache cache, IReader reader, IEnumerable definitionInfoProviders, IMessageBus messageBus) - { - this.cache = cache; - this.definitionInfoProviders = definitionInfoProviders; - this.reader = reader; - this.messageBus = messageBus; - this.objectClone = objectClone; - } - - #endregion Constructors - #region Enums /// @@ -217,13 +200,15 @@ async Task export() { throw new ArgumentNullException(nameof(parameters), "Game."); } + var definitionsInvalid = (parameters.Definitions == null || !parameters.Definitions.Any()) && - (parameters.OverwrittenConflicts == null || !parameters.OverwrittenConflicts.Any()) && - (parameters.CustomConflicts == null || !parameters.CustomConflicts.Any()); + (parameters.OverwrittenConflicts == null || !parameters.OverwrittenConflicts.Any()) && + (parameters.CustomConflicts == null || !parameters.CustomConflicts.Any()); if (definitionsInvalid) { throw new ArgumentNullException(nameof(parameters), "Definitions."); } + var definitionInfoProvider = definitionInfoProviders.FirstOrDefault(p => p.CanProcess(parameters.Game) && p.IsFullyImplemented); if (definitionInfoProvider != null) { @@ -250,12 +235,39 @@ async Task export() results.Add(await WriteMergedContentAsync(parameters.CustomConflicts.Where(p => p.ValueType != ValueType.Binary), GetPatchRootPath(parameters.RootPath, parameters.PatchPath), parameters.Game, true, FileNameGeneration.UseExistingFileName)); } + return results.All(p => p); } + return false; } + var retry = new RetryStrategy(); - return await retry.RetryActionAsync(() => export()); + return await retry.RetryActionAsync(export); + } + + /// + /// Gets an allowed languages async. + /// + /// The parameters. + /// A Task containing IReadOnlyCollection of strings. + public async Task> GetAllowedLanguagesAsync(ModPatchExporterParameters parameters) + { + var rootPath = GetPatchRootPath(parameters.RootPath, parameters.PatchPath); + var filename = Path.Combine(rootPath, AllowedLanguagesFileName); + if (File.Exists(filename)) + { + var content = await File.ReadAllTextAsync(filename); + if (!string.IsNullOrWhiteSpace(content)) + { + var split = content.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); + return split.Select(s => s.Trim()).ToList(); + } + + return []; + } + + return null; } /// @@ -278,6 +290,7 @@ public IEnumerable GetPatchFiles(ModPatchExporterParameters parameters) } } } + return files; } @@ -309,6 +322,7 @@ public async Task GetPatchStateAsync(ModPatchExporterParameters par return (PatchStateMode)value; } } + return null; } @@ -320,9 +334,8 @@ public async Task GetPatchStateAsync(ModPatchExporterParameters par /// Task<System.String>. public async Task LoadDefinitionContentsAsync(ModPatchExporterParameters parameters, string path) { - var patchPath = Path.Combine(parameters.RootPath, parameters.PatchPath); var state = await GetPatchStateAsync(parameters); - if (state != null && state.ConflictHistory != null) + if (state is { ConflictHistory: not null }) { var history = state.ConflictHistory.FirstOrDefault(p => p.FileCI.Equals(path, StringComparison.OrdinalIgnoreCase)); if (history != null) @@ -330,6 +343,7 @@ public async Task LoadDefinitionContentsAsync(ModPatchExporterParameters return history.Code; } } + return string.Empty; } @@ -351,10 +365,12 @@ async Task rename() DiskOperations.DeleteDirectory(oldPath, true); } } + return result; - }; + } + var retry = new RetryStrategy(); - return await retry.RetryActionAsync(() => rename()); + return await retry.RetryActionAsync(rename); } /// @@ -362,7 +378,7 @@ async Task rename() /// public void ResetCache() { - cache.Invalidate(new CacheInvalidateParameters() { Region = CacheStateRegion, Keys = new List() { CacheStateKey } }); + cache.Invalidate(new CacheInvalidateParameters { Region = CacheStateRegion, Keys = [CacheStateKey] }); } /// @@ -383,6 +399,7 @@ public async Task SaveStateAsync(ModPatchExporterParameters parameters) state.OverwrittenConflicts = MapDefinitions(parameters.OverwrittenConflicts, false); state.CustomConflicts = MapDefinitions(parameters.CustomConflicts, false); state.Mode = parameters.Mode; + state.AllowedLanguages = parameters.AllowedLanguages; state.LoadOrder = parameters.LoadOrder; state.HasGameDefinitions = parameters.HasGameDefinitions; var history = new ConcurrentDictionary>(); @@ -390,6 +407,7 @@ public async Task SaveStateAsync(ModPatchExporterParameters parameters) { history.TryAdd(item.Key, item.Value); } + if (parameters.ResolvedConflicts != null) { var tasks = parameters.ResolvedConflicts.Where(s => !string.IsNullOrWhiteSpace(s.Code)).Select(item => @@ -398,41 +416,45 @@ public async Task SaveStateAsync(ModPatchExporterParameters parameters) { if (!history.TryGetValue(item.TypeAndId, out var existingHits)) { - existingHits = new List(); + existingHits = []; } + var existing = existingHits.FirstOrDefault(p => item.Code.Equals(p.Code)); if (existing == null) { - var definitions = new List() { item }; - history.AddOrUpdate(item.TypeAndId, definitions, (k, v) => definitions); - modifiedHistory.AddOrUpdate(item.TypeAndId, item, (k, v) => item); + var definitions = new List { item }; + history.AddOrUpdate(item.TypeAndId, definitions, (_, _) => definitions); + modifiedHistory.AddOrUpdate(item.TypeAndId, item, (_, _) => item); } else if (existingHits.Count() > 1) { - var definitions = new List() { existing }; - history.AddOrUpdate(existing.TypeAndId, definitions, (k, v) => definitions); - modifiedHistory.AddOrUpdate(existing.TypeAndId, existing, (k, v) => existing); + var definitions = new List { existing }; + history.AddOrUpdate(existing.TypeAndId, definitions, (_, _) => definitions); + modifiedHistory.AddOrUpdate(existing.TypeAndId, existing, (_, _) => existing); } }); }); await Task.WhenAll(tasks); } + if (parameters.Definitions != null) { foreach (var item in parameters.Definitions.Where(s => !string.IsNullOrWhiteSpace(s.Code) && !modifiedHistory.Any(p => p.Key.Equals(s.TypeAndId)))) { - var definitions = new List() { item }; - history.AddOrUpdate(item.TypeAndId, definitions, (k, v) => definitions); - modifiedHistory.AddOrUpdate(item.TypeAndId, item, (k, v) => item); + var definitions = new List { item }; + history.AddOrUpdate(item.TypeAndId, definitions, (_, _) => definitions); + modifiedHistory.AddOrUpdate(item.TypeAndId, item, (_, _) => item); } } + state.ConflictHistory = MapDefinitions(history.SelectMany(p => p.Value), true); - var externallyLoadedCode = cache.Get>(new CacheGetParameters() { Key = CacheExternalCodeKey, Region = CacheStateRegion }); + var externallyLoadedCode = cache.Get>(new CacheGetParameters { Key = CacheExternalCodeKey, Region = CacheStateRegion }); if (externallyLoadedCode == null) { - externallyLoadedCode = new HashSet(); - cache.Set(new CacheAddParameters>() { Key = CacheExternalCodeKey, Value = externallyLoadedCode, Region = CacheStateRegion }); + externallyLoadedCode = []; + cache.Set(new CacheAddParameters> { Key = CacheExternalCodeKey, Value = externallyLoadedCode, Region = CacheStateRegion }); } + return StoreState(state, modifiedHistory.Select(p => p.Value), externallyLoadedCode, path); } @@ -451,8 +473,10 @@ static IList standardizeArray(IList paths) { newPaths.Add(item.StandardizeDirectorySeparator()); } + return newPaths; } + return paths; } @@ -489,17 +513,20 @@ private static async Task CopyPatchModInternalAsync(ModPatchExporterParame var destinationPath = Path.Combine(newPath, info.FullName.Replace(oldPath, string.Empty, StringComparison.OrdinalIgnoreCase).TrimStart(Path.DirectorySeparatorChar)); if (!Directory.Exists(Path.GetDirectoryName(destinationPath))) { - Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); + Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)!); } + info.CopyTo(destinationPath, true); } + var text = await ReadPatchContentAsync(newPath); foreach (var renamePair in parameters.RenamePairs) { text = text.Replace($"\"{renamePair.Key}\"", $"\"{renamePair.Value}\""); } + await SavePatchContentAsync(Path.Combine(newPath, StateName), text); - OldFormatPaths.ForEach(path => + oldFormatPaths.ForEach(path => { var fullPath = Path.Combine(newPath, path); if (File.Exists(fullPath)) @@ -509,6 +536,7 @@ private static async Task CopyPatchModInternalAsync(ModPatchExporterParame }); return true; } + return false; } @@ -539,16 +567,17 @@ private static async Task ReadPatchContentAsync(string homePath) else if (File.Exists(path)) { var bytes = await File.ReadAllBytesAsync(path); - if (bytes.Any()) + if (bytes.Length != 0) { using var source = new MemoryStream(bytes); using var destination = new MemoryStream(); - using var compress = new GZipStream(source, CompressionMode.Decompress); + await using var compress = new GZipStream(source, CompressionMode.Decompress); await compress.CopyToAsync(destination); var text = Encoding.UTF8.GetString(destination.ToArray()); return text; } } + return string.Empty; } @@ -563,7 +592,7 @@ private static async Task SavePatchContentAsync(string fullPath, string co var bytes = Encoding.UTF8.GetBytes(content); using var source = new MemoryStream(bytes); using var destination = new MemoryStream(); - using var compress = new GZipStream(destination, CompressionLevel.Fastest, true); + await using var compress = new GZipStream(destination, CompressionLevel.Fastest, true); await source.CopyToAsync(compress); await compress.FlushAsync(); await File.WriteAllBytesAsync(fullPath, destination.ToArray()); @@ -597,7 +626,9 @@ static async Task copyStream(Stream s, FileStream fs) { continue; } + var stream = reader.GetStream(def.ModPath, def.File); + // If image and no stream try switching extension if (FileSignatureUtility.IsImageFile(def.File) && stream == null) { @@ -612,22 +643,23 @@ static async Task copyStream(Stream s, FileStream fs) } } } + if (!Directory.Exists(Path.GetDirectoryName(outPath))) { - Directory.CreateDirectory(Path.GetDirectoryName(outPath)); + Directory.CreateDirectory(Path.GetDirectoryName(outPath)!); } + var fs = new FileStream(outPath, FileMode.Create, FileAccess.Write, FileShare.Read); - if (stream.CanSeek) + if (stream!.CanSeek) { stream.Seek(0, SeekOrigin.Begin); } - tasks.Add(retry.RetryActionAsync(() => - { - return copyStream(stream, fs); - })); + + tasks.Add(retry.RetryActionAsync(() => copyStream(stream, fs))); streams.Add(stream); streams.Add(fs); } + if (tasks.Count > 0) { await Task.WhenAll(tasks); @@ -637,6 +669,7 @@ static async Task copyStream(Stream s, FileStream fs) await fs.DisposeAsync(); } } + return true; } @@ -647,7 +680,7 @@ static async Task copyStream(Stream s, FileStream fs) /// IPatchState. private IPatchState GetPatchState(string path) { - var cachedItem = cache.Get(new CacheGetParameters() { Key = CacheStateKey, Region = CacheStateRegion }); + var cachedItem = cache.Get(new CacheGetParameters { Key = CacheStateKey, Region = CacheStateRegion }); if (cachedItem != null) { var lastPath = cachedItem.LastCachedPath ?? string.Empty; @@ -656,8 +689,10 @@ private IPatchState GetPatchState(string path) ResetCache(); return null; } + return cachedItem.PatchState; } + return null; } @@ -684,59 +719,69 @@ private async Task GetPatchStateInternalAsync(ModPatchExporterParam { cached.IgnoreConflictPaths = string.Empty; } + if (cached.ConflictHistory == null) { - cached.ConflictHistory = new List(); + cached.ConflictHistory = []; } else { StandardizeDefinitionPaths(cached.ConflictHistory); } + if (cached.Conflicts == null) { - cached.Conflicts = new List(); + cached.Conflicts = []; } else { StandardizeDefinitionPaths(cached.Conflicts); } + if (cached.IgnoredConflicts == null) { - cached.IgnoredConflicts = new List(); + cached.IgnoredConflicts = []; } else { StandardizeDefinitionPaths(cached.IgnoredConflicts); } + if (cached.ResolvedConflicts == null) { - cached.ResolvedConflicts = new List(); + cached.ResolvedConflicts = []; } else { StandardizeDefinitionPaths(cached.ResolvedConflicts); } + if (cached.OverwrittenConflicts == null) { - cached.OverwrittenConflicts = new List(); + cached.OverwrittenConflicts = []; } else { StandardizeDefinitionPaths(cached.OverwrittenConflicts); } + if (cached.CustomConflicts == null) { - cached.CustomConflicts = new List(); + cached.CustomConflicts = []; } else { StandardizeDefinitionPaths(cached.CustomConflicts); } - cached.LoadOrder ??= new List(); + + cached.LoadOrder ??= []; + cached.AllowedLanguages ??= []; + // If not allowing full load don't cache anything if (loadExternalCode) { var externallyLoadedCode = new ConcurrentBag(); + async Task loadCode(IDefinition definition) { var historyPath = Path.Combine(GetPatchRootPath(parameters.RootPath, parameters.PatchPath), StateHistory, definition.Type, definition.Id.GenerateValidFileName() + StateConflictHistoryExtension); @@ -747,29 +792,30 @@ async Task loadCode(IDefinition definition) externallyLoadedCode.Add(definition.TypeAndId); } } + var tasks = new List(); foreach (var item in cached.ConflictHistory) { tasks.Add(loadCode(item)); } - var cachedItem = new CachedState() - { - LastCachedPath = statePath, - PatchState = cached - }; + + var cachedItem = new CachedState { LastCachedPath = statePath, PatchState = cached }; await Task.WhenAll(tasks); - cache.Set(new CacheAddParameters() { Region = CacheStateRegion, Key = CacheStateKey, Value = cachedItem }); - cache.Set(new CacheAddParameters>() { Region = CacheStateRegion, Key = CacheExternalCodeKey, Value = externallyLoadedCode.Distinct().ToHashSet() }); + cache.Set(new CacheAddParameters { Region = CacheStateRegion, Key = CacheStateKey, Value = cachedItem }); + cache.Set(new CacheAddParameters> { Region = CacheStateRegion, Key = CacheExternalCodeKey, Value = externallyLoadedCode.Distinct().ToHashSet() }); } } + mutex.Dispose(); } + if (cached != null) { var result = DIResolver.Get(); MapPatchState(cached, result, true); return result; } + return null; } @@ -790,7 +836,7 @@ private IDefinition MapDefinition(IDefinition original, bool includeCode) /// The originals. /// if set to true [include code]. /// IEnumerable<IDefinition>. - private IEnumerable MapDefinitions(IEnumerable originals, bool includeCode) + private List MapDefinitions(IEnumerable originals, bool includeCode) { var col = new List(); if (originals != null) @@ -800,6 +846,7 @@ private IEnumerable MapDefinitions(IEnumerable origina col.Add(MapDefinition(original, includeCode)); } } + return col; } @@ -821,6 +868,7 @@ private void MapPatchState(IPatchState source, IPatchState destination, bool inc destination.Mode = source.Mode; destination.LoadOrder = source.LoadOrder; destination.HasGameDefinitions = source.HasGameDefinitions; + destination.AllowedLanguages = source.AllowedLanguages; } /// @@ -835,11 +883,11 @@ private bool StoreState(IPatchState model, IEnumerable modifiedHist { var statePath = Path.Combine(path, StateName); - var cachedItem = cache.Get(new CacheGetParameters() { Key = CacheStateKey, Region = CacheStateRegion }); + var cachedItem = cache.Get(new CacheGetParameters { Key = CacheStateKey, Region = CacheStateRegion }); cachedItem ??= new CachedState(); cachedItem.LastCachedPath = statePath; cachedItem.PatchState = model; - cache.Set(new CacheAddParameters() { Key = CacheStateKey, Value = cachedItem, Region = CacheStateRegion }); + cache.Set(new CacheAddParameters { Key = CacheStateKey, Value = cachedItem, Region = CacheStateRegion }); savingToken?.Cancel(); savingToken = new CancellationTokenSource(); @@ -859,9 +907,10 @@ private bool StoreState(IPatchState model, IEnumerable modifiedHist private async Task WriteMergedContentAsync(IEnumerable definitions, string patchRootPath, string game, bool checkIfFileExists, FileNameGeneration mode) { var tasks = new List(); - List results = new List(); + var results = new List(); var validDefinitions = definitions.Where(p => p.ValueType != ValueType.Namespace && p.ValueType != ValueType.Variable); var retry = new RetryStrategy(); + async Task evalZeroByteFiles(IDefinition definition, IDefinitionInfoProvider infoProvider, string fileName, string diskFile) { if (mode == FileNameGeneration.UseExistingFileNameAndWriteEmptyFiles) @@ -884,8 +933,8 @@ await retry.RetryActionAsync(async () => var infoProvider = definitionInfoProviders.FirstOrDefault(p => p.CanProcess(game) && p.IsFullyImplemented); if (infoProvider != null) { - string diskFile = string.Empty; - string fileName = string.Empty; + var diskFile = string.Empty; + var fileName = string.Empty; fileName = mode switch { FileNameGeneration.GenerateFileName => infoProvider.GetFileName(item), @@ -896,12 +945,14 @@ await retry.RetryActionAsync(async () => FileNameGeneration.GenerateFileName => infoProvider.GetDiskFileName(item), _ => !string.IsNullOrWhiteSpace(item.DiskFile) ? item.DiskFile : item.File }; + // For backwards compatibility when filename was used var altFileName = Path.Combine(patchRootPath, fileName); if (diskFile != fileName && File.Exists(altFileName)) { DiskOperations.DeleteFile(altFileName); } + var outPath = Path.Combine(patchRootPath, diskFile); if (checkIfFileExists && File.Exists(outPath)) { @@ -909,10 +960,12 @@ await retry.RetryActionAsync(async () => await evalZeroByteFiles(item, infoProvider, fileName, diskFile); continue; } + if (!Directory.Exists(Path.GetDirectoryName(outPath))) { - Directory.CreateDirectory(Path.GetDirectoryName(outPath)); + Directory.CreateDirectory(Path.GetDirectoryName(outPath)!); } + // Update filename item.DiskFile = diskFile; item.File = fileName; @@ -923,6 +976,7 @@ await retry.RetryActionAsync(async () => { code += Environment.NewLine; } + await File.WriteAllTextAsync(outPath, code, infoProvider.GetEncoding(item)); return true; })); @@ -934,6 +988,7 @@ await retry.RetryActionAsync(async () => results.Add(false); } } + if (tasks.Count > 0) { await Task.WhenAll(tasks); @@ -954,7 +1009,7 @@ await retry.RetryActionAsync(async () => private async Task WriteStateInBackground(IPatchState model, IEnumerable modifiedHistory, HashSet externalCode, string path, CancellationToken cancellationToken) { writeCounter++; - using var ctr = cancellationToken.Register(() => + await using var ctr = cancellationToken.Register(() => { writeCounter--; messageBus.PublishAsync(new WritingStateOperationEvent(writeCounter <= 0)).ConfigureAwait(false); @@ -971,6 +1026,7 @@ await Task.Run(async () => { return; } + var retry = new RetryStrategy(); var patchState = DIResolver.Get(); MapPatchState(model, patchState, true); @@ -989,8 +1045,9 @@ await Task.Run(async () => var historyDirectory = Path.GetDirectoryName(historyPath); if (!Directory.Exists(historyDirectory)) { - Directory.CreateDirectory(historyDirectory); + Directory.CreateDirectory(historyDirectory!); } + if (externalCode != null && !externalCode.Contains(item.TypeAndId)) { loadedCode.Add(item.TypeAndId); @@ -1003,54 +1060,59 @@ await Task.Run(async () => } } } + await retry.RetryActionAsync(async () => { - await File.WriteAllTextAsync(historyPath, item.Code); + await File.WriteAllTextAsync(historyPath, item.Code, cancellationToken); return true; }); } - var existingLoadedCode = cache.Get>(new CacheGetParameters() { Key = CacheExternalCodeKey, Region = CacheStateRegion }); + var existingLoadedCode = cache.Get>(new CacheGetParameters { Key = CacheExternalCodeKey, Region = CacheStateRegion }); if (existingLoadedCode != null) { foreach (var item in loadedCode) { existingLoadedCode.Add(item); } - cache.Set(new CacheAddParameters>() { Key = CacheExternalCodeKey, Value = existingLoadedCode, Region = CacheStateRegion }); + + cache.Set(new CacheAddParameters> { Key = CacheExternalCodeKey, Value = existingLoadedCode, Region = CacheStateRegion }); } var dirPath = Path.GetDirectoryName(statePath); if (!Directory.Exists(dirPath)) { - Directory.CreateDirectory(dirPath); + Directory.CreateDirectory(dirPath!); } if (File.Exists(stateTemp)) { DiskOperations.DeleteFile(stateTemp); } + var serialized = JsonDISerializer.Serialize(patchState); - await retry.RetryActionAsync(async () => - { - return await SavePatchContentAsync(stateTemp, serialized); - }); + await retry.RetryActionAsync(async () => await SavePatchContentAsync(stateTemp, serialized)); if (File.Exists(backupPath)) { DiskOperations.DeleteFile(backupPath); } + if (File.Exists(statePath)) { File.Copy(statePath, backupPath); DiskOperations.DeleteFile(statePath); } + if (File.Exists(stateTemp)) { File.Copy(stateTemp, statePath); } + var modeFileName = Path.Combine(path, ModeFileName); - await File.WriteAllTextAsync(modeFileName, ((int)model.Mode).ToString()); - OldFormatPaths.ForEach(oldPath => + await File.WriteAllTextAsync(modeFileName, ((int)model.Mode).ToString(), cancellationToken); + var allowedLanguagesFileName = Path.Combine(path, AllowedLanguagesFileName); + await File.WriteAllLinesAsync(allowedLanguagesFileName, model.AllowedLanguages, cancellationToken); + oldFormatPaths.ForEach(oldPath => { var fullPath = Path.Combine(path, oldPath); if (File.Exists(fullPath)) diff --git a/src/IronyModManager.IO/Mods/Models/PatchState.cs b/src/IronyModManager.IO/Mods/Models/PatchState.cs index 6ecc79ccf..49f2d09ef 100644 --- a/src/IronyModManager.IO/Mods/Models/PatchState.cs +++ b/src/IronyModManager.IO/Mods/Models/PatchState.cs @@ -4,13 +4,14 @@ // Created : 03-31-2020 // // Last Modified By : Mario -// Last Modified On : 11-01-2021 +// Last Modified On : 02-25-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; using System.Linq; @@ -31,6 +32,11 @@ public class PatchState : IPatchState { #region Fields + /// + /// A private IEnumerable{string} named allowedLanguages. + /// + private IEnumerable allowedLanguages; + /// /// The conflict history /// @@ -39,12 +45,24 @@ public class PatchState : IPatchState /// /// The indexed conflict history /// +#pragma warning disable CA1859 // Use concrete types when possible for improved performance private IDictionary> indexedConflictHistory; +#pragma warning restore CA1859 // Use concrete types when possible for improved performance #endregion Fields #region Properties + /// + /// Gets or sets a value representing the allowed languages. + /// + /// The allowed languages. + public IEnumerable AllowedLanguages + { + get => allowedLanguages; + set => allowedLanguages = value == null ? [] : [.. value]; + } + /// /// Gets or sets the conflict history. /// @@ -105,6 +123,7 @@ public IDictionary> IndexedConflictHistory { InitConflictHistoryIndex(); } + return indexedConflictHistory; } } @@ -147,15 +166,15 @@ private void InitConflictHistoryIndex() { conflictHistory.ToList().ForEach(p => { - if (indexedConflictHistory.ContainsKey(p.TypeAndId)) + if (indexedConflictHistory.TryGetValue(p.TypeAndId, out var value)) { - var col = indexedConflictHistory[p.TypeAndId].ToList(); + var col = value.ToList(); col.Add(p); indexedConflictHistory[p.Type] = col; } else { - indexedConflictHistory.Add(p.TypeAndId, new List() { p }); + indexedConflictHistory.Add(p.TypeAndId, [p]); } }); } diff --git a/src/IronyModManager.IO/Properties/PublishProfiles/linux-x64.pubxml b/src/IronyModManager.IO/Properties/PublishProfiles/linux-x64.pubxml index 04545ee0d..b8f7f6db0 100644 --- a/src/IronyModManager.IO/Properties/PublishProfiles/linux-x64.pubxml +++ b/src/IronyModManager.IO/Properties/PublishProfiles/linux-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\linux-x64\net7.0\publish\linux-x64 + net8.0 + bin\x64\linux-x64\net8.0\publish\linux-x64 linux-x64 true False diff --git a/src/IronyModManager.IO/Properties/PublishProfiles/osx-x64.pubxml b/src/IronyModManager.IO/Properties/PublishProfiles/osx-x64.pubxml index df6bd9f9f..ff92dad82 100644 --- a/src/IronyModManager.IO/Properties/PublishProfiles/osx-x64.pubxml +++ b/src/IronyModManager.IO/Properties/PublishProfiles/osx-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\osx-x64\net7.0\publish\osx-x64 + net8.0 + bin\x64\osx-x64\net8.0\publish\osx-x64 osx-x64 true False diff --git a/src/IronyModManager.IO/Properties/PublishProfiles/win-x64.pubxml b/src/IronyModManager.IO/Properties/PublishProfiles/win-x64.pubxml index 1cd122792..cbd6468db 100644 --- a/src/IronyModManager.IO/Properties/PublishProfiles/win-x64.pubxml +++ b/src/IronyModManager.IO/Properties/PublishProfiles/win-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\win-x64\net7.0\publish\win-x64 + net8.0 + bin\x64\win-x64\net8.0\publish\win-x64 win-x64 true False diff --git a/src/IronyModManager.Localization.Tests/IronyModManager.Localization.Tests.csproj b/src/IronyModManager.Localization.Tests/IronyModManager.Localization.Tests.csproj index 8fb9fe464..743329c36 100644 --- a/src/IronyModManager.Localization.Tests/IronyModManager.Localization.Tests.csproj +++ b/src/IronyModManager.Localization.Tests/IronyModManager.Localization.Tests.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 true ../../keys/Irony-Main.snk IronyModManager.Localization.Tests @@ -27,16 +27,16 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/IronyModManager.Localization/IronyModManager.Localization.csproj b/src/IronyModManager.Localization/IronyModManager.Localization.csproj index e614c6083..130a7c2b6 100644 --- a/src/IronyModManager.Localization/IronyModManager.Localization.csproj +++ b/src/IronyModManager.Localization/IronyModManager.Localization.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 Irony Mod Manager Localization Component Irony Mod Manager LICENSE @@ -49,7 +49,7 @@ - + diff --git a/src/IronyModManager.Localization/Properties/PublishProfiles/linux-x64.pubxml b/src/IronyModManager.Localization/Properties/PublishProfiles/linux-x64.pubxml index 04545ee0d..b8f7f6db0 100644 --- a/src/IronyModManager.Localization/Properties/PublishProfiles/linux-x64.pubxml +++ b/src/IronyModManager.Localization/Properties/PublishProfiles/linux-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\linux-x64\net7.0\publish\linux-x64 + net8.0 + bin\x64\linux-x64\net8.0\publish\linux-x64 linux-x64 true False diff --git a/src/IronyModManager.Localization/Properties/PublishProfiles/osx-x64.pubxml b/src/IronyModManager.Localization/Properties/PublishProfiles/osx-x64.pubxml index df6bd9f9f..ff92dad82 100644 --- a/src/IronyModManager.Localization/Properties/PublishProfiles/osx-x64.pubxml +++ b/src/IronyModManager.Localization/Properties/PublishProfiles/osx-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\osx-x64\net7.0\publish\osx-x64 + net8.0 + bin\x64\osx-x64\net8.0\publish\osx-x64 osx-x64 true False diff --git a/src/IronyModManager.Localization/Properties/PublishProfiles/win-x64.pubxml b/src/IronyModManager.Localization/Properties/PublishProfiles/win-x64.pubxml index 1cd122792..cbd6468db 100644 --- a/src/IronyModManager.Localization/Properties/PublishProfiles/win-x64.pubxml +++ b/src/IronyModManager.Localization/Properties/PublishProfiles/win-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\win-x64\net7.0\publish\win-x64 + net8.0 + bin\x64\win-x64\net8.0\publish\win-x64 win-x64 true False diff --git a/src/IronyModManager.Model.Tests/IronyModManager.Model.Tests.csproj b/src/IronyModManager.Model.Tests/IronyModManager.Model.Tests.csproj index 650431e4b..fc3663767 100644 --- a/src/IronyModManager.Model.Tests/IronyModManager.Model.Tests.csproj +++ b/src/IronyModManager.Model.Tests/IronyModManager.Model.Tests.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 IronyModManager.Model.Tests LICENSE logo.png @@ -26,16 +26,16 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -57,11 +57,6 @@ True - - - True - - PreserveNewest diff --git a/src/IronyModManager.Models.Common/IAppState.cs b/src/IronyModManager.Models.Common/IAppState.cs index 3c497debe..23c01f44b 100644 --- a/src/IronyModManager.Models.Common/IAppState.cs +++ b/src/IronyModManager.Models.Common/IAppState.cs @@ -4,15 +4,17 @@ // Created : 03-03-2020 // // Last Modified By : Mario -// Last Modified On : 02-13-2021 +// Last Modified On : 02-20-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; +using System.Linq; namespace IronyModManager.Models.Common { @@ -67,12 +69,24 @@ public interface IAppState : IModel /// The installed mods sort mode. int InstalledModsSortMode { get; set; } + /// + /// Gets or sets a value representing the last prank check. + /// + /// The last prank check. + DateTime? LastPrankCheck { get; set; } + /// /// Gets or sets the last writable check. /// /// The last writable check. DateTime? LastWritableCheck { get; set; } + /// + /// Gets or sets a value indicating whether the use new diff viewer. + /// + /// true if use new diff viewer; otherwise, false. + bool UseNewDiffViewer { get; set; } + #endregion Properties } } diff --git a/src/IronyModManager.Models.Common/IConflictResult.cs b/src/IronyModManager.Models.Common/IConflictResult.cs index 3530f6571..3a9535c0b 100644 --- a/src/IronyModManager.Models.Common/IConflictResult.cs +++ b/src/IronyModManager.Models.Common/IConflictResult.cs @@ -4,15 +4,17 @@ // Created : 03-18-2020 // // Last Modified By : Mario -// Last Modified On : 11-01-2021 +// Last Modified On : 02-25-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; +using System.Linq; using IronyModManager.Shared.Models; namespace IronyModManager.Models.Common @@ -34,6 +36,14 @@ public interface IConflictResult : IModel, IDisposable /// All conflicts. IIndexedDefinitions AllConflicts { get; set; } + /// + /// Gets or sets a value representing the allowed languages. + /// + /// + /// The allowed languages. + /// + IEnumerable AllowedLanguages { get; set; } + /// /// Gets or sets the conflicts. /// diff --git a/src/IronyModManager.Models.Common/IGameLanguage.cs b/src/IronyModManager.Models.Common/IGameLanguage.cs new file mode 100644 index 000000000..899d75ee6 --- /dev/null +++ b/src/IronyModManager.Models.Common/IGameLanguage.cs @@ -0,0 +1,49 @@ +// *********************************************************************** +// Assembly : IronyModManager.Models.Common +// Author : Mario +// Created : 02-25-2024 +// +// Last Modified By : Mario +// Last Modified On : 02-25-2024 +// *********************************************************************** +// +// Copyright (c) . All rights reserved. +// +// +// *********************************************************************** + +using System; +using System.Collections.Generic; +using System.Linq; +using IronyModManager.Localization; + +namespace IronyModManager.Models.Common +{ + /// + /// An game language interface. + /// + public interface IGameLanguage : IModel, ILocalizableModel + { + #region Properties + + /// + /// Gets or sets the display name. + /// + /// The display name. + public string DisplayName { get; set; } + + /// + /// Gets or sets a value indicating whether the is selected. + /// + /// true if is selected; otherwise, false. + public bool IsSelected { get; set; } + + /// + /// Gets or sets a value representing the type. + /// + /// The type. + public string Type { get; set; } + + #endregion Properties + } +} diff --git a/src/IronyModManager.Models.Common/IPreferences.cs b/src/IronyModManager.Models.Common/IPreferences.cs index 5b134e4ec..e372fdf0f 100644 --- a/src/IronyModManager.Models.Common/IPreferences.cs +++ b/src/IronyModManager.Models.Common/IPreferences.cs @@ -4,7 +4,7 @@ // Created : 01-11-2020 // // Last Modified By : Mario -// Last Modified On : 11-02-2022 +// Last Modified On : 02-26-2024 // *********************************************************************** // // Mario @@ -15,6 +15,11 @@ /// /// The Models namespace. /// + +using System; +using System.Collections.Generic; +using System.Linq; + namespace IronyModManager.Models.Common { /// @@ -38,6 +43,18 @@ public interface IPreferences : IModel /// true if [check for prerelease]; otherwise, false. bool CheckForPrerelease { get; set; } + /// + /// Gets or sets a value representing the conflict solver languages. + /// + /// The conflict solver languages. + List ConflictSolverLanguages { get; set; } + + /// + /// Gets or sets a value indicating whether the conflict solver languages set. + /// + /// true if conflict solver languages set; otherwise, false. + bool ConflictSolverLanguagesSet { get; set; } + /// /// Gets or sets a value indicating whether [conflict solver prompt shown]. /// diff --git a/src/IronyModManager.Models.Common/IronyModManager.Models.Common.csproj b/src/IronyModManager.Models.Common/IronyModManager.Models.Common.csproj index c0511172c..b9b8a5d0c 100644 --- a/src/IronyModManager.Models.Common/IronyModManager.Models.Common.csproj +++ b/src/IronyModManager.Models.Common/IronyModManager.Models.Common.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 true ../../keys/Irony-Main.snk Irony Mod Manager Models Common Component diff --git a/src/IronyModManager.Models.Common/Properties/PublishProfiles/linux-x64.pubxml b/src/IronyModManager.Models.Common/Properties/PublishProfiles/linux-x64.pubxml index 04545ee0d..b8f7f6db0 100644 --- a/src/IronyModManager.Models.Common/Properties/PublishProfiles/linux-x64.pubxml +++ b/src/IronyModManager.Models.Common/Properties/PublishProfiles/linux-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\linux-x64\net7.0\publish\linux-x64 + net8.0 + bin\x64\linux-x64\net8.0\publish\linux-x64 linux-x64 true False diff --git a/src/IronyModManager.Models.Common/Properties/PublishProfiles/osx-x64.pubxml b/src/IronyModManager.Models.Common/Properties/PublishProfiles/osx-x64.pubxml index df6bd9f9f..ff92dad82 100644 --- a/src/IronyModManager.Models.Common/Properties/PublishProfiles/osx-x64.pubxml +++ b/src/IronyModManager.Models.Common/Properties/PublishProfiles/osx-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\osx-x64\net7.0\publish\osx-x64 + net8.0 + bin\x64\osx-x64\net8.0\publish\osx-x64 osx-x64 true False diff --git a/src/IronyModManager.Models.Common/Properties/PublishProfiles/win-x64.pubxml b/src/IronyModManager.Models.Common/Properties/PublishProfiles/win-x64.pubxml index 1cd122792..cbd6468db 100644 --- a/src/IronyModManager.Models.Common/Properties/PublishProfiles/win-x64.pubxml +++ b/src/IronyModManager.Models.Common/Properties/PublishProfiles/win-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\win-x64\net7.0\publish\win-x64 + net8.0 + bin\x64\win-x64\net8.0\publish\win-x64 win-x64 true False diff --git a/src/IronyModManager.Models/AppState.cs b/src/IronyModManager.Models/AppState.cs index caedef72d..11f61cb89 100644 --- a/src/IronyModManager.Models/AppState.cs +++ b/src/IronyModManager.Models/AppState.cs @@ -4,15 +4,17 @@ // Created : 03-03-2020 // // Last Modified By : Mario -// Last Modified On : 02-13-2021 +// Last Modified On : 02-24-2024 // *********************************************************************** // // Mario // // // *********************************************************************** -using System.Collections.Generic; + using System; +using System.Collections.Generic; +using System.Linq; using IronyModManager.Models.Common; namespace IronyModManager.Models @@ -70,12 +72,24 @@ public class AppState : BaseModel, IAppState /// The installed mods sort mode. public virtual int InstalledModsSortMode { get; set; } + /// + /// Gets or sets a value representing the last prank check. + /// + /// The last prank check. + public DateTime? LastPrankCheck { get; set; } + /// /// Gets or sets the last writable check. /// /// The last writable check. public virtual DateTime? LastWritableCheck { get; set; } + /// + /// Gets or sets a value indicating whether the use new diff viewer. + /// + /// true if use new diff viewer; otherwise, false. + public bool UseNewDiffViewer { get; set; } = true; + #endregion Properties } } diff --git a/src/IronyModManager.Models/ConflictResult.cs b/src/IronyModManager.Models/ConflictResult.cs index c71c1dec0..708874d47 100644 --- a/src/IronyModManager.Models/ConflictResult.cs +++ b/src/IronyModManager.Models/ConflictResult.cs @@ -4,15 +4,17 @@ // Created : 03-18-2020 // // Last Modified By : Mario -// Last Modified On : 11-01-2021 +// Last Modified On : 02-25-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; +using System.Linq; using IronyModManager.Models.Common; using IronyModManager.Shared.Models; @@ -29,10 +31,15 @@ public class ConflictResult : BaseModel, IConflictResult { #region Fields + /// + /// The allowed languages + /// + private List allowedLanguages; + /// /// The disposed /// - private bool disposed = false; + private bool disposed; #endregion Fields @@ -44,6 +51,16 @@ public class ConflictResult : BaseModel, IConflictResult /// All conflicts. public IIndexedDefinitions AllConflicts { get; set; } + /// + /// Gets or sets the allowed languages. + /// + /// The allowed languages. + public IEnumerable AllowedLanguages + { + get => allowedLanguages; + set => allowedLanguages = value == null ? [] : [.. value]; + } + /// /// Gets or sets the conflicts. /// @@ -105,6 +122,7 @@ public void Dispose() { return; } + GC.SuppressFinalize(this); disposed = true; AllConflicts?.Dispose(); @@ -121,6 +139,7 @@ public void Dispose() RuleIgnoredConflicts = null; OverwrittenConflicts = null; CustomConflicts = null; + AllowedLanguages = null; } #endregion Methods diff --git a/src/IronyModManager.Models/DIPackage.cs b/src/IronyModManager.Models/DIPackage.cs index f04c85f53..e3bcb6337 100644 --- a/src/IronyModManager.Models/DIPackage.cs +++ b/src/IronyModManager.Models/DIPackage.cs @@ -1,19 +1,20 @@ - -// *********************************************************************** +// *********************************************************************** // Assembly : IronyModManager.Models // Author : Mario // Created : 01-15-2020 // // Last Modified By : Mario -// Last Modified On : 06-28-2023 +// Last Modified On : 02-25-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; +using System.Linq; using IronyModManager.DI.Extensions; using IronyModManager.Localization; using IronyModManager.Models.Common; @@ -23,7 +24,6 @@ namespace IronyModManager.Models { - /// /// Class DIPackage. /// Implements the @@ -32,7 +32,6 @@ namespace IronyModManager.Models [ExcludeFromCoverage("Should not test external DI.")] public class DIPackage : IPackage { - #region Methods /// @@ -66,6 +65,7 @@ public void RegisterServices(Container container) container.RegisterModel(); container.Register(); container.RegisterModel(); + container.RegisterLocalization(); } #endregion Methods diff --git a/src/IronyModManager.Models/GameLanguage.cs b/src/IronyModManager.Models/GameLanguage.cs new file mode 100644 index 000000000..2d23cf682 --- /dev/null +++ b/src/IronyModManager.Models/GameLanguage.cs @@ -0,0 +1,54 @@ +// *********************************************************************** +// Assembly : IronyModManager.Models +// Author : Mario +// Created : 02-25-2024 +// +// Last Modified By : Mario +// Last Modified On : 02-25-2024 +// *********************************************************************** +// +// Copyright (c) . All rights reserved. +// +// +// *********************************************************************** + +using System; +using System.Collections.Generic; +using System.Linq; +using IronyModManager.Localization.Attributes; +using IronyModManager.Models.Common; +using IronyModManager.Shared; + +namespace IronyModManager.Models +{ + /// + /// The game language. + /// + /// + /// + public class GameLanguage : BaseModel, IGameLanguage + { + #region Properties + + /// + /// Gets or sets a value representing the display name. + /// + /// The display name. + [DynamicLocalization(LocalizationResources.GameLanguages.Prefix, nameof(Type))] + public virtual string DisplayName { get; set; } + + /// + /// Gets or sets a value indicating whether the is selected. + /// + /// true if is selected; otherwise, false. + public virtual bool IsSelected { get; set; } + + /// + /// Gets or sets a value representing the type. + /// + /// The type. + public virtual string Type { get; set; } + + #endregion Properties + } +} diff --git a/src/IronyModManager.Models/IronyModManager.Models.csproj b/src/IronyModManager.Models/IronyModManager.Models.csproj index ce9b4987f..af51cef6a 100644 --- a/src/IronyModManager.Models/IronyModManager.Models.csproj +++ b/src/IronyModManager.Models/IronyModManager.Models.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 Mario Irony Mod Manager Models Component Mario @@ -46,7 +46,7 @@ - + all diff --git a/src/IronyModManager.Models/MappingProfile.cs b/src/IronyModManager.Models/MappingProfile.cs index 78d97b07b..1a0566a85 100644 --- a/src/IronyModManager.Models/MappingProfile.cs +++ b/src/IronyModManager.Models/MappingProfile.cs @@ -1,11 +1,10 @@ - -// *********************************************************************** +// *********************************************************************** // Assembly : IronyModManager.Models // Author : Mario // Created : 01-11-2020 // // Last Modified By : Mario -// Last Modified On : 06-28-2023 +// Last Modified On : 02-25-2024 // *********************************************************************** // // Copyright (c) Mario. All rights reserved. @@ -15,13 +14,13 @@ using System; using System.Collections.Generic; +using System.Linq; using IronyModManager.Models.Common; using IronyModManager.Shared; using IronyModManager.Shared.Models; namespace IronyModManager.Models { - /// /// Class MappingProfile. /// Implements the @@ -63,6 +62,7 @@ public MappingProfile() CreateMap().ReverseMap(); CreateMap().ReverseMap(); CreateMap().ReverseMap(); + CreateMap().ReverseMap(); } #endregion Constructors diff --git a/src/IronyModManager.Models/IModIgnoreConfiguration.cs b/src/IronyModManager.Models/ModIgnoreConfiguration.cs similarity index 87% rename from src/IronyModManager.Models/IModIgnoreConfiguration.cs rename to src/IronyModManager.Models/ModIgnoreConfiguration.cs index d8775cdde..9a64058d6 100644 --- a/src/IronyModManager.Models/IModIgnoreConfiguration.cs +++ b/src/IronyModManager.Models/ModIgnoreConfiguration.cs @@ -1,17 +1,17 @@ - -// *********************************************************************** +// *********************************************************************** // Assembly : IronyModManager.Models // Author : Mario // Created : 06-28-2023 // // Last Modified By : Mario -// Last Modified On : 06-28-2023 +// Last Modified On : 07-16-2023 // *********************************************************************** -// +// // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; using System.Linq; @@ -19,7 +19,6 @@ namespace IronyModManager.Models { - /// /// Class ModIgnoreConfiguration. /// Implements the @@ -36,6 +35,7 @@ public class ModIgnoreConfiguration : BaseModel, IModIgnoreConfiguration /// /// The count. public int Count { get; set; } + /// /// Gets or sets the name of the mod. /// diff --git a/src/IronyModManager.Models/Preferences.cs b/src/IronyModManager.Models/Preferences.cs index dd0a5fab2..61cb12a52 100644 --- a/src/IronyModManager.Models/Preferences.cs +++ b/src/IronyModManager.Models/Preferences.cs @@ -4,7 +4,7 @@ // Created : 01-11-2020 // // Last Modified By : Mario -// Last Modified On : 11-02-2022 +// Last Modified On : 02-26-2024 // *********************************************************************** // // Mario @@ -14,6 +14,7 @@ using System; using System.Collections.Generic; +using System.Linq; using IronyModManager.Models.Common; /// @@ -44,6 +45,18 @@ public class Preferences : BaseModel, IPreferences /// true if [check for prerelease]; otherwise, false. public virtual bool CheckForPrerelease { get; set; } + /// + /// Gets or sets a value representing the conflict solver languages. + /// + /// The conflict solver languages. + public virtual List ConflictSolverLanguages { get; set; } + + /// + /// Gets or sets a value indicating whether [conflict solver languages set]. + /// + /// true if [conflict solver languages set]; otherwise, false. + public virtual bool ConflictSolverLanguagesSet { get; set; } + /// /// Gets or sets a value indicating whether [conflict solver prompt shown]. /// diff --git a/src/IronyModManager.Models/Properties/PublishProfiles/linux-x64.pubxml b/src/IronyModManager.Models/Properties/PublishProfiles/linux-x64.pubxml index 04545ee0d..b8f7f6db0 100644 --- a/src/IronyModManager.Models/Properties/PublishProfiles/linux-x64.pubxml +++ b/src/IronyModManager.Models/Properties/PublishProfiles/linux-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\linux-x64\net7.0\publish\linux-x64 + net8.0 + bin\x64\linux-x64\net8.0\publish\linux-x64 linux-x64 true False diff --git a/src/IronyModManager.Models/Properties/PublishProfiles/osx-x64.pubxml b/src/IronyModManager.Models/Properties/PublishProfiles/osx-x64.pubxml index df6bd9f9f..ff92dad82 100644 --- a/src/IronyModManager.Models/Properties/PublishProfiles/osx-x64.pubxml +++ b/src/IronyModManager.Models/Properties/PublishProfiles/osx-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\osx-x64\net7.0\publish\osx-x64 + net8.0 + bin\x64\osx-x64\net8.0\publish\osx-x64 osx-x64 true False diff --git a/src/IronyModManager.Models/Properties/PublishProfiles/win-x64.pubxml b/src/IronyModManager.Models/Properties/PublishProfiles/win-x64.pubxml index 1cd122792..cbd6468db 100644 --- a/src/IronyModManager.Models/Properties/PublishProfiles/win-x64.pubxml +++ b/src/IronyModManager.Models/Properties/PublishProfiles/win-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\win-x64\net7.0\publish\win-x64 + net8.0 + bin\x64\win-x64\net8.0\publish\win-x64 win-x64 true False diff --git a/src/IronyModManager.Parser.Common/IronyModManager.Parser.Common.csproj b/src/IronyModManager.Parser.Common/IronyModManager.Parser.Common.csproj index 6237c9e8c..d6788dd8b 100644 --- a/src/IronyModManager.Parser.Common/IronyModManager.Parser.Common.csproj +++ b/src/IronyModManager.Parser.Common/IronyModManager.Parser.Common.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 true ../../keys/Irony-Main.snk true @@ -40,7 +40,7 @@ - + all diff --git a/src/IronyModManager.Parser.Common/Properties/PublishProfiles/linux-x64.pubxml b/src/IronyModManager.Parser.Common/Properties/PublishProfiles/linux-x64.pubxml index 04545ee0d..b8f7f6db0 100644 --- a/src/IronyModManager.Parser.Common/Properties/PublishProfiles/linux-x64.pubxml +++ b/src/IronyModManager.Parser.Common/Properties/PublishProfiles/linux-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\linux-x64\net7.0\publish\linux-x64 + net8.0 + bin\x64\linux-x64\net8.0\publish\linux-x64 linux-x64 true False diff --git a/src/IronyModManager.Parser.Common/Properties/PublishProfiles/osx-x64.pubxml b/src/IronyModManager.Parser.Common/Properties/PublishProfiles/osx-x64.pubxml index df6bd9f9f..ff92dad82 100644 --- a/src/IronyModManager.Parser.Common/Properties/PublishProfiles/osx-x64.pubxml +++ b/src/IronyModManager.Parser.Common/Properties/PublishProfiles/osx-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\osx-x64\net7.0\publish\osx-x64 + net8.0 + bin\x64\osx-x64\net8.0\publish\osx-x64 osx-x64 true False diff --git a/src/IronyModManager.Parser.Common/Properties/PublishProfiles/win-x64.pubxml b/src/IronyModManager.Parser.Common/Properties/PublishProfiles/win-x64.pubxml index 1cd122792..cbd6468db 100644 --- a/src/IronyModManager.Parser.Common/Properties/PublishProfiles/win-x64.pubxml +++ b/src/IronyModManager.Parser.Common/Properties/PublishProfiles/win-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\win-x64\net7.0\publish\win-x64 + net8.0 + bin\x64\win-x64\net8.0\publish\win-x64 win-x64 true False diff --git a/src/IronyModManager.Parser.Tests/IronyModManager.Parser.Tests.csproj b/src/IronyModManager.Parser.Tests/IronyModManager.Parser.Tests.csproj index 85ebd0374..4199ebc96 100644 --- a/src/IronyModManager.Parser.Tests/IronyModManager.Parser.Tests.csproj +++ b/src/IronyModManager.Parser.Tests/IronyModManager.Parser.Tests.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 true ../../keys/Irony-Main.snk IronyModManager.Parser.Tests @@ -43,20 +43,20 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/IronyModManager.Parser.Tests/StellarisValidation.cs b/src/IronyModManager.Parser.Tests/StellarisValidation.cs index 832af61af..057ec67b5 100644 --- a/src/IronyModManager.Parser.Tests/StellarisValidation.cs +++ b/src/IronyModManager.Parser.Tests/StellarisValidation.cs @@ -66,7 +66,7 @@ public async Task StellarisDetectDuplicatesAndGenerateParserMap() /// /// Defines the test method StellarisExtensions. /// - public void StellarisExtensions() + public async Task StellarisExtensions() { Extensions(); } diff --git a/src/IronyModManager.Parser.Tests/ValidationBase.cs b/src/IronyModManager.Parser.Tests/ValidationBase.cs index a65a73e24..d525e4936 100644 --- a/src/IronyModManager.Parser.Tests/ValidationBase.cs +++ b/src/IronyModManager.Parser.Tests/ValidationBase.cs @@ -171,7 +171,8 @@ string getKey(string line, string key) //{ // continue; //} - var lines = content.Contains("\r\n") ? content.Split("\r\n", StringSplitOptions.RemoveEmptyEntries) : content.Split("\n", StringSplitOptions.RemoveEmptyEntries); + + var lines = content.SplitOnNewLine(); bool notEmpty = false; foreach (var line in lines) { diff --git a/src/IronyModManager.Parser/Definitions/IndexedDefinitions.cs b/src/IronyModManager.Parser/Definitions/IndexedDefinitions.cs index 00f8516bd..61343e4eb 100644 --- a/src/IronyModManager.Parser/Definitions/IndexedDefinitions.cs +++ b/src/IronyModManager.Parser/Definitions/IndexedDefinitions.cs @@ -1,17 +1,17 @@ - -// *********************************************************************** +// *********************************************************************** // Assembly : IronyModManager.Parser.Definitions // Author : Mario // Created : 02-16-2020 // // Last Modified By : Mario -// Last Modified On : 12-13-2023 +// Last Modified On : 02-23-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -21,6 +21,7 @@ using System.Threading.Tasks; using CodexMicroORM.Core.Collections; using IronyModManager.DI; +using IronyModManager.Shared; using IronyModManager.Shared.KeyValueStore; using IronyModManager.Shared.Models; using IronyModManager.Shared.Trie; @@ -30,7 +31,6 @@ namespace IronyModManager.Parser.Definitions { - /// /// Class IndexedDefinitions. /// Implements the @@ -98,7 +98,7 @@ public class IndexedDefinitions : IIndexedDefinitions /// /// The disposed /// - private bool disposed = false; + private bool disposed; /// /// The file ci keys @@ -111,9 +111,9 @@ public class IndexedDefinitions : IIndexedDefinitions private long gameDefinitionsCount; /// - /// The main hierarchal definitions + /// A private ConcurrentIndexedList{IronyModManager.Shared.Models.IHierarchicalDefinitions} named mainHierarchicalDefinitions. /// - private ConcurrentIndexedList mainHierarchalDefinitions; + private ConcurrentIndexedList mainHierarchicalDefinitions; /// /// The reset definitions count @@ -123,12 +123,13 @@ public class IndexedDefinitions : IIndexedDefinitions /// /// The search database /// - private LiteDatabase searchDb = null; + private LiteDatabase searchDb; /// /// The search database path /// private string searchDbPath = string.Empty; + /// /// The store /// @@ -153,10 +154,11 @@ public class IndexedDefinitions : IIndexedDefinitions /// The type values /// private Dictionary> typeKeyValues; + /// - /// The use hierarchal map + /// A private bool named useHierarchicalMap. /// - private bool useHierarchalMap = false; + private bool useHierarchicalMap; #endregion Fields @@ -169,16 +171,16 @@ public IndexedDefinitions() { definitions = new ConcurrentIndexedList(nameof(IDefinition.FileCI), nameof(IDefinition.Type), nameof(IDefinition.TypeAndId), nameof(IDefinition.ParentDirectoryCI), nameof(IDefinition.ValueType), nameof(IDefinition.DiskFileCI)); - fileCIKeys = new Dictionary>(); - diskFileCIKeys = new Dictionary>(); - typeAndIdKeys = new HashSet(); - typeKeys = new Dictionary>(); - allFileKeys = new HashSet(); - directoryCIKeys = new Dictionary>(); - resetDefinitions = new HashSet(); - typeKeyValues = new Dictionary>(); + fileCIKeys = []; + diskFileCIKeys = []; + typeAndIdKeys = []; + typeKeys = []; + allFileKeys = []; + directoryCIKeys = []; + resetDefinitions = []; + typeKeyValues = []; childHierarchicalDefinitions = new ConcurrentDictionary>(); - mainHierarchalDefinitions = new ConcurrentIndexedList(nameof(IHierarchicalDefinitions.Name)); + mainHierarchicalDefinitions = new ConcurrentIndexedList(nameof(IHierarchicalDefinitions.Name)); } #endregion Constructors @@ -209,14 +211,14 @@ public Task AddToMapAsync(IDefinition definition, bool forceIgnoreHierarchical = /// Changes the state of the hierarchical reset. /// /// The definition. - /// true if XXXX, false otherwise. + /// true if changed, false otherwise. public async Task ChangeHierarchicalResetStateAsync(IDefinition definition) { using var mutex = await opLock.LockAsync(); if (definition != null) { - var parentDirectoryCI = ResolveHierarchalParentDirectory(definition); - var hierarchicalDefinition = mainHierarchalDefinitions.GetFirstByName(nameof(IHierarchicalDefinitions.Name), parentDirectoryCI); + var parentDirectoryCI = ResolveHierarchicalParentDirectory(definition); + var hierarchicalDefinition = mainHierarchicalDefinitions.GetFirstByName(nameof(IHierarchicalDefinitions.Name), parentDirectoryCI); if (hierarchicalDefinition != null) { if (childHierarchicalDefinitions.TryGetValue(hierarchicalDefinition.Name, out var children)) @@ -233,6 +235,7 @@ public async Task ChangeHierarchicalResetStateAsync(IDefinition definition } } } + mutex.Dispose(); return false; } @@ -246,6 +249,7 @@ public void Dispose() { return; } + GC.SuppressFinalize(this); disposed = true; definitions.Clear(); @@ -264,8 +268,8 @@ public void Dispose() allFileKeys = null; childHierarchicalDefinitions.Clear(); childHierarchicalDefinitions = null; - mainHierarchalDefinitions.Clear(); - mainHierarchalDefinitions = null; + mainHierarchicalDefinitions.Clear(); + mainHierarchicalDefinitions = null; trie = null; resetDefinitions.Clear(); resetDefinitions = null; @@ -297,6 +301,7 @@ public Task> GetAllAsync() { return ReadDefinitionsFromStoreAsync(typeAndIdKeys); } + return Task.FromResult>(new HashSet(definitions)); } @@ -306,7 +311,7 @@ public Task> GetAllAsync() /// IEnumerable<System.String>. public Task> GetAllDirectoryKeysAsync() { - return Task.FromResult>(directoryCIKeys.Keys.ToHashSet()); + return Task.FromResult>([.. directoryCIKeys.Keys]); } /// @@ -315,7 +320,7 @@ public Task> GetAllDirectoryKeysAsync() /// IEnumerable<System.String>. public Task> GetAllFileKeysAsync() { - return Task.FromResult>(fileCIKeys.Keys.ToHashSet()); + return Task.FromResult>([.. fileCIKeys.Keys]); } /// @@ -324,7 +329,7 @@ public Task> GetAllFileKeysAsync() /// IEnumerable<System.String>. public Task> GetAllTypeAndIdKeysAsync() { - return Task.FromResult>(typeAndIdKeys.ToHashSet()); + return Task.FromResult>([.. typeAndIdKeys]); } /// @@ -333,7 +338,7 @@ public Task> GetAllTypeAndIdKeysAsync() /// IEnumerable<System.String>. public Task> GetAllTypeKeysAsync() { - return Task.FromResult>(typeKeys.Keys.ToHashSet()); + return Task.FromResult>([.. typeKeys.Keys]); } /// @@ -350,8 +355,10 @@ public Task> GetByDiskFileAsync(string file) { return ReadDefinitionsFromStoreAsync(value); } - return Task.FromResult>(Array.Empty()); + + return Task.FromResult>([]); } + return Task.FromResult(definitions.GetAllByName(nameof(IDefinition.DiskFileCI), file.ToLowerInvariant())); } @@ -365,12 +372,9 @@ public Task> GetByFileAsync(string file) EnsureAllowedAllIsRespected(); if (store != null) { - if (fileCIKeys.TryGetValue(file, out var value)) - { - return ReadDefinitionsFromStoreAsync(value); - } - return Task.FromResult>(Array.Empty()); + return fileCIKeys.TryGetValue(file, out var value) ? ReadDefinitionsFromStoreAsync(value) : Task.FromResult>([]); } + return Task.FromResult(definitions.GetAllByName(nameof(IDefinition.FileCI), file.ToLowerInvariant())); } @@ -388,8 +392,10 @@ public Task> GetByParentDirectoryAsync(string directory { return ReadDefinitionsFromStoreAsync(value); } - return Task.FromResult>(Array.Empty()); + + return Task.FromResult>([]); } + return Task.FromResult(definitions.GetAllByName(nameof(IDefinition.ParentDirectoryCI), directory.ToLowerInvariant())); } @@ -416,6 +422,7 @@ public async Task> GetByTypeAndIdAsync(string typeAndId { return await store.ReadAsync(typeAndId); } + return definitions.GetAllByName(nameof(IDefinition.TypeAndId), typeAndId); } @@ -433,8 +440,10 @@ public Task> GetByTypeAsync(string type) { return ReadDefinitionsFromStoreAsync(value); } - return Task.FromResult>(Array.Empty()); + + return Task.FromResult>([]); } + return Task.FromResult(definitions.GetAllByName(nameof(IDefinition.Type), type)); } @@ -443,6 +452,7 @@ public Task> GetByTypeAsync(string type) /// /// The type. /// IEnumerable<IDefinition>. + /// Only invalid types can be queried. /// Only invalid types can be queried. public Task> GetByValueTypeAsync(ValueType type) { @@ -450,14 +460,17 @@ public Task> GetByValueTypeAsync(ValueType type) { throw new ArgumentException("Only invalid types can be queried."); } + if (store != null) { if (typeKeyValues.TryGetValue(type, out var value)) { return ReadDefinitionsFromStoreAsync(value); } - return Task.FromResult>(Array.Empty()); + + return Task.FromResult>([]); } + return Task.FromResult(definitions.GetAllByName(nameof(IDefinition.ValueType), type)); } @@ -467,15 +480,16 @@ public Task> GetByValueTypeAsync(ValueType type) /// IEnumerable<IHierarchicalDefinitions>. public IEnumerable GetHierarchicalDefinitions() { - var hierarchicalDefinitions = CopyHierarchicalDefinition(mainHierarchalDefinitions); + var hierarchicalDefinitions = CopyHierarchicalDefinition(mainHierarchicalDefinitions); foreach (var item in hierarchicalDefinitions) { if (childHierarchicalDefinitions.TryGetValue(item.Name, out var value)) { - item.Children = CopyHierarchicalDefinition(value.Select(p => p).OrderBy(p => p.Name).ToHashSet()).ToHashSet(); + item.Children = [.. CopyHierarchicalDefinition([.. value.Select(p => p).OrderBy(p => p.Name)])]; } } - return hierarchicalDefinitions.Select(p => p).OrderBy(p => p.Name).ToHashSet(); + + return [.. hierarchicalDefinitions.Select(p => p).OrderBy(p => p.Name)]; } /// @@ -493,7 +507,7 @@ public Task HasGameDefinitionsAsync() /// true if [has reset definitions]; otherwise, false. public Task HasResetDefinitionsAsync() { - return Task.FromResult(resetDefinitions.Any()); + return Task.FromResult(resetDefinitions.Count != 0); } /// @@ -503,7 +517,7 @@ public Task HasResetDefinitionsAsync() /// Task. public async Task InitializeSearchAsync(IReadOnlyCollection definitions) { - if (definitions != null && definitions.Any()) + if (definitions != null && definitions.Count != 0) { var total = definitions.Count; var counter = 0; @@ -530,17 +544,17 @@ await Task.Run(() => { counter++; var displayName = $"{definition.Id} - {definition.File} - {definition.ModName}"; - var item = new DefinitionSearch() { DisplayName = displayName, Tags = definition.Tags.ToArray() }; + var item = new DefinitionSearch { DisplayName = displayName, Tags = [.. definition.Tags] }; items.Add(item); OnProcessedSearchItem(counter, total); } + var col = searchDb.GetCollection(SearchTableName); col.EnsureIndex(x => x.Tags); col.InsertBulk(items); }); - GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized); - GC.WaitForPendingFinalizers(); - GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized); + + GCRunner.RunGC(GCCollectionMode.Optimized, false); } } } @@ -553,7 +567,7 @@ await Task.Run(() => /// A Task representing the asynchronous operation. public async Task InitMapAsync(IEnumerable definitions, bool mapHierarchicalDefinitions = false) { - useHierarchalMap = mapHierarchicalDefinitions; + useHierarchicalMap = mapHierarchicalDefinitions; if (definitions != null) { foreach (var item in definitions) @@ -575,10 +589,12 @@ public async Task RemoveAsync(IDefinition definition) { gameDefinitionsCount--; } + if (gameDefinitionsCount < 0) { gameDefinitionsCount = 0; } + AddOrRemoveFromResetDefinitions(definition, false); if (store != null) { @@ -594,7 +610,8 @@ public async Task RemoveAsync(IDefinition definition) { definitions.Remove(definition); } - var hierarchicalDefinition = mainHierarchalDefinitions.GetFirstByName(nameof(IHierarchicalDefinitions.Name), ResolveHierarchalParentDirectory(definition)); + + var hierarchicalDefinition = mainHierarchicalDefinitions.GetFirstByName(nameof(IHierarchicalDefinitions.Name), ResolveHierarchicalParentDirectory(definition)); if (hierarchicalDefinition != null) { if (childHierarchicalDefinitions.TryGetValue(hierarchicalDefinition.Name, out var children)) @@ -604,19 +621,22 @@ public async Task RemoveAsync(IDefinition definition) { children.Remove(child); } - bool removed = false; + + var removed = false; if (!children.Select(p => p).Any()) { removed = true; childHierarchicalDefinitions.TryRemove(hierarchicalDefinition.Name, out _); - mainHierarchalDefinitions.Remove(hierarchicalDefinition); + mainHierarchicalDefinitions.Remove(hierarchicalDefinition); } + if (!removed) { - hierarchicalDefinition.ResetType = children.Any() && children.Any(p => p.ResetType != ResetType.None) ? ResetType.Any : ResetType.None; + hierarchicalDefinition.ResetType = children.Count != 0 && children.Any(p => p.ResetType != ResetType.None) ? ResetType.Any : ResetType.None; } } } + mutex.Dispose(); } @@ -628,10 +648,11 @@ public async Task RemoveAsync(IDefinition definition) /// IEnumerable<IDefinition>. public async Task> SearchDefinitionsAsync(string searchTerm, CancellationToken? token = null) { - if (token != null && token.GetValueOrDefault().IsCancellationRequested) + if (token is { IsCancellationRequested: true }) { return null; } + if (trie != null) { var tags = trie.Get(searchTerm.ToLowerInvariant()); @@ -647,13 +668,12 @@ public async Task> SearchDefinitionsAsync(string searchTerm, { var col = searchDb.GetCollection(SearchTableName); var result = col.Query().Where(x => x.Tags.Any(f => f.Contains(searchTerm, StringComparison.OrdinalIgnoreCase))).Select(p => p.DisplayName).ToList(); - GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized); - GC.WaitForPendingFinalizers(); - GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized); + GCRunner.RunGC(GCCollectionMode.Optimized); return Task.FromResult(result.Distinct()); }, token ?? CancellationToken.None); return result; } + return null; } @@ -661,13 +681,15 @@ public async Task> SearchDefinitionsAsync(string searchTerm, /// Sets the type of the allowed. /// /// Type of the allowed. + /// Cannot set allowed type index definition is already initialized. /// Cannot set allowed type index definition is already initialized. public void SetAllowedType(AddToMapAllowedType allowedType) { - if (typeKeys.Any()) + if (typeKeys.Count != 0) { throw new InvalidOperationException("Cannot set allowed type index definition is already initialized."); } + this.allowedType = allowedType; } @@ -679,21 +701,24 @@ public void SetAllowedType(AddToMapAllowedType allowedType) public async Task UpdateDefinitionsAsync(IReadOnlyCollection definitions) { // No implementation for in memory variants - if (definitions == null || !definitions.Any()) + if (definitions == null || definitions.Count == 0) { return false; } + if (store != null) { using var mutex = await opLock.LockAsync(); var group = definitions.GroupBy(p => p.TypeAndId); foreach (var item in group) { - await store.InsertAsync(item.Key, item.ToList()); + await store.InsertAsync(item.Key, [.. item]); } + mutex.Dispose(); return true; } + return true; } @@ -701,21 +726,16 @@ public async Task UpdateDefinitionsAsync(IReadOnlyCollection /// Uses the disk store. /// /// The store path. + /// Unable to switch to disk store as there are items in the memory. /// Unable to switch to disk store as there are items in the memory. public void UseDiskStore(string storePath) { - if (definitions.Any()) + if (definitions.Count != 0) { throw new InvalidOperationException("Unable to switch to disk store as there are items in the memory."); } - store = new Store>(ResolveStoragePath(storePath), (type) => - { - if (type.Equals(nameof(IDefinition))) - { - return DIResolver.GetImplementationType(typeof(IDefinition)); - } - return null; - }); + + store = new Store>(ResolveStoragePath(storePath), type => type.Equals(nameof(IDefinition)) ? DIResolver.GetImplementationType(typeof(IDefinition)) : null); } /// @@ -723,7 +743,7 @@ public void UseDiskStore(string storePath) /// /// The database path which is specified indicates that db provider is used. /// The database path suffix. Not used if dbPath is not provided - public void UseSearch(string dbPath = Shared.Constants.EmptyParam, string dbPathSuffix = Shared.Constants.EmptyParam) + public void UseSearch(string dbPath = Constants.EmptyParam, string dbPathSuffix = Constants.EmptyParam) { if (!string.IsNullOrWhiteSpace(dbPath)) { @@ -731,7 +751,7 @@ public void UseSearch(string dbPath = Shared.Constants.EmptyParam, string dbPath DisposeSearchDB(); if (!Directory.Exists(Path.GetDirectoryName(searchDbPath))) { - Directory.CreateDirectory(Path.GetDirectoryName(searchDbPath)); + Directory.CreateDirectory(Path.GetDirectoryName(searchDbPath)!); } } else @@ -747,11 +767,11 @@ public void UseSearch(string dbPath = Shared.Constants.EmptyParam, string dbPath /// System.String. protected virtual string ResolveStoragePath(string path) { - return Path.Combine(path, Parser.Common.Constants.StoreCacheRootRolder); + return Path.Combine(path, Common.Constants.StoreCacheRootRolder); } /// - /// Adds the or remove from reset definitions. + /// Adds or removes from reset definitions. /// /// The definition. /// if set to true [add]. @@ -796,6 +816,7 @@ async Task addDefinition() { mutex = await opLock.LockAsync(); } + MapKeys(fileCIKeys, definition.FileCI, definition.TypeAndId); MapKeys(typeKeys, definition.Type, definition.TypeAndId); MapKeys(typeAndIdKeys, ConstructKey(definition.Type, definition.Id)); @@ -807,6 +828,7 @@ async Task addDefinition() MapKeys(diskFileCIKeys, definition.DiskFileCI, definition.TypeAndId); MapKeys(allFileKeys, definition.DiskFileCI); } + if (definition.OverwrittenFileNames?.Count > 0) { foreach (var item in definition.OverwrittenFileNames) @@ -814,10 +836,12 @@ async Task addDefinition() MapKeys(allFileKeys, item.ToLowerInvariant()); } } - if (useHierarchalMap && !forceIgnoreHierarchical) + + if (useHierarchicalMap && !forceIgnoreHierarchical) { MapHierarchicalDefinition(definition); } + if (definition.IsFromGame) { gameDefinitionsCount++; @@ -830,6 +854,7 @@ async Task addDefinition() { await addDefinition(); } + break; default: await addDefinition(); @@ -854,7 +879,7 @@ private string ConstructKey(params string[] keys) /// /// The source. /// IEnumerable<IHierarchicalDefinitions>. - private IEnumerable CopyHierarchicalDefinition(IEnumerable source) + private HashSet CopyHierarchicalDefinition(IEnumerable source) { var result = new HashSet(); foreach (var item in source) @@ -869,6 +894,7 @@ private IEnumerable CopyHierarchicalDefinition(IEnumer copy.NonGameDefinitions = item.NonGameDefinitions; result.Add(copy); } + return result; } @@ -890,6 +916,7 @@ private void DisposeSearchDB() { item.Attributes = FileAttributes.Normal; } + dirInfo.Delete(true); } } @@ -903,6 +930,7 @@ private void DisposeSearchDB() /// Ensures the allowed all is respected. /// /// if set to true [allow invalid]. + /// Collection is empty. /// Collection is empty. private void EnsureAllowedAllIsRespected(bool allowInvalid = false) { @@ -931,9 +959,9 @@ private LiteDatabase GetDatabase(string path) /// The definition. private void MapHierarchicalDefinition(IDefinition definition) { - bool shouldAdd = false; - var parentDirectoryCI = ResolveHierarchalParentDirectory(definition); - var hierarchicalDefinition = mainHierarchalDefinitions.GetFirstByName(nameof(IHierarchicalDefinitions.Name), parentDirectoryCI); + var shouldAdd = false; + var parentDirectoryCI = ResolveHierarchicalParentDirectory(definition); + var hierarchicalDefinition = mainHierarchicalDefinitions.GetFirstByName(nameof(IHierarchicalDefinitions.Name), parentDirectoryCI); if (hierarchicalDefinition == null) { hierarchicalDefinition = DIResolver.Get(); @@ -941,23 +969,25 @@ private void MapHierarchicalDefinition(IDefinition definition) childHierarchicalDefinitions.TryAdd(parentDirectoryCI, new ConcurrentIndexedList(nameof(IHierarchicalDefinitions.Name))); shouldAdd = true; } - bool exists = false; + + var exists = false; IHierarchicalDefinitions child = null; if (childHierarchicalDefinitions.TryGetValue(hierarchicalDefinition.Name, out var children)) { child = children.GetFirstByName(nameof(IHierarchicalDefinitions.Name), definition.Id); exists = child != null; } + if (!exists) { child = DIResolver.Get(); child.Name = definition.Id; child.Key = definition.TypeAndId; child.FileNames.Add(definition.FileCI); - children.Add(child); + children!.Add(child); if (shouldAdd) { - mainHierarchalDefinitions.Add(hierarchicalDefinition); + mainHierarchicalDefinitions.Add(hierarchicalDefinition); } } else @@ -967,23 +997,27 @@ private void MapHierarchicalDefinition(IDefinition definition) child.FileNames.Add(definition.FileCI); } } + if (definition.ResetType != ResetType.None) { child.ResetType = definition.ResetType; hierarchicalDefinition.ResetType = ResetType.Any; AddOrRemoveFromResetDefinitions(definition, true); } - child.Mods ??= new List(); + + child.Mods ??= []; if (!child.Mods.Contains(definition.ModName) && !definition.IsFromGame) { child.Mods.Add(definition.ModName); } + if (!definition.IsFromGame) { child.NonGameDefinitions++; hierarchicalDefinition.NonGameDefinitions++; } - hierarchicalDefinition.Mods ??= new List(); + + hierarchicalDefinition.Mods ??= []; if (!hierarchicalDefinition.Mods.Contains(definition.ModName) && !definition.IsFromGame) { hierarchicalDefinition.Mods.Add(definition.ModName); @@ -997,10 +1031,7 @@ private void MapHierarchicalDefinition(IDefinition definition) /// The key. private void MapKeys(HashSet map, string key) { - if (!map.Contains(key)) - { - map.Add(key); - } + map.Add(key); } /// @@ -1012,7 +1043,7 @@ private void MapKeys(HashSet map, string key) /// The cache value. private void MapKeys(Dictionary> map, T key, string cacheValue) { - if (object.Equals(key, default(T))) + if (Equals(key, default(T))) { return; } @@ -1020,13 +1051,14 @@ private void MapKeys(Dictionary> map, T key, string cacheV { return; } + if (map.TryGetValue(key, out var values)) { values.Add(cacheValue); } else { - map[key] = new HashSet() { cacheValue }; + map[key] = [cacheValue]; } } @@ -1037,7 +1069,7 @@ private void MapKeys(Dictionary> map, T key, string cacheV /// The total. private void OnProcessedSearchItem(int current, int total) { - ProcessedSearchItem?.Invoke(this, new ProcessedArgs() { Current = current, Total = total }); + ProcessedSearchItem?.Invoke(this, new ProcessedArgs { Current = current, Total = total }); } /// @@ -1053,17 +1085,13 @@ private async Task> ReadDefinitionsFromStoreAsync(IRead } /// - /// Resolves the hierarchal parent directory. + /// Resolves the hierarchical parent directory. /// /// The definition. /// System.String. - private string ResolveHierarchalParentDirectory(IDefinition definition) + private string ResolveHierarchicalParentDirectory(IDefinition definition) { - if (string.IsNullOrWhiteSpace(definition.VirtualParentDirectoryCI)) - { - return definition.ParentDirectoryCI; - } - return definition.VirtualParentDirectoryCI; + return string.IsNullOrWhiteSpace(definition.VirtualParentDirectoryCI) ? definition.ParentDirectoryCI : definition.VirtualParentDirectoryCI; } /// @@ -1081,7 +1109,7 @@ private async Task UpdateStoreDefinitionAsync(IDefinition definition) } else { - await store.InsertAsync(definition.TypeAndId, new List() { definition }); + await store.InsertAsync(definition.TypeAndId, [definition]); } } diff --git a/src/IronyModManager.Parser/IronyModManager.Parser.csproj b/src/IronyModManager.Parser/IronyModManager.Parser.csproj index 40d1ed8e1..29ba83ee5 100644 --- a/src/IronyModManager.Parser/IronyModManager.Parser.csproj +++ b/src/IronyModManager.Parser/IronyModManager.Parser.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 true ../../keys/Irony-Main.snk true @@ -40,8 +40,8 @@ - - + + all diff --git a/src/IronyModManager.Parser/Properties/PublishProfiles/linux-x64.pubxml b/src/IronyModManager.Parser/Properties/PublishProfiles/linux-x64.pubxml index 04545ee0d..b8f7f6db0 100644 --- a/src/IronyModManager.Parser/Properties/PublishProfiles/linux-x64.pubxml +++ b/src/IronyModManager.Parser/Properties/PublishProfiles/linux-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\linux-x64\net7.0\publish\linux-x64 + net8.0 + bin\x64\linux-x64\net8.0\publish\linux-x64 linux-x64 true False diff --git a/src/IronyModManager.Parser/Properties/PublishProfiles/osx-x64.pubxml b/src/IronyModManager.Parser/Properties/PublishProfiles/osx-x64.pubxml index df6bd9f9f..ff92dad82 100644 --- a/src/IronyModManager.Parser/Properties/PublishProfiles/osx-x64.pubxml +++ b/src/IronyModManager.Parser/Properties/PublishProfiles/osx-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\osx-x64\net7.0\publish\osx-x64 + net8.0 + bin\x64\osx-x64\net8.0\publish\osx-x64 osx-x64 true False diff --git a/src/IronyModManager.Parser/Properties/PublishProfiles/win-x64.pubxml b/src/IronyModManager.Parser/Properties/PublishProfiles/win-x64.pubxml index 1cd122792..cbd6468db 100644 --- a/src/IronyModManager.Parser/Properties/PublishProfiles/win-x64.pubxml +++ b/src/IronyModManager.Parser/Properties/PublishProfiles/win-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\win-x64\net7.0\publish\win-x64 + net8.0 + bin\x64\win-x64\net8.0\publish\win-x64 win-x64 true False diff --git a/src/IronyModManager.Platform/Configuration/PlatformConfigurationOptions.cs b/src/IronyModManager.Platform/Configuration/PlatformConfigurationOptions.cs index 1340f942b..2ddea5bfc 100644 --- a/src/IronyModManager.Platform/Configuration/PlatformConfigurationOptions.cs +++ b/src/IronyModManager.Platform/Configuration/PlatformConfigurationOptions.cs @@ -4,19 +4,32 @@ // Created : 04-16-2021 // // Last Modified By : Mario -// Last Modified On : 05-12-2023 +// Last Modified On : 02-11-2024 // *********************************************************************** // // Mario // // // *********************************************************************** -using System; -using System.Collections.Generic; -using System.Linq; - namespace IronyModManager.Platform.Configuration { + + /// + /// Class App. + /// + public class App + { + #region Properties + + /// + /// Gets or sets a value indicating whether [single instance]. + /// + /// true if [single instance]; otherwise, false. + public bool SingleInstance { get; set; } + + #endregion Properties + } + /// /// Class ConflictSolver. /// @@ -118,6 +131,12 @@ public class PlatformConfigurationOptions { #region Properties + /// + /// Gets the application. + /// + /// The application. + public App App { get; } = new App(); + /// /// Gets the conflict solver. /// @@ -208,6 +227,12 @@ public class Updates /// true if disable; otherwise, false. public bool Disable { get; set; } + /// + /// Gets or sets a value indicating whether [disable install only]. + /// + /// true if [disable install only]; otherwise, false. + public bool DisableInstallOnly { get; set; } + #endregion Properties } } diff --git a/src/IronyModManager.Platform/IronyModManager.Platform.csproj b/src/IronyModManager.Platform/IronyModManager.Platform.csproj index 1add30a11..b0983338f 100644 --- a/src/IronyModManager.Platform/IronyModManager.Platform.csproj +++ b/src/IronyModManager.Platform/IronyModManager.Platform.csproj @@ -1,6 +1,6 @@  - net7.0 + net8.0 true ../../keys/Irony-Main.snk true @@ -64,7 +64,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/IronyModManager.Platform/Properties/PublishProfiles/linux-x64.pubxml b/src/IronyModManager.Platform/Properties/PublishProfiles/linux-x64.pubxml index 04545ee0d..b8f7f6db0 100644 --- a/src/IronyModManager.Platform/Properties/PublishProfiles/linux-x64.pubxml +++ b/src/IronyModManager.Platform/Properties/PublishProfiles/linux-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\linux-x64\net7.0\publish\linux-x64 + net8.0 + bin\x64\linux-x64\net8.0\publish\linux-x64 linux-x64 true False diff --git a/src/IronyModManager.Platform/Properties/PublishProfiles/osx-x64.pubxml b/src/IronyModManager.Platform/Properties/PublishProfiles/osx-x64.pubxml index df6bd9f9f..ff92dad82 100644 --- a/src/IronyModManager.Platform/Properties/PublishProfiles/osx-x64.pubxml +++ b/src/IronyModManager.Platform/Properties/PublishProfiles/osx-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\osx-x64\net7.0\publish\osx-x64 + net8.0 + bin\x64\osx-x64\net8.0\publish\osx-x64 osx-x64 true False diff --git a/src/IronyModManager.Platform/Properties/PublishProfiles/win-x64.pubxml b/src/IronyModManager.Platform/Properties/PublishProfiles/win-x64.pubxml index 1cd122792..cbd6468db 100644 --- a/src/IronyModManager.Platform/Properties/PublishProfiles/win-x64.pubxml +++ b/src/IronyModManager.Platform/Properties/PublishProfiles/win-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\win-x64\net7.0\publish\win-x64 + net8.0 + bin\x64\win-x64\net8.0\publish\win-x64 win-x64 true False diff --git a/src/IronyModManager.Services.Common/IGameIndexService.cs b/src/IronyModManager.Services.Common/IGameIndexService.cs index 8ecc593dd..48ae2cdc3 100644 --- a/src/IronyModManager.Services.Common/IGameIndexService.cs +++ b/src/IronyModManager.Services.Common/IGameIndexService.cs @@ -4,13 +4,14 @@ // Created : 05-27-2021 // // Last Modified By : Mario -// Last Modified On : 09-05-2021 +// Last Modified On : 02-25-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; using System.Linq; @@ -44,8 +45,9 @@ public interface IGameIndexService : IBaseService /// The mod definitions. /// The game. /// The versions. + /// The game languages. /// Task<IIndexedDefinitions>. - Task LoadDefinitionsAsync(IIndexedDefinitions modDefinitions, IGame game, IEnumerable versions); + Task LoadDefinitionsAsync(IIndexedDefinitions modDefinitions, IGame game, IEnumerable versions, IReadOnlyCollection gameLanguages); #endregion Methods } diff --git a/src/IronyModManager.Services.Common/IGameLanguageService.cs b/src/IronyModManager.Services.Common/IGameLanguageService.cs new file mode 100644 index 000000000..4c18aee39 --- /dev/null +++ b/src/IronyModManager.Services.Common/IGameLanguageService.cs @@ -0,0 +1,56 @@ +// *********************************************************************** +// Assembly : IronyModManager.Services.Common +// Author : Mario +// Created : 02-25-2024 +// +// Last Modified By : Mario +// Last Modified On : 02-25-2024 +// *********************************************************************** +// +// Copyright (c) . All rights reserved. +// +// +// *********************************************************************** + +using System; +using System.Collections.Generic; +using System.Linq; +using IronyModManager.Models.Common; + +namespace IronyModManager.Services.Common +{ + /// + /// An game language service interface. + /// + public interface IGameLanguageService : IBaseService + { + #region Methods + + /// + /// Get. + /// + /// A list of IGameLanguages. + IEnumerable Get(); + + /// + /// Gets the by abrv. + /// + /// The languages. + /// IReadOnlyCollection<IGameLanguage>. + IReadOnlyCollection GetByAbrv(IReadOnlyCollection languages); + + /// + /// Get selected. + /// + /// A read only collection of IGameLanguages. + IReadOnlyCollection GetSelected(); + + /// + /// Save. + /// + /// The languages. + void Save(IEnumerable languages); + + #endregion Methods + } +} diff --git a/src/IronyModManager.Services.Common/IModPatchCollectionService.cs b/src/IronyModManager.Services.Common/IModPatchCollectionService.cs index 51fe43e77..4892dbd13 100644 --- a/src/IronyModManager.Services.Common/IModPatchCollectionService.cs +++ b/src/IronyModManager.Services.Common/IModPatchCollectionService.cs @@ -1,19 +1,20 @@ - -// *********************************************************************** +// *********************************************************************** // Assembly : IronyModManager.Services.Common // Author : Mario // Created : 05-26-2020 // // Last Modified By : Mario -// Last Modified On : 06-28-2023 +// Last Modified On : 02-25-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using IronyModManager.Models.Common; using IronyModManager.Parser.Common.Parsers.Models; @@ -21,7 +22,6 @@ namespace IronyModManager.Services.Common { - /// /// Interface IModPatchCollectionService /// Implements the @@ -92,8 +92,16 @@ public interface IModPatchCollectionService : IBaseService /// The indexed definitions. /// The mod order. /// The patch state mode. + /// The allowed languages. /// Task<IConflictResult>. - Task FindConflictsAsync(IIndexedDefinitions indexedDefinitions, IList modOrder, PatchStateMode patchStateMode); + Task FindConflictsAsync(IIndexedDefinitions indexedDefinitions, IList modOrder, PatchStateMode patchStateMode, IReadOnlyCollection allowedLanguages); + + /// + /// Gets an allowed languages async. + /// + /// The collection name. + /// A Task containing IReadOnlyCollection of strings. + Task> GetAllowedLanguagesAsync(string collectionName); /// /// Gets the bracket count. @@ -117,8 +125,9 @@ public interface IModPatchCollectionService : IBaseService /// The mods. /// Name of the collection. /// The mode. + /// The allowed game languages. /// Task<IIndexedDefinitions>. - Task GetModObjectsAsync(IGame game, IEnumerable mods, string collectionName, PatchStateMode mode); + Task GetModObjectsAsync(IGame game, IEnumerable mods, string collectionName, PatchStateMode mode, IReadOnlyCollection allowedGameLanguages); /// /// Gets the patch state mode asynchronous. diff --git a/src/IronyModManager.Services.Common/IronyModManager.Services.Common.csproj b/src/IronyModManager.Services.Common/IronyModManager.Services.Common.csproj index 1f2fd020e..32e5cf8a1 100644 --- a/src/IronyModManager.Services.Common/IronyModManager.Services.Common.csproj +++ b/src/IronyModManager.Services.Common/IronyModManager.Services.Common.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 true ../../keys/Irony-Main.snk Irony Mod Manager Services Common Component diff --git a/src/IronyModManager.Services.Common/Properties/PublishProfiles/linux-x64.pubxml b/src/IronyModManager.Services.Common/Properties/PublishProfiles/linux-x64.pubxml index 04545ee0d..b8f7f6db0 100644 --- a/src/IronyModManager.Services.Common/Properties/PublishProfiles/linux-x64.pubxml +++ b/src/IronyModManager.Services.Common/Properties/PublishProfiles/linux-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\linux-x64\net7.0\publish\linux-x64 + net8.0 + bin\x64\linux-x64\net8.0\publish\linux-x64 linux-x64 true False diff --git a/src/IronyModManager.Services.Common/Properties/PublishProfiles/osx-x64.pubxml b/src/IronyModManager.Services.Common/Properties/PublishProfiles/osx-x64.pubxml index df6bd9f9f..ff92dad82 100644 --- a/src/IronyModManager.Services.Common/Properties/PublishProfiles/osx-x64.pubxml +++ b/src/IronyModManager.Services.Common/Properties/PublishProfiles/osx-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\osx-x64\net7.0\publish\osx-x64 + net8.0 + bin\x64\osx-x64\net8.0\publish\osx-x64 osx-x64 true False diff --git a/src/IronyModManager.Services.Common/Properties/PublishProfiles/win-x64.pubxml b/src/IronyModManager.Services.Common/Properties/PublishProfiles/win-x64.pubxml index 1cd122792..cbd6468db 100644 --- a/src/IronyModManager.Services.Common/Properties/PublishProfiles/win-x64.pubxml +++ b/src/IronyModManager.Services.Common/Properties/PublishProfiles/win-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\win-x64\net7.0\publish\win-x64 + net8.0 + bin\x64\win-x64\net8.0\publish\win-x64 win-x64 true False diff --git a/src/IronyModManager.Services.Tests/GameIndexServiceTests.cs b/src/IronyModManager.Services.Tests/GameIndexServiceTests.cs index 0dc13c0bf..0ce5f891c 100644 --- a/src/IronyModManager.Services.Tests/GameIndexServiceTests.cs +++ b/src/IronyModManager.Services.Tests/GameIndexServiceTests.cs @@ -11,11 +11,11 @@ // // // *********************************************************************** + using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; using AutoMapper; using FluentAssertions; @@ -43,6 +43,7 @@ namespace IronyModManager.Services.Tests /// /// Class GameIndexServiceTests. /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0028:Simplify collection initialization", Justification = "It's a unit test, back off")] public class GameIndexServiceTests { /// @@ -59,13 +60,14 @@ public class GameIndexServiceTests /// The definition information providers. /// GameIndexService. private static GameIndexService GetService(Mock gameIndexer, Mock storageProvider, Mock modParser, - Mock parserManager, Mock reader, Mock mapper, Mock modWriter, - Mock gameService, IEnumerable definitionInfoProviders = null) + Mock parserManager, Mock reader, Mock mapper, Mock modWriter, + Mock gameService, IEnumerable definitionInfoProviders = null) { var messageBus = new Mock(); messageBus.Setup(p => p.PublishAsync(It.IsAny())); messageBus.Setup(p => p.Publish(It.IsAny())); - return new GameIndexService(messageBus.Object, parserManager.Object, gameIndexer.Object, new Cache(), definitionInfoProviders, reader.Object, modWriter.Object, modParser.Object, gameService.Object, storageProvider.Object, mapper.Object); + return new GameIndexService(messageBus.Object, parserManager.Object, gameIndexer.Object, new Cache(), definitionInfoProviders, reader.Object, modWriter.Object, modParser.Object, gameService.Object, storageProvider.Object, + mapper.Object); } /// @@ -84,7 +86,7 @@ public async Task Should_not_index_definitions_when_no_game() var gameIndexer = new Mock(); var service = GetService(gameIndexer, storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService); - var result = await service.IndexDefinitionsAsync(null, new List() { "3.0.3" }, new IndexedDefinitions()); + var result = await service.IndexDefinitionsAsync(null, new List { "3.0.3" }, new IndexedDefinitions()); result.Should().BeFalse(); } @@ -128,37 +130,24 @@ public async Task Should_index_definitions_when_definition_signature_not_same() gameIndexer.Setup(p => p.ClearDefinitionAsync(It.IsAny(), It.IsAny())).Returns((string p1, IGame p2) => Task.FromResult(false)); gameIndexer.Setup(p => p.WriteVersionAsync(It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())).Returns((string p1, IGame p2, IEnumerable p3, int p4) => Task.FromResult(false)); gameIndexer.Setup(p => p.FolderCachedAsync(It.IsAny(), It.IsAny(), It.IsAny())).Returns((string p1, IGame p2, string p3) => Task.FromResult(false)); - reader.Setup(p => p.GetFiles(It.IsAny())).Returns(new List() { "test1\\1.txt", "test2\\2.txt", "test3\\3.txt" }); - var fileInfos1 = new List() - { - new FileInfo() - { - Content = new List() { "1" }, - FileName = "test1\\1.txt", - IsBinary = false - } - }; - var fileInfos2 = new List() - { - new FileInfo() - { - Content = new List() { "1" }, - FileName = "test2\\2.txt", - IsBinary = false - } - }; + reader.Setup(p => p.GetFiles(It.IsAny())).Returns(new List { "test1\\1.txt", "test2\\2.txt", "test3\\3.txt" }); + var fileInfos1 = new List { new FileInfo { Content = new List { "1" }, FileName = "test1\\1.txt", IsBinary = false } }; + var fileInfos2 = new List { new FileInfo { Content = new List { "1" }, FileName = "test2\\2.txt", IsBinary = false } }; reader.Setup(s => s.Read(It.Is(p => p.Contains("test1")), It.IsAny>(), It.IsAny())).Returns(fileInfos1); reader.Setup(s => s.Read(It.Is(p => p.Contains("test2")), It.IsAny>(), It.IsAny())).Returns(fileInfos2); parserManager.Setup(s => s.Parse(It.IsAny())).Returns((ParserManagerArgs args) => { - return new List() { new Definition() + return new List { - Code = args.File, - File = args.File, - ContentSHA = args.File, - Id = args.File, - Type = args.ModName - } }; + new Definition + { + Code = args.File, + File = args.File, + ContentSHA = args.File, + Id = args.File, + Type = args.ModName + } + }; }); var saved = new ConcurrentBag(); gameIndexer.Setup(p => p.SaveDefinitionsAsync(It.IsAny(), It.IsAny(), It.IsAny>())).Returns((string p1, IGame p2, IEnumerable p3) => @@ -169,8 +158,10 @@ public async Task Should_index_definitions_when_definition_signature_not_same() var service = GetService(gameIndexer, storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService); var indexed = new IndexedDefinitions(); - await indexed.InitMapAsync(new List() { new Definition() { File = "test1\\1.txt" }, new Definition() { File = "test2\\3.txt" } }); - var result = await service.IndexDefinitionsAsync(new Game() { ExecutableLocation = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)), GameFolders = new List() { "test1", "test2" } }, new List() { "3.0.3" }, indexed); + await indexed.InitMapAsync(new List { new Definition { File = "test1\\1.txt" }, new Definition { File = "test2\\3.txt" } }); + var result = await service.IndexDefinitionsAsync( + new Game { ExecutableLocation = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)), GameFolders = new List { "test1", "test2" } }, + new List { "3.0.3" }, indexed); result.Should().BeTrue(); saved.Count.Should().Be(2); saved.FirstOrDefault(p => p.Contains("test1")).Should().NotBeNull(); @@ -197,37 +188,24 @@ public async Task Should_index_definitions_when_game_version_signature_not_same( gameIndexer.Setup(p => p.ClearDefinitionAsync(It.IsAny(), It.IsAny())).Returns((string p1, IGame p2) => Task.FromResult(false)); gameIndexer.Setup(p => p.WriteVersionAsync(It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())).Returns((string p1, IGame p2, IEnumerable p3, int p4) => Task.FromResult(false)); gameIndexer.Setup(p => p.FolderCachedAsync(It.IsAny(), It.IsAny(), It.IsAny())).Returns((string p1, IGame p2, string p3) => Task.FromResult(false)); - reader.Setup(p => p.GetFiles(It.IsAny())).Returns(new List() { "test1\\1.txt", "test2\\2.txt", "test3\\3.txt" }); - var fileInfos1 = new List() - { - new FileInfo() - { - Content = new List() { "1" }, - FileName = "test1\\1.txt", - IsBinary = false - } - }; - var fileInfos2 = new List() - { - new FileInfo() - { - Content = new List() { "1" }, - FileName = "test2\\2.txt", - IsBinary = false - } - }; + reader.Setup(p => p.GetFiles(It.IsAny())).Returns(new List { "test1\\1.txt", "test2\\2.txt", "test3\\3.txt" }); + var fileInfos1 = new List { new FileInfo { Content = new List { "1" }, FileName = "test1\\1.txt", IsBinary = false } }; + var fileInfos2 = new List { new FileInfo { Content = new List { "1" }, FileName = "test2\\2.txt", IsBinary = false } }; reader.Setup(s => s.Read(It.Is(p => p.Contains("test1")), It.IsAny>(), It.IsAny())).Returns(fileInfos1); reader.Setup(s => s.Read(It.Is(p => p.Contains("test2")), It.IsAny>(), It.IsAny())).Returns(fileInfos2); parserManager.Setup(s => s.Parse(It.IsAny())).Returns((ParserManagerArgs args) => { - return new List() { new Definition() + return new List { - Code = args.File, - File = args.File, - ContentSHA = args.File, - Id = args.File, - Type = args.ModName - } }; + new Definition + { + Code = args.File, + File = args.File, + ContentSHA = args.File, + Id = args.File, + Type = args.ModName + } + }; }); var saved = new ConcurrentBag(); gameIndexer.Setup(p => p.SaveDefinitionsAsync(It.IsAny(), It.IsAny(), It.IsAny>())).Returns((string p1, IGame p2, IEnumerable p3) => @@ -238,8 +216,10 @@ public async Task Should_index_definitions_when_game_version_signature_not_same( var service = GetService(gameIndexer, storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService); var indexed = new IndexedDefinitions(); - await indexed.InitMapAsync(new List() { new Definition() { File = "test1\\1.txt" }, new Definition() { File = "test2\\3.txt" } }); - var result = await service.IndexDefinitionsAsync(new Game() { ExecutableLocation = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)), GameFolders = new List() { "test1", "test2" } }, new List() { "3.0.3" }, indexed); + await indexed.InitMapAsync(new List { new Definition { File = "test1\\1.txt" }, new Definition { File = "test2\\3.txt" } }); + var result = await service.IndexDefinitionsAsync( + new Game { ExecutableLocation = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)), GameFolders = new List { "test1", "test2" } }, + new List { "3.0.3" }, indexed); result.Should().BeTrue(); saved.Count.Should().Be(2); saved.FirstOrDefault(p => p.Contains("test1")).Should().NotBeNull(); @@ -271,39 +251,27 @@ public async Task Should_index_definitions_which_are_not_indexed() { return Task.FromResult(true); } + return Task.FromResult(false); }); - reader.Setup(p => p.GetFiles(It.IsAny())).Returns(new List() { "test1\\1.txt", "test2\\2.txt", "test3\\3.txt" }); - var fileInfos1 = new List() - { - new FileInfo() - { - Content = new List() { "1" }, - FileName = "test1\\1.txt", - IsBinary = false - } - }; - var fileInfos2 = new List() - { - new FileInfo() - { - Content = new List() { "1" }, - FileName = "test2\\2.txt", - IsBinary = false - } - }; + reader.Setup(p => p.GetFiles(It.IsAny())).Returns(new List { "test1\\1.txt", "test2\\2.txt", "test3\\3.txt" }); + var fileInfos1 = new List { new FileInfo { Content = new List { "1" }, FileName = "test1\\1.txt", IsBinary = false } }; + var fileInfos2 = new List { new FileInfo { Content = new List { "1" }, FileName = "test2\\2.txt", IsBinary = false } }; reader.Setup(s => s.Read(It.Is(p => p.Contains("test1")), It.IsAny>(), It.IsAny())).Returns(fileInfos1); reader.Setup(s => s.Read(It.Is(p => p.Contains("test2")), It.IsAny>(), It.IsAny())).Returns(fileInfos2); parserManager.Setup(s => s.Parse(It.IsAny())).Returns((ParserManagerArgs args) => { - return new List() { new Definition() + return new List { - Code = args.File, - File = args.File, - ContentSHA = args.File, - Id = args.File, - Type = args.ModName - } }; + new Definition + { + Code = args.File, + File = args.File, + ContentSHA = args.File, + Id = args.File, + Type = args.ModName + } + }; }); var saved = new ConcurrentBag(); gameIndexer.Setup(p => p.SaveDefinitionsAsync(It.IsAny(), It.IsAny(), It.IsAny>())).Returns((string p1, IGame p2, IEnumerable p3) => @@ -314,8 +282,10 @@ public async Task Should_index_definitions_which_are_not_indexed() var service = GetService(gameIndexer, storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService); var indexed = new IndexedDefinitions(); - await indexed.InitMapAsync(new List() { new Definition() { File = "test1\\1.txt" }, new Definition() { File = "test2\\3.txt" } }); - var result = await service.IndexDefinitionsAsync(new Game() { ExecutableLocation = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)), GameFolders = new List() { "test1", "test2" } }, new List() { "3.0.3" }, indexed); + await indexed.InitMapAsync(new List { new Definition { File = "test1\\1.txt" }, new Definition { File = "test2\\3.txt" } }); + var result = await service.IndexDefinitionsAsync( + new Game { ExecutableLocation = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)), GameFolders = new List { "test1", "test2" } }, + new List { "3.0.3" }, indexed); result.Should().BeTrue(); saved.Count.Should().Be(1); saved.FirstOrDefault(p => p.Contains("test1")).Should().NotBeNull(); @@ -342,37 +312,24 @@ public async Task Should_not_index_definitions() gameIndexer.Setup(p => p.ClearDefinitionAsync(It.IsAny(), It.IsAny())).Returns((string p1, IGame p2) => Task.FromResult(false)); gameIndexer.Setup(p => p.WriteVersionAsync(It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())).Returns((string p1, IGame p2, IEnumerable p3, int p4) => Task.FromResult(false)); gameIndexer.Setup(p => p.FolderCachedAsync(It.IsAny(), It.IsAny(), It.IsAny())).Returns((string p1, IGame p2, string p3) => Task.FromResult(true)); - reader.Setup(p => p.GetFiles(It.IsAny())).Returns(new List() { "test1\\1.txt", "test2\\2.txt", "test3\\3.txt" }); - var fileInfos1 = new List() - { - new FileInfo() - { - Content = new List() { "1" }, - FileName = "test1\\1.txt", - IsBinary = false - } - }; - var fileInfos2 = new List() - { - new FileInfo() - { - Content = new List() { "1" }, - FileName = "test2\\2.txt", - IsBinary = false - } - }; + reader.Setup(p => p.GetFiles(It.IsAny())).Returns(new List { "test1\\1.txt", "test2\\2.txt", "test3\\3.txt" }); + var fileInfos1 = new List { new FileInfo { Content = new List { "1" }, FileName = "test1\\1.txt", IsBinary = false } }; + var fileInfos2 = new List { new FileInfo { Content = new List { "1" }, FileName = "test2\\2.txt", IsBinary = false } }; reader.Setup(s => s.Read(It.Is(p => p.Contains("test1")), It.IsAny>(), It.IsAny())).Returns(fileInfos1); reader.Setup(s => s.Read(It.Is(p => p.Contains("test2")), It.IsAny>(), It.IsAny())).Returns(fileInfos2); parserManager.Setup(s => s.Parse(It.IsAny())).Returns((ParserManagerArgs args) => { - return new List() { new Definition() + return new List { - Code = args.File, - File = args.File, - ContentSHA = args.File, - Id = args.File, - Type = args.ModName - } }; + new Definition + { + Code = args.File, + File = args.File, + ContentSHA = args.File, + Id = args.File, + Type = args.ModName + } + }; }); var saved = new ConcurrentBag(); gameIndexer.Setup(p => p.SaveDefinitionsAsync(It.IsAny(), It.IsAny(), It.IsAny>())).Returns((string p1, IGame p2, IEnumerable p3) => @@ -383,8 +340,8 @@ public async Task Should_not_index_definitions() var service = GetService(gameIndexer, storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService); var indexed = new IndexedDefinitions(); - await indexed.InitMapAsync(new List() { new Definition() { File = "test1\\1.txt" }, new Definition() { File = "test2\\3.txt" } }); - var result = await service.IndexDefinitionsAsync(new Game() { ExecutableLocation = "c:\\test\\test.exe", GameFolders = new List() { "test1", "test2" } }, new List() { "3.0.3" }, indexed); + await indexed.InitMapAsync(new List { new Definition { File = "test1\\1.txt" }, new Definition { File = "test2\\3.txt" } }); + var result = await service.IndexDefinitionsAsync(new Game { ExecutableLocation = "c:\\test\\test.exe", GameFolders = new List { "test1", "test2" } }, new List { "3.0.3" }, indexed); result.Should().BeTrue(); saved.Count.Should().Be(0); } @@ -405,17 +362,9 @@ public async Task Should_not_load_definitions_when_no_game() var gameIndexer = new Mock(); var defs = new IndexedDefinitions(); - await defs.InitMapAsync(new List() - { - new Definition() - { - Type = "test", - Id = "1", - ModName = "test" - } - }); + await defs.InitMapAsync(new List { new Definition { Type = "test", Id = "1", ModName = "test" } }); var service = GetService(gameIndexer, storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService); - var result = await service.LoadDefinitionsAsync(defs, null, new List() { "3.0.3" }); + var result = await service.LoadDefinitionsAsync(defs, null, new List { "3.0.3" }, null); result.Should().Be(defs); } @@ -435,17 +384,9 @@ public async Task Should_not_load_definitions_when_no_version() var gameIndexer = new Mock(); var defs = new IndexedDefinitions(); - await defs.InitMapAsync(new List() - { - new Definition() - { - Type = "test", - Id = "1", - ModName = "test" - } - }); + await defs.InitMapAsync(new List { new Definition { Type = "test", Id = "1", ModName = "test" } }); var service = GetService(gameIndexer, storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService); - var result = await service.LoadDefinitionsAsync(defs, new Game(), new List() { string.Empty }); + var result = await service.LoadDefinitionsAsync(defs, new Game(), new List { string.Empty }, null); result.Should().Be(defs); } @@ -467,17 +408,9 @@ public async Task Should_not_load_definitions_when_definitions_dont_exist() gameIndexer.Setup(p => p.GameVersionsSameAsync(It.IsAny(), It.IsAny(), It.IsAny>())).Returns((string p1, IGame p2, IEnumerable p3) => Task.FromResult(false)); var defs = new IndexedDefinitions(); - await defs.InitMapAsync(new List() - { - new Definition() - { - Type = "test", - Id = "1", - ModName = "test" - } - }); + await defs.InitMapAsync(new List { new Definition { Type = "test", Id = "1", ModName = "test" } }); var service = GetService(gameIndexer, storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService); - var result = await service.LoadDefinitionsAsync(defs, new Game(), new List() { "3.0.3" }); + var result = await service.LoadDefinitionsAsync(defs, new Game(), new List { "3.0.3" }, null); result.Should().Be(defs); } @@ -499,22 +432,13 @@ public async Task Should_load_definitions() var gameIndexer = new Mock(); storageProvider.Setup(p => p.GetRootStoragePath()).Returns("c:\\test"); gameIndexer.Setup(p => p.GameVersionsSameAsync(It.IsAny(), It.IsAny(), It.IsAny>())).Returns((string p1, IGame p2, IEnumerable p3) => Task.FromResult(true)); - var gameDefs = new List() { new Definition() { File = "test\\testgame.txt", Type = "test", Id = "2", ModName = "test game" } }; + var gameDefs = new List { new Definition { File = "test\\testgame.txt", Type = "test", Id = "2", ModName = "test game" } }; gameIndexer.Setup(p => p.GetDefinitionsAsync(It.IsAny(), It.IsAny(), It.IsAny())).Returns((string p1, IGame p2, string p3) => Task.FromResult(gameDefs as IEnumerable)); var defs = new IndexedDefinitions(); - await defs.InitMapAsync(new List() - { - new Definition() - { - File = "test\\test.txt", - Type = "test", - Id = "1", - ModName = "test" - } - }); + await defs.InitMapAsync(new List { new Definition { File = "test\\test.txt", Type = "test", Id = "1", ModName = "test" } }); var service = GetService(gameIndexer, storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService); - var result = await service.LoadDefinitionsAsync(defs, new Game() { Name = "fake game" }, new List() { "3.0.3" }); + var result = await service.LoadDefinitionsAsync(defs, new Game { Name = "fake game" }, new List { "3.0.3" }, null); (await result.GetAllAsync()).Count().Should().Be(2); (await result.GetAllAsync()).FirstOrDefault(p => p.ModName == "fake game").Should().NotBeNull(); } diff --git a/src/IronyModManager.Services.Tests/GameLanguageServiceTests.cs b/src/IronyModManager.Services.Tests/GameLanguageServiceTests.cs new file mode 100644 index 000000000..0f80ccda0 --- /dev/null +++ b/src/IronyModManager.Services.Tests/GameLanguageServiceTests.cs @@ -0,0 +1,134 @@ +// *********************************************************************** +// Assembly : +// Author : Mario +// Created : 02-25-2024 +// +// Last Modified By : Mario +// Last Modified On : 02-25-2024 +// *********************************************************************** +// +// Copyright (c) . All rights reserved. +// +// +// *********************************************************************** + +using System; +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using IronyModManager.Localization; +using IronyModManager.Models; +using IronyModManager.Models.Common; +using IronyModManager.Services.Common; +using IronyModManager.Storage.Common; +using IronyModManager.Tests.Common; +using Moq; +using Xunit; + +namespace IronyModManager.Services.Tests +{ + /// + /// The game language service tests. + /// + public class GameLanguageServiceTests + { + /// + /// Setup mocks. + /// + /// The preferences service. + /// A list of strings + private static void SetupMocks(Mock preferencesService, bool csSet, params string[] languages) + { + DISetup.SetupContainer(); + CurrentLocale.SetCurrent("en"); + preferencesService.Setup(p => p.Get()).Returns(() => new Preferences { ConflictSolverLanguages = [.. languages], ConflictSolverLanguagesSet = csSet}); + preferencesService.Setup(p => p.Save(It.IsAny())).Returns(true); + } + + /// + /// Shoulds a return default to all language. + /// + [Fact] + public void Should_return_default_to_all_language() + { + // So we use a magic string -- because lazy ensure that tests cover it properly + var preferencesService = new Mock(); + SetupMocks(preferencesService, false, "l_english"); + var service = new GameLanguageService(new Mock().Object, null, preferencesService.Object); + var result = service.Get(); + result.All(p => p.IsSelected).Should().BeTrue(); + } + + /// + /// Shoulds a return valid selection. + /// + [Fact] + public void Should_return_valid_selection() + { + var preferencesService = new Mock(); + SetupMocks(preferencesService, true, "l_english"); + var service = new GameLanguageService(new Mock().Object, null, preferencesService.Object); + var result = service.Get(); + result.Count(p => p.IsSelected).Should().Be(1); + result.FirstOrDefault(p => p.IsSelected)!.Type.Should().Be("l_english"); + } + + /// + /// Defines the test method Should_return_only_selected. + /// + [Fact] + public void Should_return_only_selected() + { + var preferencesService = new Mock(); + SetupMocks(preferencesService, true,"l_english"); + var service = new GameLanguageService(new Mock().Object, null, preferencesService.Object); + var result = service.GetSelected(); + result.Count.Should().Be(1); + result.FirstOrDefault(p => p.IsSelected)!.Type.Should().Be("l_english"); + } + + /// + /// Shoulds a return only requested. + /// + /// + [Fact] + public void Should_return_only_requested() + { + var preferencesService = new Mock(); + SetupMocks(preferencesService, true, "l_english"); + var service = new GameLanguageService(new Mock().Object, null, preferencesService.Object); + var result = service.GetByAbrv(["l_german"]); + result.Count.Should().Be(1); + result.FirstOrDefault(p => p.IsSelected)!.Type.Should().Be("l_german"); + } + + /// + /// Shoulds a save selection. + /// + [Fact] + public void Should_save_selection() + { + var preferencesService = new Mock(); + SetupMocks(preferencesService, false,"l_german"); + IPreferences prefs = null; + preferencesService.Setup(p => p.Save(It.IsAny())).Returns((IPreferences saved) => + { + prefs = saved; + return true; + }); + var service = new GameLanguageService(new Mock().Object, null, preferencesService.Object); + var langs = service.Get(); + foreach (var lang in langs) + { + lang.IsSelected = lang.Type == "l_english"; + } + + service.Save(langs); + + prefs.Should().NotBeNull(); + prefs.ConflictSolverLanguages.Count.Should().Be(1); + prefs.ConflictSolverLanguages.FirstOrDefault().Should().Be("l_english"); + prefs.ConflictSolverLanguagesSet.Should().BeTrue(); + } + } +} diff --git a/src/IronyModManager.Services.Tests/IronyModManager.Services.Tests.csproj b/src/IronyModManager.Services.Tests/IronyModManager.Services.Tests.csproj index 0d535aae1..85e34f203 100644 --- a/src/IronyModManager.Services.Tests/IronyModManager.Services.Tests.csproj +++ b/src/IronyModManager.Services.Tests/IronyModManager.Services.Tests.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 IronyModManager.Services.Tests LICENSE logo.png @@ -43,16 +43,16 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/IronyModManager.Services.Tests/ModPatchCollectionServiceTests.cs b/src/IronyModManager.Services.Tests/ModPatchCollectionServiceTests.cs index c2038b722..2044f961c 100644 --- a/src/IronyModManager.Services.Tests/ModPatchCollectionServiceTests.cs +++ b/src/IronyModManager.Services.Tests/ModPatchCollectionServiceTests.cs @@ -4,7 +4,7 @@ // Created : 05-26-2020 // // Last Modified By : Mario -// Last Modified On : 06-28-2023 +// Last Modified On : 02-25-2024 // *********************************************************************** // // Mario @@ -20,13 +20,11 @@ using System.Threading.Tasks; using AutoMapper; using FluentAssertions; -using IronyModManager.IO.Common; using IronyModManager.IO.Common.Mods; using IronyModManager.IO.Common.Readers; using IronyModManager.IO.Mods.Models; using IronyModManager.Models; using IronyModManager.Models.Common; -using IronyModManager.Parser; using IronyModManager.Parser.Common; using IronyModManager.Parser.Common.Args; using IronyModManager.Parser.Common.Mod; @@ -52,6 +50,7 @@ namespace IronyModManager.Services.Tests /// /// Class ModPatchCollectionServiceTests. /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0028:Simplify collection initialization", Justification = "It's a unit test")] public class ModPatchCollectionServiceTests { /// @@ -67,15 +66,18 @@ public class ModPatchCollectionServiceTests /// The mod patch exporter. /// The definition information providers. /// The validate parser. + /// The parametrized parser. /// ModService. private static ModPatchCollectionService GetService(Mock storageProvider, Mock modParser, Mock parserManager, Mock reader, Mock mapper, Mock modWriter, - Mock gameService, Mock modPatchExporter, IEnumerable definitionInfoProviders = null, Mock validateParser = null, Mock parametrizedParser = null) + Mock gameService, Mock modPatchExporter, IEnumerable definitionInfoProviders = null, Mock validateParser = null, + Mock parametrizedParser = null) { var messageBus = new Mock(); messageBus.Setup(p => p.PublishAsync(It.IsAny())); messageBus.Setup(p => p.Publish(It.IsAny())); - return new ModPatchCollectionService(new Cache(), messageBus.Object, parserManager.Object, definitionInfoProviders, modPatchExporter.Object, reader.Object, modWriter.Object, modParser.Object, gameService.Object, storageProvider.Object, mapper.Object, validateParser?.Object, parametrizedParser?.Object); + return new ModPatchCollectionService(new Cache(), messageBus.Object, parserManager.Object, definitionInfoProviders, modPatchExporter.Object, reader.Object, modWriter.Object, modParser.Object, gameService.Object, + storageProvider.Object, mapper.Object, validateParser?.Object, parametrizedParser?.Object); } /// @@ -86,42 +88,30 @@ private static ModPatchCollectionService GetService(Mock stora /// The mod parser. private static void SetupMockCase(Mock reader, Mock parserManager, Mock modParser) { - var fileInfos = new List() + var fileInfos = new List { - new FileInfo() - { - Content = new List() { "1" }, - FileName = "fake1.txt", - IsBinary = false - }, - new FileInfo() - { - Content = new List() { "2" } , - FileName = "fake2.txt", - IsBinary = false - } + new FileInfo { Content = new List { "1" }, FileName = "fake1.txt", IsBinary = false }, new FileInfo { Content = new List { "2" }, FileName = "fake2.txt", IsBinary = false } }; reader.Setup(s => s.Read(It.IsAny(), It.IsAny>(), It.IsAny())).Returns(fileInfos); modParser.Setup(s => s.Parse(It.IsAny>(), It.IsAny())).Returns((IEnumerable values, DescriptorModType t) => { - return new ModObject() - { - FileName = values.First(), - Name = values.First() - }; + return new ModObject { FileName = values.First(), Name = values.First() }; }); parserManager.Setup(s => s.Parse(It.IsAny())).Returns((ParserManagerArgs args) => { - return new List() { new Definition() + return new List { - Code = args.File, - File = args.File, - ContentSHA = args.File, - Id = args.File, - Type = args.ModName - } }; + new Definition + { + Code = args.File, + File = args.File, + ContentSHA = args.File, + Id = args.File, + Type = args.ModName + } + }; }); } @@ -141,13 +131,13 @@ public async Task Should_not_return_any_mod_objects_when_no_game_or_mods() var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var result = await service.GetModObjectsAsync(null, new List(), string.Empty, IronyModManager.Models.Common.PatchStateMode.Advanced); + var result = await service.GetModObjectsAsync(null, new List(), string.Empty, PatchStateMode.Advanced, null); result.Should().BeNull(); - result = await service.GetModObjectsAsync(new Game(), new List(), string.Empty, IronyModManager.Models.Common.PatchStateMode.Advanced); + result = await service.GetModObjectsAsync(new Game(), new List(), string.Empty, PatchStateMode.Advanced, null); result.Should().BeNull(); - result = await service.GetModObjectsAsync(new Game(), null, string.Empty, IronyModManager.Models.Common.PatchStateMode.Advanced); + result = await service.GetModObjectsAsync(new Game(), null, string.Empty, PatchStateMode.Advanced, null); result.Should().BeNull(); } @@ -174,15 +164,9 @@ public async Task Should_return_mod_objects_when_using_fully_qualified_path() SetupMockCase(reader, parserManager, modParser); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); - var result = await service.GetModObjectsAsync(new Game() { UserDirectory = "c:\\fake", GameFolders = new List() }, new List() - { - new Mod() - { - FileName = Assembly.GetExecutingAssembly().Location, - Name = "fake" - } - }, string.Empty, IronyModManager.Models.Common.PatchStateMode.Advanced); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); + var result = await service.GetModObjectsAsync(new Game { UserDirectory = "c:\\fake", GameFolders = new List() }, new List { new Mod { FileName = Assembly.GetExecutingAssembly().Location, Name = "fake" } }, + string.Empty, PatchStateMode.Advanced, null); (await result.GetAllAsync()).Count().Should().Be(2); var ordered = (await result.GetAllAsync()).OrderBy(p => p.Id); ordered.First().Id.Should().Be("fake1.txt"); @@ -212,15 +196,9 @@ public async Task Should_return_mod_objects_when_using_user_directory() SetupMockCase(reader, parserManager, modParser); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); - var result = await service.GetModObjectsAsync(new Game() { UserDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), WorkshopDirectory = new List(), GameFolders = new List() { "fake1" } }, new List() - { - new Mod() - { - FileName = Path.GetFileName(Assembly.GetExecutingAssembly().Location), - Name = "fake" - } - }, string.Empty, IronyModManager.Models.Common.PatchStateMode.Advanced); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); + var result = await service.GetModObjectsAsync(new Game { UserDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), WorkshopDirectory = new List(), GameFolders = new List { "fake1" } }, + new List { new Mod { FileName = Path.GetFileName(Assembly.GetExecutingAssembly().Location), Name = "fake" } }, string.Empty, PatchStateMode.Advanced, null); (await result.GetAllAsync()).Count().Should().Be(2); var ordered = (await result.GetAllAsync()).OrderBy(p => p.Id); ordered.First().Id.Should().Be("fake1.txt"); @@ -250,15 +228,9 @@ public async Task Should_return_mod_objects_when_using_workshop_directory() SetupMockCase(reader, parserManager, modParser); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); - var result = await service.GetModObjectsAsync(new Game() { WorkshopDirectory = new List() { Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) }, UserDirectory = "fake1", GameFolders = new List() }, new List() - { - new Mod() - { - FileName = Path.GetFileName(Assembly.GetExecutingAssembly().Location), - Name = "fake" - } - }, string.Empty, IronyModManager.Models.Common.PatchStateMode.Advanced); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); + var result = await service.GetModObjectsAsync(new Game { WorkshopDirectory = new List { Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) }, UserDirectory = "fake1", GameFolders = new List() }, + new List { new Mod { FileName = Path.GetFileName(Assembly.GetExecutingAssembly().Location), Name = "fake" } }, string.Empty, PatchStateMode.Advanced, null); (await result.GetAllAsync()).Count().Should().Be(2); var ordered = (await result.GetAllAsync()).OrderBy(p => p.Id); ordered.First().Id.Should().Be("fake1.txt"); @@ -286,18 +258,18 @@ public async Task Should_find_filename_conflicts() SetupMockCase(reader, parserManager, modParser); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var definitions = new List() + var definitions = new List { - new Definition() + new Definition { File = "events\\1.txt", Code = "a", Id = "a", - Type= "events", + Type = "events", ModName = "test1", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\1.txt", Code = "b", @@ -309,7 +281,7 @@ public async Task Should_find_filename_conflicts() }; var indexed = new IndexedDefinitions(); await indexed.InitMapAsync(definitions); - var result = await service.FindConflictsAsync(indexed, new List(), IronyModManager.Models.Common.PatchStateMode.Default); + var result = await service.FindConflictsAsync(indexed, new List(), PatchStateMode.Default, null); (await result.Conflicts.GetAllAsync()).Count().Should().Be(2); (await result.Conflicts.GetAllFileKeysAsync()).Count().Should().Be(1); (await result.Conflicts.GetAllAsync()).All(p => p.ModName == "test1" || p.ModName == "test2").Should().BeTrue(); @@ -335,18 +307,18 @@ public async Task Should_find_orphan_filename_conflicts() SetupMockCase(reader, parserManager, modParser); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var definitions = new List() + var definitions = new List { - new Definition() + new Definition { File = "events\\1.txt", Code = "a", Id = "a", - Type= "events", + Type = "events", ModName = "test1", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\1.txt", Code = "b", @@ -355,7 +327,7 @@ public async Task Should_find_orphan_filename_conflicts() ModName = "test2", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\1.txt", Code = "b", @@ -367,7 +339,7 @@ public async Task Should_find_orphan_filename_conflicts() }; var indexed = new IndexedDefinitions(); await indexed.InitMapAsync(definitions); - var result = await service.FindConflictsAsync(indexed, new List() { "test2", "test1" }, IronyModManager.Models.Common.PatchStateMode.Default); + var result = await service.FindConflictsAsync(indexed, new List { "test2", "test1" }, PatchStateMode.Default, null); (await result.Conflicts.GetAllAsync()).Count().Should().Be(4); (await result.Conflicts.GetAllFileKeysAsync()).Count().Should().Be(1); (await result.Conflicts.GetAllAsync()).All(p => p.ModName == "test1" || p.ModName == "test2").Should().BeTrue(); @@ -375,7 +347,6 @@ public async Task Should_find_orphan_filename_conflicts() } - /// /// Defines the test method Should_ignore_orphan_filename_conflicts. /// @@ -396,18 +367,18 @@ public async Task Should_not_ignore_orphan_localisation_filename_conflicts() SetupMockCase(reader, parserManager, modParser); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var definitions = new List() + var definitions = new List { - new Definition() + new Definition { File = "localisation\\1.yml", Code = "a", Id = "a", - Type= "events", + Type = "events", ModName = "test1", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "localisation\\1.yml", Code = "b", @@ -416,7 +387,7 @@ public async Task Should_not_ignore_orphan_localisation_filename_conflicts() ModName = "test2", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "localisation\\1.yml", Code = "b", @@ -428,7 +399,7 @@ public async Task Should_not_ignore_orphan_localisation_filename_conflicts() }; var indexed = new IndexedDefinitions(); await indexed.InitMapAsync(definitions); - var result = await service.FindConflictsAsync(indexed, new List() { "test2", "test1" }, IronyModManager.Models.Common.PatchStateMode.Default); + var result = await service.FindConflictsAsync(indexed, new List { "test2", "test1" }, PatchStateMode.Default, null); (await result.Conflicts.GetAllAsync()).Count().Should().Be(4); (await result.Conflicts.GetAllFileKeysAsync()).Count().Should().Be(1); (await result.Conflicts.GetAllAsync()).All(p => p.ModName == "test1" || p.ModName == "test2").Should().BeTrue(); @@ -455,18 +426,18 @@ public async Task Should_find_definition_conflicts() SetupMockCase(reader, parserManager, modParser); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var definitions = new List() + var definitions = new List { - new Definition() + new Definition { File = "events\\1.txt", Code = "a", Id = "a", - Type= "events", + Type = "events", ModName = "test1", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\2.txt", Code = "b", @@ -478,7 +449,7 @@ public async Task Should_find_definition_conflicts() }; var indexed = new IndexedDefinitions(); await indexed.InitMapAsync(definitions); - var result = await service.FindConflictsAsync(indexed, new List(), IronyModManager.Models.Common.PatchStateMode.Default); + var result = await service.FindConflictsAsync(indexed, new List(), PatchStateMode.Default, null); (await result.Conflicts.GetAllAsync()).Count().Should().Be(2); (await result.Conflicts.GetAllFileKeysAsync()).Count().Should().Be(2); (await result.Conflicts.GetAllAsync()).All(p => p.ModName == "test1" || p.ModName == "test2").Should().BeTrue(); @@ -504,18 +475,18 @@ public async Task Should_not_find_override_conflicts() SetupMockCase(reader, parserManager, modParser); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var definitions = new List() + var definitions = new List { - new Definition() + new Definition { File = "events\\1.txt", Code = "a", Id = "a", - Type= "events", + Type = "events", ModName = "test1", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\1.txt", Code = "b", @@ -524,7 +495,7 @@ public async Task Should_not_find_override_conflicts() ModName = "test2", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\1.txt", Code = "b", @@ -532,12 +503,12 @@ public async Task Should_not_find_override_conflicts() Id = "a", ModName = "test3", ValueType = ValueType.Object, - Dependencies = new List() { "test1", "test2" } + Dependencies = new List { "test1", "test2" } } }; var indexed = new IndexedDefinitions(); await indexed.InitMapAsync(definitions); - var result = await service.FindConflictsAsync(indexed, new List(), IronyModManager.Models.Common.PatchStateMode.Default); + var result = await service.FindConflictsAsync(indexed, new List(), PatchStateMode.Default, null); (await result.Conflicts.GetAllAsync()).Count().Should().Be(0); (await result.Conflicts.GetAllFileKeysAsync()).Count().Should().Be(0); } @@ -562,18 +533,18 @@ public async Task Should_not_find_dependency_conflicts() SetupMockCase(reader, parserManager, modParser); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var definitions = new List() + var definitions = new List { - new Definition() + new Definition { File = "events\\1.txt", Code = "a", Id = "a", - Type= "events", + Type = "events", ModName = "test1", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\1.txt", Code = "b", @@ -582,7 +553,7 @@ public async Task Should_not_find_dependency_conflicts() ModName = "test2", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\1.txt", Code = "b", @@ -590,12 +561,12 @@ public async Task Should_not_find_dependency_conflicts() Id = "a", ModName = "test3", ValueType = ValueType.Object, - Dependencies = new List() { "test1" } + Dependencies = new List { "test1" } } }; var indexed = new IndexedDefinitions(); await indexed.InitMapAsync(definitions); - var result = await service.FindConflictsAsync(indexed, new List(), IronyModManager.Models.Common.PatchStateMode.Default); + var result = await service.FindConflictsAsync(indexed, new List(), PatchStateMode.Default, null); (await result.Conflicts.GetAllAsync()).Count().Should().Be(0); (await result.Conflicts.GetAllFileKeysAsync()).Count().Should().Be(0); } @@ -620,18 +591,18 @@ public async Task Should_find_dependency_conflicts() SetupMockCase(reader, parserManager, modParser); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var definitions = new List() + var definitions = new List { - new Definition() + new Definition { File = "events\\1.txt", Code = "a", Id = "a", - Type= "events", + Type = "events", ModName = "test1", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\1.txt", Code = "b", @@ -640,7 +611,7 @@ public async Task Should_find_dependency_conflicts() ModName = "test2", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\1.txt", Code = "b", @@ -648,12 +619,12 @@ public async Task Should_find_dependency_conflicts() Id = "a", ModName = "test3", ValueType = ValueType.Object, - Dependencies = new List() { "test2" } + Dependencies = new List { "test2" } } }; var indexed = new IndexedDefinitions(); await indexed.InitMapAsync(definitions); - var result = await service.FindConflictsAsync(indexed, new List(), IronyModManager.Models.Common.PatchStateMode.Default); + var result = await service.FindConflictsAsync(indexed, new List(), PatchStateMode.Default, null); (await result.Conflicts.GetAllAsync()).Count().Should().Be(2); (await result.Conflicts.GetAllFileKeysAsync()).Count().Should().Be(1); (await result.Conflicts.GetAllAsync()).All(p => p.ModName == "test1" || p.ModName == "test3").Should().BeTrue(); @@ -679,18 +650,18 @@ public async Task Should_find_multiple_dependency_conflicts() SetupMockCase(reader, parserManager, modParser); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var definitions = new List() + var definitions = new List { - new Definition() + new Definition { File = "events\\1.txt", Code = "a", Id = "a", - Type= "events", + Type = "events", ModName = "test1", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\1.txt", Code = "b", @@ -699,7 +670,7 @@ public async Task Should_find_multiple_dependency_conflicts() ModName = "test2", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\1.txt", Code = "b", @@ -707,9 +678,9 @@ public async Task Should_find_multiple_dependency_conflicts() Id = "a", ModName = "test3", ValueType = ValueType.Object, - Dependencies = new List() { "test1", "test2" } + Dependencies = new List { "test1", "test2" } }, - new Definition() + new Definition { File = "events\\1.txt", Code = "f", @@ -721,7 +692,7 @@ public async Task Should_find_multiple_dependency_conflicts() }; var indexed = new IndexedDefinitions(); await indexed.InitMapAsync(definitions); - var result = await service.FindConflictsAsync(indexed, new List(), IronyModManager.Models.Common.PatchStateMode.Default); + var result = await service.FindConflictsAsync(indexed, new List(), PatchStateMode.Default, null); (await result.Conflicts.GetAllAsync()).Count().Should().Be(2); (await result.Conflicts.GetAllFileKeysAsync()).Count().Should().Be(1); (await result.Conflicts.GetAllAsync()).All(p => p.ModName == "test3" || p.ModName == "test4").Should().BeTrue(); @@ -747,27 +718,27 @@ public async Task Should_not_include_variable_conflicts() SetupMockCase(reader, parserManager, modParser); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var definitions = new List() + var definitions = new List { - new Definition() + new Definition { File = "events\\1.txt", Code = "a", Id = "a1", - Type= "events", + Type = "events", ModName = "test1", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\1.txt", Code = "a", Id = "a2", - Type= "events", + Type = "events", ModName = "test1", ValueType = ValueType.Variable }, - new Definition() + new Definition { File = "events\\2.txt", Code = "a", @@ -776,19 +747,19 @@ public async Task Should_not_include_variable_conflicts() ModName = "test2", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\2.txt", Code = "b", Id = "a2", - Type= "events", + Type = "events", ModName = "test2", ValueType = ValueType.Variable - }, + } }; var indexed = new IndexedDefinitions(); await indexed.InitMapAsync(definitions); - var result = await service.FindConflictsAsync(indexed, new List(), IronyModManager.Models.Common.PatchStateMode.Default); + var result = await service.FindConflictsAsync(indexed, new List(), PatchStateMode.Default, null); (await result.Conflicts.GetAllAsync()).Count().Should().Be(0); (await result.Conflicts.GetAllFileKeysAsync()).Count().Should().Be(0); } @@ -813,18 +784,18 @@ public async Task Should_return_all_conflicts() SetupMockCase(reader, parserManager, modParser); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var definitions = new List() + var definitions = new List { - new Definition() + new Definition { File = "events\\1.txt", Code = "a", Id = "a", - Type= "events", + Type = "events", ModName = "test1", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\1.txt", Code = "b", @@ -836,7 +807,7 @@ public async Task Should_return_all_conflicts() }; var indexed = new IndexedDefinitions(); await indexed.InitMapAsync(definitions); - var result = await service.FindConflictsAsync(indexed, new List(), IronyModManager.Models.Common.PatchStateMode.Default); + var result = await service.FindConflictsAsync(indexed, new List(), PatchStateMode.Default, null); (await result.Conflicts.GetAllAsync()).Count().Should().Be(2); } @@ -890,20 +861,13 @@ public async Task Should_not_apply_mod_patch_when_nothing_to_merge() var gameService = new Mock(); var mapper = new Mock(); var modPatchExporter = new Mock(); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { - Type = "Should_not_apply_mod_patch_when_nothing_to_merge", - UserDirectory = "C:\\Users\\Fake", - WorkshopDirectory = new List() { "C:\\fake" }, - CustomModDirectory = string.Empty + Type = "Should_not_apply_mod_patch_when_nothing_to_merge", UserDirectory = "C:\\Users\\Fake", WorkshopDirectory = new List { "C:\\fake" }, CustomModDirectory = string.Empty }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; + return new Mod { FileName = o.FileName, Name = o.Name }; }); SetupMockCase(reader, parserManager, modParser); @@ -911,13 +875,8 @@ public async Task Should_not_apply_mod_patch_when_nothing_to_merge() var indexed = new IndexedDefinitions(); await indexed.InitMapAsync(new List()); - var c = new ConflictResult() - { - AllConflicts = indexed, - Conflicts = indexed, - ResolvedConflicts = indexed - }; - var result = await service.ApplyModPatchAsync(c, new Definition() { ModName = "test", ValueType = ValueType.Object }, "colname"); + var c = new ConflictResult { AllConflicts = indexed, Conflicts = indexed, ResolvedConflicts = indexed }; + var result = await service.ApplyModPatchAsync(c, new Definition { ModName = "test", ValueType = ValueType.Object }, "colname"); result.Should().BeFalse(); } @@ -938,31 +897,15 @@ public async Task Should_return_true_when_applying_patches() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { - Type = "Should_return_true_when_applying_patches", - UserDirectory = "C:\\Users\\Fake", - WorkshopDirectory = new List() { "C:\\fake" }, - CustomModDirectory = string.Empty + Type = "Should_return_true_when_applying_patches", UserDirectory = "C:\\Users\\Fake", WorkshopDirectory = new List { "C:\\fake" }, CustomModDirectory = string.Empty }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; + return new Mod { FileName = o.FileName, Name = o.Name }; }); - var collections = new List() - { - new ModCollection() - { - IsSelected = true, - Mods = new List() { "mod/fake1.txt", "mod/fake2.txt"}, - Name = "test", - Game = "Should_return_true_when_applying_patches" - } - }; + var collections = new List { new ModCollection { IsSelected = true, Mods = new List { "mod/fake1.txt", "mod/fake2.txt" }, Name = "test", Game = "Should_return_true_when_applying_patches" } }; storageProvider.Setup(s => s.GetModCollections()).Returns(() => { return collections; @@ -977,6 +920,7 @@ public async Task Should_return_true_when_applying_patches() { return true; } + return false; }); modWriter.Setup(p => p.ModDirectoryExists(It.IsAny())).Returns((ModWriterParameters p) => @@ -985,18 +929,18 @@ public async Task Should_return_true_when_applying_patches() }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var definitions = new List() + var definitions = new List { - new Definition() + new Definition { File = "events\\1.txt", Code = "a", Id = "a", - Type= "events", + Type = "events", ModName = "test1", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\2.txt", Code = "b", @@ -1004,7 +948,7 @@ public async Task Should_return_true_when_applying_patches() Id = "a", ModName = "test2", ValueType = ValueType.Object - }, + } }; var all = new IndexedDefinitions(); await all.InitMapAsync(definitions); @@ -1015,14 +959,8 @@ public async Task Should_return_true_when_applying_patches() var resolved = new IndexedDefinitions(); await resolved.InitMapAsync(new List()); - var c = new ConflictResult() - { - AllConflicts = all, - Conflicts = all, - ResolvedConflicts = resolved, - OverwrittenConflicts = overwritten - }; - var result = await service.ApplyModPatchAsync(c, new Definition() { ModName = "1" }, "colname"); + var c = new ConflictResult { AllConflicts = all, Conflicts = all, ResolvedConflicts = resolved, OverwrittenConflicts = overwritten }; + var result = await service.ApplyModPatchAsync(c, new Definition { ModName = "1" }, "colname"); result.Should().BeTrue(); } @@ -1042,23 +980,16 @@ public async Task Should_not_create_patch_definition() var gameService = new Mock(); var mapper = new Mock(); var modPatchExporter = new Mock(); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "Should_not_create_patch_definition", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Should_not_create_patch_definition", UserDirectory = "C:\\Users\\Fake" }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IDefinition o) => { - return new Definition() - { - File = o.File - }; + return new Definition { File = o.File }; }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); var result = await service.CreatePatchDefinitionAsync(null, "fake"); result.Should().BeNull(); - result = await service.CreatePatchDefinitionAsync(new Definition() { File = "1" }, null); + result = await service.CreatePatchDefinitionAsync(new Definition { File = "1" }, null); result.Should().BeNull(); } @@ -1081,14 +1012,11 @@ public async Task Should_not_create_patch_definition_when_no_selected_game() gameService.Setup(p => p.GetSelected()).Returns((IGame)null); mapper.Setup(s => s.Map(It.IsAny())).Returns((IDefinition o) => { - return new Definition() - { - File = o.File - }; + return new Definition { File = o.File }; }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var result = await service.CreatePatchDefinitionAsync(new Definition() { File = "1" }, "fake"); + var result = await service.CreatePatchDefinitionAsync(new Definition { File = "1" }, "fake"); result.Should().BeNull(); } @@ -1108,20 +1036,13 @@ public async Task Should_create_patch_definition() var gameService = new Mock(); var mapper = new Mock(); var modPatchExporter = new Mock(); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "Should_create_patch_definition", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Should_create_patch_definition", UserDirectory = "C:\\Users\\Fake" }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IDefinition o) => { - return new Definition() - { - File = o.File - }; + return new Definition { File = o.File }; }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var result = await service.CreatePatchDefinitionAsync(new Definition() { File = "1" }, "fake"); + var result = await service.CreatePatchDefinitionAsync(new Definition { File = "1" }, "fake"); result.Should().NotBeNull(); result.ModName.Should().Be("IronyModManager_fake"); } @@ -1142,32 +1063,21 @@ public async Task Should_create_patch_definition_and_overwrite_code_from_history var gameService = new Mock(); var mapper = new Mock(); var modPatchExporter = new Mock(); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "Should_create_patch_definition_and_overwrite_code_from_history", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Should_create_patch_definition_and_overwrite_code_from_history", UserDirectory = "C:\\Users\\Fake" }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IDefinition o) => { - return new Definition() - { - File = o.File, - Id = o.Id, - Type = o.Type - }; + return new Definition { File = o.File, Id = o.Id, Type = o.Type }; }); modPatchExporter.Setup(p => p.GetPatchStateAsync(It.IsAny(), It.IsAny())).ReturnsAsync((ModPatchExporterParameters p, bool load) => { - var res = new PatchState() + var res = new PatchState { - Conflicts = new List(), - ResolvedConflicts = new List(), - ConflictHistory = new List() { new Definition() { File = "1", Id = "test", Type = "events", Code = "ab" } } + Conflicts = new List(), ResolvedConflicts = new List(), ConflictHistory = new List { new Definition { File = "1", Id = "test", Type = "events", Code = "ab" } } }; return res; }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var result = await service.CreatePatchDefinitionAsync(new Definition() { File = "1", Id = "test", Type = "events", Code = "a" }, "fake"); + var result = await service.CreatePatchDefinitionAsync(new Definition { File = "1", Id = "test", Type = "events", Code = "a" }, "fake"); result.Should().NotBeNull(); result.ModName.Should().Be("IronyModManager_fake"); result.Code.Should().Be("ab"); @@ -1193,12 +1103,7 @@ public async Task Should_not_initialize_patch_state_when_no_selected_game() var indexed = new IndexedDefinitions(); await indexed.InitMapAsync(new List()); - var c = new ConflictResult() - { - AllConflicts = indexed, - Conflicts = indexed, - ResolvedConflicts = indexed - }; + var c = new ConflictResult { AllConflicts = indexed, Conflicts = indexed, ResolvedConflicts = indexed }; var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); var result = await service.InitializePatchStateAsync(c, "fake"); result.Should().BeNull(); @@ -1224,12 +1129,7 @@ public async Task Should_not_initialize_patch_state() var indexed = new IndexedDefinitions(); await indexed.InitMapAsync(new List()); - var c = new ConflictResult() - { - AllConflicts = indexed, - Conflicts = indexed, - ResolvedConflicts = indexed - }; + var c = new ConflictResult { AllConflicts = indexed, Conflicts = indexed, ResolvedConflicts = indexed }; var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); var result = await service.InitializePatchStateAsync(c, null); result.Should().BeNull(); @@ -1258,33 +1158,23 @@ public async Task Should_initialize_patch_state() { return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "dummy"); }); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "Should_sync_patch_state", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Should_sync_patch_state", UserDirectory = "C:\\Users\\Fake" }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IConflictResult o) => { - return new ConflictResult() - { - AllConflicts = o.Conflicts, - Conflicts = o.Conflicts, - ResolvedConflicts = o.ResolvedConflicts, - OverwrittenConflicts = o.OverwrittenConflicts - }; + return new ConflictResult { AllConflicts = o.Conflicts, Conflicts = o.Conflicts, ResolvedConflicts = o.ResolvedConflicts, OverwrittenConflicts = o.OverwrittenConflicts }; }); - var definitions = new List() + var definitions = new List { - new Definition() + new Definition { File = "events\\1.txt", Code = "a", Id = "a", - Type= "events", + Type = "events", ModName = "test1", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\2.txt", Code = "b", @@ -1292,16 +1182,11 @@ public async Task Should_initialize_patch_state() Id = "a", ModName = "test2", ValueType = ValueType.Object - }, + } }; modPatchExporter.Setup(p => p.GetPatchStateAsync(It.IsAny(), It.IsAny())).ReturnsAsync((ModPatchExporterParameters p, bool load) => { - var res = new PatchState() - { - Conflicts = definitions, - ResolvedConflicts = definitions, - OverwrittenConflicts = new List() - }; + var res = new PatchState { Conflicts = definitions, ResolvedConflicts = definitions, OverwrittenConflicts = new List() }; return res; }); modWriter.Setup(p => p.PurgeModDirectoryAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(true)); @@ -1318,12 +1203,7 @@ public async Task Should_initialize_patch_state() var resolved = new IndexedDefinitions(); await resolved.InitMapAsync(new List()); - var c = new ConflictResult() - { - AllConflicts = all, - Conflicts = conflicts, - OverwrittenConflicts = overwritten - }; + var c = new ConflictResult { AllConflicts = all, Conflicts = conflicts, OverwrittenConflicts = overwritten }; var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); var result = await service.InitializePatchStateAsync(c, "fake"); (await result.Conflicts.GetAllAsync()).Count().Should().Be(2); @@ -1351,33 +1231,23 @@ public async Task Should_initialize_patch_state_and_remove_different() { return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "dummy"); }); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "Should_sync_patch_state_and_remove_different", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Should_sync_patch_state_and_remove_different", UserDirectory = "C:\\Users\\Fake" }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IConflictResult o) => { - return new ConflictResult() - { - AllConflicts = o.Conflicts, - Conflicts = o.Conflicts, - ResolvedConflicts = o.ResolvedConflicts, - OverwrittenConflicts = o.OverwrittenConflicts - }; + return new ConflictResult { AllConflicts = o.Conflicts, Conflicts = o.Conflicts, ResolvedConflicts = o.ResolvedConflicts, OverwrittenConflicts = o.OverwrittenConflicts }; }); - var definitions = new List() + var definitions = new List { - new Definition() + new Definition { File = "events\\1.txt", Code = "a", Id = "a", - Type= "events", + Type = "events", ModName = "test1", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\2.txt", Code = "b", @@ -1385,20 +1255,20 @@ public async Task Should_initialize_patch_state_and_remove_different() Id = "a", ModName = "test2", ValueType = ValueType.Object - }, + } }; - var definitions2 = new List() + var definitions2 = new List { - new Definition() + new Definition { File = "events\\1.txt", Code = "ab", Id = "a", - Type= "events", + Type = "events", ModName = "test1", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\2.txt", Code = "b", @@ -1410,12 +1280,7 @@ public async Task Should_initialize_patch_state_and_remove_different() }; modPatchExporter.Setup(p => p.GetPatchStateAsync(It.IsAny(), It.IsAny())).ReturnsAsync((ModPatchExporterParameters p, bool load) => { - var res = new PatchState() - { - Conflicts = definitions2, - ResolvedConflicts = definitions2, - OverwrittenConflicts = new List() - }; + var res = new PatchState { Conflicts = definitions2, ResolvedConflicts = definitions2, OverwrittenConflicts = new List() }; return res; }); modWriter.Setup(p => p.PurgeModDirectoryAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(true)); @@ -1432,12 +1297,7 @@ public async Task Should_initialize_patch_state_and_remove_different() var resolved = new IndexedDefinitions(); await resolved.InitMapAsync(new List()); - var c = new ConflictResult() - { - AllConflicts = all, - Conflicts = conflicts, - OverwrittenConflicts = overwritten - }; + var c = new ConflictResult { AllConflicts = all, Conflicts = conflicts, OverwrittenConflicts = overwritten }; var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); var result = await service.InitializePatchStateAsync(c, "fake"); (await result.Conflicts.GetAllAsync()).Count().Should().Be(2); @@ -1460,10 +1320,7 @@ public void Should_not_be_a_patch_mod() var mapper = new Mock(); var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var result = service.IsPatchMod(new Mod() - { - Name = "test" - }); + var result = service.IsPatchMod(new Mod { Name = "test" }); result.Should().BeFalse(); result = service.IsPatchMod(default(Mod)); result.Should().BeFalse(); @@ -1484,10 +1341,7 @@ public void Should_be_a_patch_mod() var mapper = new Mock(); var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var result = service.IsPatchMod(new Mod() - { - Name = "IronyModManager_fake_collection" - }); + var result = service.IsPatchMod(new Mod { Name = "IronyModManager_fake_collection" }); result.Should().BeTrue(); } @@ -1533,12 +1387,7 @@ public async Task Should_clean_collection_patch() var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "Should_clean_collection_patch", - UserDirectory = "C:\\Users\\Fake", - WorkshopDirectory = new List() { "C:\\workshop" } - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Should_clean_collection_patch", UserDirectory = "C:\\Users\\Fake", WorkshopDirectory = new List { "C:\\workshop" } }); modWriter.Setup(p => p.DeleteDescriptorAsync(It.IsAny())).Returns(Task.FromResult(true)); modWriter.Setup(p => p.PurgeModDirectoryAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(true)); @@ -1596,20 +1445,13 @@ public async Task Should_not_ignore_mod_patch_when_nothing_to_merge() var gameService = new Mock(); var mapper = new Mock(); var modPatchExporter = new Mock(); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { - Type = "Should_not_ignore_mod_patch_when_nothing_to_merge", - UserDirectory = "C:\\Users\\Fake", - WorkshopDirectory = new List() { "C:\\fake" }, - CustomModDirectory = string.Empty + Type = "Should_not_ignore_mod_patch_when_nothing_to_merge", UserDirectory = "C:\\Users\\Fake", WorkshopDirectory = new List { "C:\\fake" }, CustomModDirectory = string.Empty }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; + return new Mod { FileName = o.FileName, Name = o.Name }; }); SetupMockCase(reader, parserManager, modParser); @@ -1617,14 +1459,8 @@ public async Task Should_not_ignore_mod_patch_when_nothing_to_merge() var indexed = new IndexedDefinitions(); await indexed.InitMapAsync(new List()); - var c = new ConflictResult() - { - AllConflicts = indexed, - Conflicts = indexed, - ResolvedConflicts = indexed, - IgnoredConflicts = indexed - }; - var result = await service.IgnoreModPatchAsync(c, new Definition() { ModName = "test", ValueType = ValueType.Object }, "colname"); + var c = new ConflictResult { AllConflicts = indexed, Conflicts = indexed, ResolvedConflicts = indexed, IgnoredConflicts = indexed }; + var result = await service.IgnoreModPatchAsync(c, new Definition { ModName = "test", ValueType = ValueType.Object }, "colname"); result.Should().BeFalse(); } @@ -1645,20 +1481,13 @@ public async Task Should_return_true_when_ignoring_patches() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { - Type = "Should_return_true_when_ignoring_patches", - UserDirectory = "C:\\Users\\Fake", - WorkshopDirectory = new List() { "C:\\fake" }, - CustomModDirectory = string.Empty + Type = "Should_return_true_when_ignoring_patches", UserDirectory = "C:\\Users\\Fake", WorkshopDirectory = new List { "C:\\fake" }, CustomModDirectory = string.Empty }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; + return new Mod { FileName = o.FileName, Name = o.Name }; }); modWriter.Setup(p => p.CreateModDirectoryAsync(It.IsAny())).Returns(Task.FromResult(true)); modWriter.Setup(p => p.WriteDescriptorAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(true)); @@ -1670,6 +1499,7 @@ public async Task Should_return_true_when_ignoring_patches() { return true; } + return false; }); modWriter.Setup(p => p.ModDirectoryExists(It.IsAny())).Returns((ModWriterParameters p) => @@ -1678,18 +1508,18 @@ public async Task Should_return_true_when_ignoring_patches() }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var definitions = new List() + var definitions = new List { - new Definition() + new Definition { File = "events\\1.txt", Code = "a", Id = "a", - Type= "events", + Type = "events", ModName = "test1", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\2.txt", Code = "b", @@ -1697,7 +1527,7 @@ public async Task Should_return_true_when_ignoring_patches() Id = "a", ModName = "test2", ValueType = ValueType.Object - }, + } }; var all = new IndexedDefinitions(); await all.InitMapAsync(definitions); @@ -1709,14 +1539,8 @@ public async Task Should_return_true_when_ignoring_patches() await ignored.InitMapAsync(new List()); - var c = new ConflictResult() - { - AllConflicts = all, - Conflicts = all, - ResolvedConflicts = resolved, - IgnoredConflicts = ignored - }; - var result = await service.IgnoreModPatchAsync(c, new Definition() { ModName = "1" }, "colname"); + var c = new ConflictResult { AllConflicts = all, Conflicts = all, ResolvedConflicts = resolved, IgnoredConflicts = ignored }; + var result = await service.IgnoreModPatchAsync(c, new Definition { ModName = "1" }, "colname"); result.Should().BeTrue(); } @@ -1742,7 +1566,7 @@ public void EvalDefinitionPriority_should_return_null() infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(true); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); var result = service.EvalDefinitionPriority(null); result.Definition.Should().BeNull(); @@ -1765,19 +1589,15 @@ public void EvalDefinitionPriority_should_return_first_object() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_first_object", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_first_object", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(true); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); var def = new Definition(); - var result = service.EvalDefinitionPriority(new List() { def }); + var result = service.EvalDefinitionPriority(new List { def }); result.Definition.Should().Be(def); result.PriorityType.Should().Be(DefinitionPriorityType.None); } @@ -1799,20 +1619,16 @@ public void EvalDefinitionPriority_should_return_first_object_when_no_info_provi var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_first_object_when_no_info_provider", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_first_object_when_no_info_provider", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(false); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(false); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); var def = new Definition(); var def2 = new Definition(); - var result = service.EvalDefinitionPriority(new List() { def, def2 }); + var result = service.EvalDefinitionPriority(new List { def, def2 }); result.Definition.Should().Be(def); result.PriorityType.Should().Be(DefinitionPriorityType.NoProvider); } @@ -1834,19 +1650,15 @@ public void EvalDefinitionPriority_should_return_first_game_object() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_first_game_object", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_first_game_object", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(true); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { IsFromGame = true }; - var result = service.EvalDefinitionPriority(new List() { def }); + var def = new Definition { IsFromGame = true }; + var result = service.EvalDefinitionPriority(new List { def }); result.Definition.Should().Be(def); result.PriorityType.Should().Be(DefinitionPriorityType.None); } @@ -1868,20 +1680,16 @@ public void EvalDefinitionPriority_should_return_first_only_valid_object() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_first_only_valid_object", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_first_only_valid_object", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(true); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { }; - var def2 = new Definition() { ExistsInLastFile = false }; - var result = service.EvalDefinitionPriority(new List() { def, def2 }); + var def = new Definition(); + var def2 = new Definition { ExistsInLastFile = false }; + var result = service.EvalDefinitionPriority(new List { def, def2 }); result.Definition.Should().Be(def); result.PriorityType.Should().Be(DefinitionPriorityType.ModOrder); } @@ -1903,20 +1711,16 @@ public void EvalDefinitionPriority_should_return_last_object() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_last_object", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_last_object", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(true); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = "test.txt", ModName = "1" }; - var def2 = new Definition() { File = "test.txt", ModName = "2" }; - var result = service.EvalDefinitionPriority(new List() { def, def2 }); + var def = new Definition { File = "test.txt", ModName = "1" }; + var def2 = new Definition { File = "test.txt", ModName = "2" }; + var result = service.EvalDefinitionPriority(new List { def, def2 }); result.Definition.Should().Be(def2); result.PriorityType.Should().Be(DefinitionPriorityType.ModOrder); } @@ -1938,20 +1742,16 @@ public void EvalDefinitionPriority_should_return_last_object_as_cutom_patch() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_last_object_as_cutom_patch", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_last_object_as_cutom_patch", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(true); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = "test.txt", ModName = "1", IsCustomPatch = true }; - var def2 = new Definition() { File = "test.txt", ModName = "2" }; - var result = service.EvalDefinitionPriority(new List() { def, def2 }); + var def = new Definition { File = "test.txt", ModName = "1", IsCustomPatch = true }; + var def2 = new Definition { File = "test.txt", ModName = "2" }; + var result = service.EvalDefinitionPriority(new List { def, def2 }); result.Definition.Should().Be(def); result.PriorityType.Should().Be(DefinitionPriorityType.ModOrder); } @@ -1973,21 +1773,17 @@ public void EvalDefinitionPriority_should_return_last_non_game_object() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_last_non_game_object", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_last_non_game_object", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(true); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = "test.txt", ModName = "1" }; - var def2 = new Definition() { File = "test.txt", ModName = "2" }; - var def3 = new Definition() { File = "test.txt", ModName = "Game", IsFromGame = true }; - var result = service.EvalDefinitionPriority(new List() { def3, def, def2 }); + var def = new Definition { File = "test.txt", ModName = "1" }; + var def2 = new Definition { File = "test.txt", ModName = "2" }; + var def3 = new Definition { File = "test.txt", ModName = "Game", IsFromGame = true }; + var result = service.EvalDefinitionPriority(new List { def3, def, def2 }); result.Definition.Should().Be(def2); result.PriorityType.Should().Be(DefinitionPriorityType.ModOrder); } @@ -2009,20 +1805,16 @@ public void EvalDefinitionPriority_should_return_first_object_due_to_FIOS() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_first_object_due_to_FIOS", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_first_object_due_to_FIOS", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(true); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = "test1.txt", ModName = "1" }; - var def2 = new Definition() { File = "test2.txt", ModName = "2" }; - var result = service.EvalDefinitionPriority(new List() { def, def2 }); + var def = new Definition { File = "test1.txt", ModName = "1" }; + var def2 = new Definition { File = "test2.txt", ModName = "2" }; + var result = service.EvalDefinitionPriority(new List { def, def2 }); result.Definition.Should().Be(def); result.PriorityType.Should().Be(DefinitionPriorityType.FIOS); } @@ -2044,21 +1836,17 @@ public void EvalDefinitionPriority_should_return_first_object_due_to_FIOS_and_ig var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_first_object_due_to_FIOS_and_ignore_game_object", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_first_object_due_to_FIOS_and_ignore_game_object", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(true); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = "test1.txt", ModName = "1" }; - var def2 = new Definition() { File = "test2.txt", ModName = "2" }; - var def3 = new Definition() { File = "test1.txt", ModName = "Game", IsFromGame = true }; - var result = service.EvalDefinitionPriority(new List() { def3, def, def2 }); + var def = new Definition { File = "test1.txt", ModName = "1" }; + var def2 = new Definition { File = "test2.txt", ModName = "2" }; + var def3 = new Definition { File = "test1.txt", ModName = "Game", IsFromGame = true }; + var result = service.EvalDefinitionPriority(new List { def3, def, def2 }); result.Definition.Should().Be(def); result.PriorityType.Should().Be(DefinitionPriorityType.FIOS); } @@ -2080,20 +1868,16 @@ public void EvalDefinitionPriority_should_return_object_due_to_Override() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_object_due_to_Override", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_object_due_to_Override", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(true); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = "test1.txt", ModName = "1", Dependencies = new List() { "2" } }; - var def2 = new Definition() { File = "test1.txt", ModName = "2" }; - var result = service.EvalDefinitionPriority(new List() { def, def2 }); + var def = new Definition { File = "test1.txt", ModName = "1", Dependencies = new List { "2" } }; + var def2 = new Definition { File = "test1.txt", ModName = "2" }; + var result = service.EvalDefinitionPriority(new List { def, def2 }); result.Definition.Should().Be(def); result.PriorityType.Should().Be(DefinitionPriorityType.ModOverride); } @@ -2115,20 +1899,16 @@ public void EvalDefinitionPriority_should_return_object_due_to_game_object_filte var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_object_due_to_game_object_filtering", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_object_due_to_game_object_filtering", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(true); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = "test1.txt", ModName = "1", IsFromGame = true }; - var def2 = new Definition() { File = "test1.txt", ModName = "2" }; - var result = service.EvalDefinitionPriority(new List() { def, def2 }); + var def = new Definition { File = "test1.txt", ModName = "1", IsFromGame = true }; + var def2 = new Definition { File = "test1.txt", ModName = "2" }; + var result = service.EvalDefinitionPriority(new List { def, def2 }); result.Definition.Should().Be(def2); result.PriorityType.Should().Be(DefinitionPriorityType.ModOrder); } @@ -2150,21 +1930,17 @@ public void EvalDefinitionPriority_should_return_object_due_to_Override_and_igno var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_object_due_to_Override_and_ignore_game_object", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_object_due_to_Override_and_ignore_game_object", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(true); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = "test1.txt", ModName = "1", Dependencies = new List() { "2" } }; - var def2 = new Definition() { File = "test1.txt", ModName = "2" }; - var def3 = new Definition() { File = "test1.txt", ModName = "Game", IsFromGame = true }; - var result = service.EvalDefinitionPriority(new List() { def3, def, def2 }); + var def = new Definition { File = "test1.txt", ModName = "1", Dependencies = new List { "2" } }; + var def2 = new Definition { File = "test1.txt", ModName = "2" }; + var def3 = new Definition { File = "test1.txt", ModName = "Game", IsFromGame = true }; + var result = service.EvalDefinitionPriority(new List { def3, def, def2 }); result.Definition.Should().Be(def); result.PriorityType.Should().Be(DefinitionPriorityType.ModOverride); } @@ -2186,20 +1962,16 @@ public void EvalDefinitionPriority_should_return_object_due_to_override_but_prio var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_object_due_to_override_but_priority_should_be_fios", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_object_due_to_override_but_priority_should_be_fios", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(true); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = "test1.txt", ModName = "1", Dependencies = new List() { "2" } }; - var def2 = new Definition() { File = "test2.txt", ModName = "2" }; - var result = service.EvalDefinitionPriority(new List() { def, def2 }); + var def = new Definition { File = "test1.txt", ModName = "1", Dependencies = new List { "2" } }; + var def2 = new Definition { File = "test2.txt", ModName = "2" }; + var result = service.EvalDefinitionPriority(new List { def, def2 }); result.Definition.Should().Be(def); result.PriorityType.Should().Be(DefinitionPriorityType.FIOS); } @@ -2221,21 +1993,17 @@ public void EvalDefinitionPriority_should_return_object_due_to_override_and_igno var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_object_due_to_override_and_ignore_non_game_object_and_priority_should_be_fios", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_object_due_to_override_and_ignore_non_game_object_and_priority_should_be_fios", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(true); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = "test1.txt", ModName = "1", Dependencies = new List() { "2" } }; - var def2 = new Definition() { File = "test2.txt", ModName = "2" }; - var def3 = new Definition() { File = "test2.txt", ModName = "Game", IsFromGame = true }; - var result = service.EvalDefinitionPriority(new List() { def3, def, def2 }); + var def = new Definition { File = "test1.txt", ModName = "1", Dependencies = new List { "2" } }; + var def2 = new Definition { File = "test2.txt", ModName = "2" }; + var def3 = new Definition { File = "test2.txt", ModName = "Game", IsFromGame = true }; + var result = service.EvalDefinitionPriority(new List { def3, def, def2 }); result.Definition.Should().Be(def); result.PriorityType.Should().Be(DefinitionPriorityType.FIOS); } @@ -2257,20 +2025,16 @@ public void EvalDefinitionPriority_should_return_first_object_due_to_LIOS() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_first_object_due_to_LIOS", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_first_object_due_to_LIOS", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(false); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = "test1.txt", ModName = "1" }; - var def2 = new Definition() { File = "test2.txt", ModName = "2" }; - var result = service.EvalDefinitionPriority(new List() { def, def2 }); + var def = new Definition { File = "test1.txt", ModName = "1" }; + var def2 = new Definition { File = "test2.txt", ModName = "2" }; + var result = service.EvalDefinitionPriority(new List { def, def2 }); result.Definition.Should().Be(def2); result.PriorityType.Should().Be(DefinitionPriorityType.LIOS); } @@ -2292,21 +2056,17 @@ public void EvalDefinitionPriority_should_return_first_object_due_to_LIOS_and_ig var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_first_object_due_to_LIOS_and_ignore_non_game_object", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_first_object_due_to_LIOS_and_ignore_non_game_object", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(false); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = "test1.txt", ModName = "1" }; - var def2 = new Definition() { File = "test2.txt", ModName = "2" }; - var def3 = new Definition() { File = "test2.txt", ModName = "Game", IsFromGame = true }; - var result = service.EvalDefinitionPriority(new List() { def3, def, def2 }); + var def = new Definition { File = "test1.txt", ModName = "1" }; + var def2 = new Definition { File = "test2.txt", ModName = "2" }; + var def3 = new Definition { File = "test2.txt", ModName = "Game", IsFromGame = true }; + var result = service.EvalDefinitionPriority(new List { def3, def, def2 }); result.Definition.Should().Be(def2); result.PriorityType.Should().Be(DefinitionPriorityType.LIOS); } @@ -2328,20 +2088,16 @@ public void EvalDefinitionPriority_should_return_localization_override() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_localization_override", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_localization_override", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(false); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = @"localisation\test.yml", ModName = "1" }; - var def2 = new Definition() { File = @"localisation\replace\test.yml", ModName = "2" }; - var result = service.EvalDefinitionPriority(new List() { def, def2 }); + var def = new Definition { File = @"localisation\test.yml", ModName = "1" }; + var def2 = new Definition { File = @"localisation\replace\test.yml", ModName = "2" }; + var result = service.EvalDefinitionPriority(new List { def, def2 }); result.Definition.Should().Be(def2); result.PriorityType.Should().Be(DefinitionPriorityType.None); } @@ -2363,20 +2119,16 @@ public void EvalDefinitionPriority_should_return_localization() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_localization", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_localization", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(false); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = @"localisation\test.yml", ModName = "1" }; - var def2 = new Definition() { File = @"localisation\test.yml", ModName = "2" }; - var result = service.EvalDefinitionPriority(new List() { def, def2 }); + var def = new Definition { File = @"localisation\test.yml", ModName = "1" }; + var def2 = new Definition { File = @"localisation\test.yml", ModName = "2" }; + var result = service.EvalDefinitionPriority(new List { def, def2 }); result.Definition.Should().Be(def2); result.PriorityType.Should().Be(DefinitionPriorityType.None); } @@ -2398,20 +2150,16 @@ public void EvalDefinitionPriority_should_return_localization_override_with_cust var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_localization_override_with_custom_priority", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_localization_override_with_custom_priority", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(false); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = @"localisation\test.yml", ModName = "1" }; - var def2 = new Definition() { File = @"localisation\replace\test.yml", ModName = "2", CustomPriorityOrder = 5 }; - var result = service.EvalDefinitionPriority(new List() { def, def2 }); + var def = new Definition { File = @"localisation\test.yml", ModName = "1" }; + var def2 = new Definition { File = @"localisation\replace\test.yml", ModName = "2", CustomPriorityOrder = 5 }; + var result = service.EvalDefinitionPriority(new List { def, def2 }); result.Definition.Should().Be(def2); result.PriorityType.Should().Be(DefinitionPriorityType.None); } @@ -2433,20 +2181,16 @@ public void EvalDefinitionPriority_should_return_localization_with_custom_priori var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_localization_with_custom_priority", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_localization_with_custom_priority", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(false); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = @"localisation\test.yml", ModName = "1" }; - var def2 = new Definition() { File = @"localisation\test.yml", ModName = "2", CustomPriorityOrder = 5 }; - var result = service.EvalDefinitionPriority(new List() { def, def2 }); + var def = new Definition { File = @"localisation\test.yml", ModName = "1" }; + var def2 = new Definition { File = @"localisation\test.yml", ModName = "2", CustomPriorityOrder = 5 }; + var result = service.EvalDefinitionPriority(new List { def, def2 }); result.Definition.Should().Be(def2); result.PriorityType.Should().Be(DefinitionPriorityType.None); } @@ -2468,21 +2212,17 @@ public void EvalDefinitionPriority_should_return_localization_override_and_filen var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_localization_override_and_filename_priority", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_localization_override_and_filename_priority", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(false); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = @"localisation\test.yml", ModName = "1" }; - var def2 = new Definition() { File = @"localisation\replace\test.yml", ModName = "2" }; - var def3 = new Definition() { File = @"localisation\replace\test2.yml", ModName = "3" }; - var result = service.EvalDefinitionPriority(new List() { def, def2, def3 }); + var def = new Definition { File = @"localisation\test.yml", ModName = "1" }; + var def2 = new Definition { File = @"localisation\replace\test.yml", ModName = "2" }; + var def3 = new Definition { File = @"localisation\replace\test2.yml", ModName = "3" }; + var result = service.EvalDefinitionPriority(new List { def, def2, def3 }); result.Definition.Should().Be(def3); result.PriorityType.Should().Be(DefinitionPriorityType.None); } @@ -2504,21 +2244,17 @@ public void EvalDefinitionPriority_should_return_localization_and_filename_prior var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_localization_and_filename_priority", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_localization_and_filename_priority", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(false); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = @"localisation\test.yml", ModName = "1" }; - var def2 = new Definition() { File = @"localisation\test.yml", ModName = "2" }; - var def3 = new Definition() { File = @"localisation\test2.yml", ModName = "3" }; - var result = service.EvalDefinitionPriority(new List() { def, def2, def3 }); + var def = new Definition { File = @"localisation\test.yml", ModName = "1" }; + var def2 = new Definition { File = @"localisation\test.yml", ModName = "2" }; + var def3 = new Definition { File = @"localisation\test2.yml", ModName = "3" }; + var result = service.EvalDefinitionPriority(new List { def, def2, def3 }); result.Definition.Should().Be(def3); result.PriorityType.Should().Be(DefinitionPriorityType.None); } @@ -2540,21 +2276,17 @@ public void EvalDefinitionPriority_should_return_localization_override_with_mult var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_localization_override_with_multiple_custom_priority", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_localization_override_with_multiple_custom_priority", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(false); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = @"localisation\test.yml", ModName = "1" }; - var def2 = new Definition() { File = @"localisation\replace\test.yml", ModName = "2", CustomPriorityOrder = 1000 }; - var def3 = new Definition() { File = @"localisation\replace\test2.yml", ModName = "3", CustomPriorityOrder = 100 }; - var result = service.EvalDefinitionPriority(new List() { def, def2, def3 }); + var def = new Definition { File = @"localisation\test.yml", ModName = "1" }; + var def2 = new Definition { File = @"localisation\replace\test.yml", ModName = "2", CustomPriorityOrder = 1000 }; + var def3 = new Definition { File = @"localisation\replace\test2.yml", ModName = "3", CustomPriorityOrder = 100 }; + var result = service.EvalDefinitionPriority(new List { def, def2, def3 }); result.Definition.Should().Be(def2); result.PriorityType.Should().Be(DefinitionPriorityType.None); } @@ -2576,21 +2308,17 @@ public void EvalDefinitionPriority_should_return_localization_with_multiple_cust var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_localization_with_multiple_custom_priority", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_localization_with_multiple_custom_priority", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(false); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = @"localisation\test.yml", ModName = "1" }; - var def2 = new Definition() { File = @"localisation\test.yml", ModName = "2", CustomPriorityOrder = 1000 }; - var def3 = new Definition() { File = @"localisation\test2.yml", ModName = "3", CustomPriorityOrder = 100 }; - var result = service.EvalDefinitionPriority(new List() { def, def2, def3 }); + var def = new Definition { File = @"localisation\test.yml", ModName = "1" }; + var def2 = new Definition { File = @"localisation\test.yml", ModName = "2", CustomPriorityOrder = 1000 }; + var def3 = new Definition { File = @"localisation\test2.yml", ModName = "3", CustomPriorityOrder = 100 }; + var result = service.EvalDefinitionPriority(new List { def, def2, def3 }); result.Definition.Should().Be(def2); result.PriorityType.Should().Be(DefinitionPriorityType.None); } @@ -2612,21 +2340,17 @@ public void EvalDefinitionPriority_should_return_localization_override_with_cust var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_localization_override_with_custom_priority_and_filename_priority", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_localization_override_with_custom_priority_and_filename_priority", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(false); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = @"localisation\test.yml", ModName = "1" }; - var def2 = new Definition() { File = @"localisation\replace\test.yml", ModName = "2", CustomPriorityOrder = 1000 }; - var def3 = new Definition() { File = @"localisation\replace\test2.yml", ModName = "3", CustomPriorityOrder = 1000 }; - var result = service.EvalDefinitionPriority(new List() { def, def2, def3 }); + var def = new Definition { File = @"localisation\test.yml", ModName = "1" }; + var def2 = new Definition { File = @"localisation\replace\test.yml", ModName = "2", CustomPriorityOrder = 1000 }; + var def3 = new Definition { File = @"localisation\replace\test2.yml", ModName = "3", CustomPriorityOrder = 1000 }; + var result = service.EvalDefinitionPriority(new List { def, def2, def3 }); result.Definition.Should().Be(def3); result.PriorityType.Should().Be(DefinitionPriorityType.None); } @@ -2648,21 +2372,17 @@ public void EvalDefinitionPriority_should_return_localization_with_custom_priori var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_localization_with_custom_priority_and_filename_priority", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_localization_with_custom_priority_and_filename_priority", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(false); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = @"localisation\test.yml", ModName = "1" }; - var def2 = new Definition() { File = @"localisation\replace\test.yml", ModName = "2", CustomPriorityOrder = 1000 }; - var def3 = new Definition() { File = @"localisation\replace\test2.yml", ModName = "3", CustomPriorityOrder = 1000 }; - var result = service.EvalDefinitionPriority(new List() { def, def2, def3 }); + var def = new Definition { File = @"localisation\test.yml", ModName = "1" }; + var def2 = new Definition { File = @"localisation\replace\test.yml", ModName = "2", CustomPriorityOrder = 1000 }; + var def3 = new Definition { File = @"localisation\replace\test2.yml", ModName = "3", CustomPriorityOrder = 1000 }; + var result = service.EvalDefinitionPriority(new List { def, def2, def3 }); result.Definition.Should().Be(def3); result.PriorityType.Should().Be(DefinitionPriorityType.None); } @@ -2684,21 +2404,17 @@ public void EvalDefinitionPriority_should_return_localization_override_and_ignor var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_localization_override_and_ignore_non_game_definitions", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_localization_override_and_ignore_non_game_definitions", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(false); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = @"localisation\test.yml", ModName = "1" }; - var def2 = new Definition() { File = @"localisation\replace\test2.yml", ModName = "2" }; - var def3 = new Definition() { File = @"localisation\replace\test.yml", ModName = "Game", IsFromGame = true }; - var result = service.EvalDefinitionPriority(new List() { def3, def, def2 }); + var def = new Definition { File = @"localisation\test.yml", ModName = "1" }; + var def2 = new Definition { File = @"localisation\replace\test2.yml", ModName = "2" }; + var def3 = new Definition { File = @"localisation\replace\test.yml", ModName = "Game", IsFromGame = true }; + var result = service.EvalDefinitionPriority(new List { def3, def, def2 }); result.Definition.Should().Be(def2); result.PriorityType.Should().Be(DefinitionPriorityType.None); } @@ -2720,20 +2436,16 @@ public void EvalDefinitionPriority_should_return_localization_and_ignore_non_gam var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_should_return_localization_and_ignore_non_game_definitions", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_should_return_localization_and_ignore_non_game_definitions", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(false); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = @"localisation\test.yml", ModName = "1" }; - var def3 = new Definition() { File = @"localisation\test.yml", ModName = "Game", IsFromGame = true }; - var result = service.EvalDefinitionPriority(new List() { def3, def }); + var def = new Definition { File = @"localisation\test.yml", ModName = "1" }; + var def3 = new Definition { File = @"localisation\test.yml", ModName = "Game", IsFromGame = true }; + var result = service.EvalDefinitionPriority(new List { def3, def }); result.Definition.Should().Be(def); result.PriorityType.Should().Be(DefinitionPriorityType.None); } @@ -2755,21 +2467,17 @@ public void EvalDefinitionPriority_gfx_replace_directory_should_win() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "EvalDefinitionPriority_gfx_replace_directory_should_win", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "EvalDefinitionPriority_gfx_replace_directory_should_win", UserDirectory = "C:\\Users\\Fake" }); var infoProvider = new Mock(); infoProvider.Setup(p => p.DefinitionUsesFIOSRules(It.IsAny())).Returns(false); infoProvider.Setup(p => p.CanProcess(It.IsAny())).Returns(true); infoProvider.Setup(p => p.IsFullyImplemented).Returns(true); - var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List() { infoProvider.Object }); + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, new List { infoProvider.Object }); - var def = new Definition() { File = @"gfx\test.gfx", ModName = "1" }; - var def2 = new Definition() { File = @"gfx\replace\test.gfx", ModName = "2", VirtualPath = "gfx\\test.gfx" }; - var def3 = new Definition() { File = @"gfx\test.gfx", ModName = "Game", IsFromGame = true }; - var result = service.EvalDefinitionPriority(new List() { def3, def, def2 }); + var def = new Definition { File = @"gfx\test.gfx", ModName = "1" }; + var def2 = new Definition { File = @"gfx\replace\test.gfx", ModName = "2", VirtualPath = "gfx\\test.gfx" }; + var def3 = new Definition { File = @"gfx\test.gfx", ModName = "Game", IsFromGame = true }; + var result = service.EvalDefinitionPriority(new List { def3, def, def2 }); result.Definition.Should().Be(def2); result.PriorityType.Should().Be(DefinitionPriorityType.ModOrder); } @@ -2816,32 +2524,16 @@ public async Task SaveIgnoredPathsAsync_should_be_false() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { - Type = "SaveIgnoredPathsAsync_should_be_false", - UserDirectory = "C:\\Users\\Fake", - WorkshopDirectory = new List() { "C:\\Fake" }, - CustomModDirectory = string.Empty + Type = "SaveIgnoredPathsAsync_should_be_false", UserDirectory = "C:\\Users\\Fake", WorkshopDirectory = new List { "C:\\Fake" }, CustomModDirectory = string.Empty }); modPatchExporter.Setup(p => p.SaveStateAsync(It.IsAny())).Returns(Task.FromResult(false)); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; + return new Mod { FileName = o.FileName, Name = o.Name }; }); - var collections = new List() - { - new ModCollection() - { - IsSelected = true, - Mods = new List() { "mod/fake1.txt", "mod/fake2.txt"}, - Name = "test", - Game = "SaveIgnoredPathsAsync_should_be_false" - } - }; + var collections = new List { new ModCollection { IsSelected = true, Mods = new List { "mod/fake1.txt", "mod/fake2.txt" }, Name = "test", Game = "SaveIgnoredPathsAsync_should_be_false" } }; storageProvider.Setup(s => s.GetModCollections()).Returns(() => { return collections; @@ -2853,7 +2545,7 @@ public async Task SaveIgnoredPathsAsync_should_be_false() var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, null); var indexed = new IndexedDefinitions(); - var result = await service.SaveIgnoredPathsAsync(new ConflictResult() { AllConflicts = indexed, Conflicts = indexed }, "test"); + var result = await service.SaveIgnoredPathsAsync(new ConflictResult { AllConflicts = indexed, Conflicts = indexed }, "test"); result.Should().BeFalse(); } @@ -2874,32 +2566,16 @@ public async Task SaveIgnoredPathsAsync_should_be_true() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { - Type = "SaveIgnoredPathsAsync_should_be_true", - UserDirectory = "C:\\Users\\Fake", - WorkshopDirectory = new List() { "C:\\Fake" }, - CustomModDirectory = string.Empty + Type = "SaveIgnoredPathsAsync_should_be_true", UserDirectory = "C:\\Users\\Fake", WorkshopDirectory = new List { "C:\\Fake" }, CustomModDirectory = string.Empty }); modPatchExporter.Setup(p => p.SaveStateAsync(It.IsAny())).Returns(Task.FromResult(true)); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; + return new Mod { FileName = o.FileName, Name = o.Name }; }); - var collections = new List() - { - new ModCollection() - { - IsSelected = true, - Mods = new List() { "mod/fake1.txt", "mod/fake2.txt"}, - Name = "test", - Game = "SaveIgnoredPathsAsync_should_be_false" - } - }; + var collections = new List { new ModCollection { IsSelected = true, Mods = new List { "mod/fake1.txt", "mod/fake2.txt" }, Name = "test", Game = "SaveIgnoredPathsAsync_should_be_false" } }; storageProvider.Setup(s => s.GetModCollections()).Returns(() => { return collections; @@ -2911,7 +2587,7 @@ public async Task SaveIgnoredPathsAsync_should_be_true() var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, null); var indexed = new IndexedDefinitions(); - var result = await service.SaveIgnoredPathsAsync(new ConflictResult() { AllConflicts = indexed, Conflicts = indexed }, "test"); + var result = await service.SaveIgnoredPathsAsync(new ConflictResult { AllConflicts = indexed, Conflicts = indexed }, "test"); result.Should().BeTrue(); } @@ -2932,32 +2608,16 @@ public async Task SaveIgnoredPathsAsync_should_be_true_when_readonly_mode() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { - Type = "SaveIgnoredPathsAsync_should_be_false_when_readonly_mode", - UserDirectory = "C:\\Users\\Fake", - WorkshopDirectory = new List() { "C:\\Fake" }, - CustomModDirectory = string.Empty + Type = "SaveIgnoredPathsAsync_should_be_false_when_readonly_mode", UserDirectory = "C:\\Users\\Fake", WorkshopDirectory = new List { "C:\\Fake" }, CustomModDirectory = string.Empty }); modPatchExporter.Setup(p => p.SaveStateAsync(It.IsAny())).Returns(Task.FromResult(true)); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; + return new Mod { FileName = o.FileName, Name = o.Name }; }); - var collections = new List() - { - new ModCollection() - { - IsSelected = true, - Mods = new List() { "mod/fake1.txt", "mod/fake2.txt"}, - Name = "test", - Game = "SaveIgnoredPathsAsync_should_be_false" - } - }; + var collections = new List { new ModCollection { IsSelected = true, Mods = new List { "mod/fake1.txt", "mod/fake2.txt" }, Name = "test", Game = "SaveIgnoredPathsAsync_should_be_false" } }; storageProvider.Setup(s => s.GetModCollections()).Returns(() => { return collections; @@ -2969,7 +2629,7 @@ public async Task SaveIgnoredPathsAsync_should_be_true_when_readonly_mode() var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, null); var indexed = new IndexedDefinitions(); - var result = await service.SaveIgnoredPathsAsync(new ConflictResult() { AllConflicts = indexed, Conflicts = indexed, Mode = IronyModManager.Models.Common.PatchStateMode.ReadOnly }, "test"); + var result = await service.SaveIgnoredPathsAsync(new ConflictResult { AllConflicts = indexed, Conflicts = indexed, Mode = PatchStateMode.ReadOnly }, "test"); result.Should().BeTrue(); } @@ -3016,11 +2676,7 @@ public async Task CopyPatchMod_should_be_false() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "CopyPatchMod_should_be_false", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "CopyPatchMod_should_be_false", UserDirectory = "C:\\Users\\Fake" }); modPatchExporter.Setup(p => p.CopyPatchModAsync(It.IsAny())).Returns(Task.FromResult(false)); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, null); @@ -3045,11 +2701,7 @@ public async Task CopyPatchMod_should_be_true() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "CopyPatchMod_should_be_true", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "CopyPatchMod_should_be_true", UserDirectory = "C:\\Users\\Fake" }); modPatchExporter.Setup(p => p.CopyPatchModAsync(It.IsAny())).Returns(Task.FromResult(true)); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, null); @@ -3100,11 +2752,7 @@ public async Task RenamePatchMod_should_be_false() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "RenamePatchMod_should_be_false", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "RenamePatchMod_should_be_false", UserDirectory = "C:\\Users\\Fake" }); modPatchExporter.Setup(p => p.RenamePatchModAsync(It.IsAny())).Returns(Task.FromResult(false)); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, null); @@ -3129,11 +2777,7 @@ public async Task RenamePatchMod_should_be_true() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "RenamePatchMod_should_be_true", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "RenamePatchMod_should_be_true", UserDirectory = "C:\\Users\\Fake" }); modPatchExporter.Setup(p => p.RenamePatchModAsync(It.IsAny())).Returns(Task.FromResult(true)); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, null); @@ -3158,11 +2802,7 @@ public void ResetCache_should_be_true() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "ResetCache_should_be_true", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "ResetCache_should_be_true", UserDirectory = "C:\\Users\\Fake" }); modPatchExporter.Setup(p => p.ResetCache()); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, null); @@ -3190,7 +2830,7 @@ public async Task Should_not_get_patch_state_when_no_selected_game() var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); var result = await service.GetPatchStateModeAsync("fake"); - result.Should().Be(IronyModManager.Models.Common.PatchStateMode.None); + result.Should().Be(PatchStateMode.None); } /// @@ -3213,7 +2853,7 @@ public async Task Should_not_get_patch_state_when_no_collection() var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); var result = await service.GetPatchStateModeAsync(null); - result.Should().Be(IronyModManager.Models.Common.PatchStateMode.None); + result.Should().Be(PatchStateMode.None); } /// @@ -3234,24 +2874,14 @@ public async Task Should_get_patch_state() var modPatchExporter = new Mock(); modPatchExporter.Setup(p => p.GetPatchStateAsync(It.IsAny(), It.IsAny())).ReturnsAsync((ModPatchExporterParameters p, bool load) => { - var res = new PatchState() - { - Conflicts = new List(), - ResolvedConflicts = new List(), - OverwrittenConflicts = new List(), - Mode = IO.Common.PatchStateMode.Default - }; + var res = new PatchState { Conflicts = new List(), ResolvedConflicts = new List(), OverwrittenConflicts = new List(), Mode = IO.Common.PatchStateMode.Default }; return res; }); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "Should_get_patch_state", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Should_get_patch_state", UserDirectory = "C:\\Users\\Fake" }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); var result = await service.GetPatchStateModeAsync("fake"); - result.Should().Be(IronyModManager.Models.Common.PatchStateMode.Default); + result.Should().Be(PatchStateMode.Default); } /// @@ -3274,15 +2904,11 @@ public async Task Should_get_patch_state_from_mode_text() { return IO.Common.PatchStateMode.Default; }); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "Should_get_patch_state", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Should_get_patch_state", UserDirectory = "C:\\Users\\Fake" }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); var result = await service.GetPatchStateModeAsync("fake"); - result.Should().Be(IronyModManager.Models.Common.PatchStateMode.Default); + result.Should().Be(PatchStateMode.Default); } /// @@ -3303,20 +2929,12 @@ public void Should_not_resolve_full_definition_path_when_no_game() var modPatchExporter = new Mock(); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; + return new Mod { FileName = o.FileName, Name = o.Name }; }); gameService.Setup(p => p.GetSelected()).Returns((IGame)null); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var result = service.ResolveFullDefinitionPath(new Definition() - { - File = "events\\test.txt", - ModName = "test" - }); + var result = service.ResolveFullDefinitionPath(new Definition { File = "events\\test.txt", ModName = "test" }); result.Should().Be(string.Empty); } @@ -3338,18 +2956,9 @@ public void Should_not_resolve_full_definition_path_when_definition_null() var modPatchExporter = new Mock(); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; - }); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "Should_not_resolve_full_definition_path_when_definition_null", - UserDirectory = "C:\\Users\\Fake", - WorkshopDirectory = new List() { "C:\\fake" } + return new Mod { FileName = o.FileName, Name = o.Name }; }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Should_not_resolve_full_definition_path_when_definition_null", UserDirectory = "C:\\Users\\Fake", WorkshopDirectory = new List { "C:\\fake" } }); SetupMockCase(reader, parserManager, modParser); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); @@ -3373,53 +2982,28 @@ public void Should_resolve_full_definition_path() var gameService = new Mock(); var mapper = new Mock(); var modPatchExporter = new Mock(); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Should_resolve_full_definition_path", UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), - WorkshopDirectory = new List() { "C:\\fake" }, + WorkshopDirectory = new List { "C:\\fake" }, CustomModDirectory = string.Empty }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; + return new Mod { FileName = o.FileName, Name = o.Name }; }); SetupMockCase(reader, parserManager, modParser); - var collections = new List() - { - new ModCollection() - { - IsSelected = true, - Mods = new List() { "mod/fakemod.mod"}, - Name = "test", - Game = "Should_resolve_full_definition_path" - } - }; + var collections = new List { new ModCollection { IsSelected = true, Mods = new List { "mod/fakemod.mod" }, Name = "test", Game = "Should_resolve_full_definition_path" } }; storageProvider.Setup(s => s.GetModCollections()).Returns(() => { return collections; }); - var fileInfos = new List() - { - new FileInfo() - { - Content = new List() { "1" }, - FileName = "fakemod.mod", - IsBinary = false - } - }; + var fileInfos = new List { new FileInfo { Content = new List { "1" }, FileName = "fakemod.mod", IsBinary = false } }; reader.Setup(s => s.Read(It.IsAny(), It.IsAny>(), It.IsAny())).Returns(fileInfos); modParser.Setup(s => s.Parse(It.IsAny>(), It.IsAny())).Returns((IEnumerable values, DescriptorModType t) => { - return new ModObject() - { - FileName = "fakemod", - Name = "1" - }; + return new ModObject { FileName = "fakemod", Name = "1" }; }); modWriter.Setup(p => p.ModDirectoryExists(It.IsAny())).Returns((ModWriterParameters p) => { @@ -3427,11 +3011,7 @@ public void Should_resolve_full_definition_path() }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var result = service.ResolveFullDefinitionPath(new Definition() - { - File = "events\\test.txt", - ModName = "1" - }); + var result = service.ResolveFullDefinitionPath(new Definition { File = "events\\test.txt", ModName = "1" }); result.Should().Be(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod\\fakemod\\events\\test.txt")); } @@ -3451,53 +3031,28 @@ public void Should_resolve_full_definition_archive_path() var gameService = new Mock(); var mapper = new Mock(); var modPatchExporter = new Mock(); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Should_resolve_full_definition_archive_path", UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), - WorkshopDirectory = new List() { "C:\\fake" }, + WorkshopDirectory = new List { "C:\\fake" }, CustomModDirectory = string.Empty }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; + return new Mod { FileName = o.FileName, Name = o.Name }; }); SetupMockCase(reader, parserManager, modParser); - var collections = new List() - { - new ModCollection() - { - IsSelected = true, - Mods = new List() { "mod/fakemod.mod"}, - Name = "test", - Game = "Should_resolve_full_definition_archive_path" - } - }; + var collections = new List { new ModCollection { IsSelected = true, Mods = new List { "mod/fakemod.mod" }, Name = "test", Game = "Should_resolve_full_definition_archive_path" } }; storageProvider.Setup(s => s.GetModCollections()).Returns(() => { return collections; }); - var fileInfos = new List() - { - new FileInfo() - { - Content = new List() { "1" }, - FileName = "fakemod.mod", - IsBinary = false - } - }; + var fileInfos = new List { new FileInfo { Content = new List { "1" }, FileName = "fakemod.mod", IsBinary = false } }; reader.Setup(s => s.Read(It.IsAny(), It.IsAny>(), It.IsAny())).Returns(fileInfos); modParser.Setup(s => s.Parse(It.IsAny>(), It.IsAny())).Returns((IEnumerable values, DescriptorModType t) => { - return new ModObject() - { - FileName = "fakemod.zip", - Name = "1" - }; + return new ModObject { FileName = "fakemod.zip", Name = "1" }; }); modWriter.Setup(p => p.ModDirectoryExists(It.IsAny())).Returns((ModWriterParameters p) => { @@ -3505,11 +3060,7 @@ public void Should_resolve_full_definition_archive_path() }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var result = service.ResolveFullDefinitionPath(new Definition() - { - File = "events\\test.txt", - ModName = "1" - }); + var result = service.ResolveFullDefinitionPath(new Definition { File = "events\\test.txt", ModName = "1" }); result.Should().Be(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod\\fakemod.zip")); } @@ -3528,11 +3079,8 @@ public void Should_add_mods_to_ignore_list() var mapper = new Mock(); var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var c = new ConflictResult() - { - IgnoredPaths = "modName:a" - }; - service.AddModsToIgnoreList(c, new List() { new ModIgnoreConfiguration() { ModName = "a" }, new ModIgnoreConfiguration() { ModName = "b" } }); + var c = new ConflictResult { IgnoredPaths = "modName:a" }; + service.AddModsToIgnoreList(c, new List { new ModIgnoreConfiguration { ModName = "a" }, new ModIgnoreConfiguration { ModName = "b" } }); c.IgnoredPaths.Should().Be("modName:a--count:2" + Environment.NewLine + "modName:b--count:2"); } @@ -3551,11 +3099,8 @@ public void Should_add_mods_to_ignore_list_with_specified_count() var mapper = new Mock(); var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var c = new ConflictResult() - { - IgnoredPaths = "modName:a" - }; - service.AddModsToIgnoreList(c, new List() { new ModIgnoreConfiguration() { ModName = "a", Count = 3 }, new ModIgnoreConfiguration() { ModName = "b" } }); + var c = new ConflictResult { IgnoredPaths = "modName:a" }; + service.AddModsToIgnoreList(c, new List { new ModIgnoreConfiguration { ModName = "a", Count = 3 }, new ModIgnoreConfiguration { ModName = "b" } }); c.IgnoredPaths.Should().Be("modName:a--count:3" + Environment.NewLine + "modName:b--count:2"); } @@ -3574,10 +3119,7 @@ public void Should_get_ignored_mods() var mapper = new Mock(); var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var result = service.GetIgnoredMods(new ConflictResult() - { - IgnoredPaths = "modName:a" + Environment.NewLine + "modName:b" - }); + var result = service.GetIgnoredMods(new ConflictResult { IgnoredPaths = "modName:a" + Environment.NewLine + "modName:b" }); result.Count.Should().Be(2); result[0].ModName.Should().Be("a"); result[0].Count.Should().Be(2); @@ -3600,10 +3142,7 @@ public void Should_get_ignored_mods_with_specified_count() var mapper = new Mock(); var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var result = service.GetIgnoredMods(new ConflictResult() - { - IgnoredPaths = "modName:a--count:3" + Environment.NewLine + "modName:b" - }); + var result = service.GetIgnoredMods(new ConflictResult { IgnoredPaths = "modName:a--count:3" + Environment.NewLine + "modName:b" }); result.Count.Should().Be(2); result[0].ModName.Should().Be("a"); result[0].Count.Should().Be(3); @@ -3625,30 +3164,16 @@ public async Task Should_reset_resolved_conflict() var gameService = new Mock(); var mapper = new Mock(); var modPatchExporter = new Mock(); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { - Type = "Should_reset_resolved_conflict", - UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), - WorkshopDirectory = new List() { "C:\\fake" } + Type = "Should_reset_resolved_conflict", UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), WorkshopDirectory = new List { "C:\\fake" } }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); var resolved = new IndexedDefinitions(); - await resolved.InitMapAsync(new List() - { - new Definition() - { - Type = "test", - Id = "1", - ModName = "test" - } - }); + await resolved.InitMapAsync(new List { new Definition { Type = "test", Id = "1", ModName = "test" } }); - var c = new ConflictResult() - { - AllConflicts = new IndexedDefinitions(), - ResolvedConflicts = resolved - }; + var c = new ConflictResult { AllConflicts = new IndexedDefinitions(), ResolvedConflicts = resolved }; var result = await service.ResetResolvedConflictAsync(c, "test-1", "fake"); result.Should().BeTrue(); } @@ -3667,28 +3192,15 @@ public async Task Should_not_reset_resolved_conflict() var gameService = new Mock(); var mapper = new Mock(); var modPatchExporter = new Mock(); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { - Type = "Should_not_reset_resolved_conflict", - UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), - WorkshopDirectory = new List() { "C:\\fake" } + Type = "Should_not_reset_resolved_conflict", UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), WorkshopDirectory = new List { "C:\\fake" } }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); var resolved = new IndexedDefinitions(); - await resolved.InitMapAsync(new List() - { - new Definition() - { - Type = "test", - Id = "1", - ModName = "test" - } - }); + await resolved.InitMapAsync(new List { new Definition { Type = "test", Id = "1", ModName = "test" } }); - var c = new ConflictResult() - { - ResolvedConflicts = resolved - }; + var c = new ConflictResult { ResolvedConflicts = resolved }; var result = await service.ResetResolvedConflictAsync(c, "test-2", "fake"); result.Should().BeFalse(); } @@ -3708,30 +3220,16 @@ public async Task Should_reset_ignored_conflict() var gameService = new Mock(); var mapper = new Mock(); var modPatchExporter = new Mock(); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { - Type = "Should_reset_ignored_conflict", - UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), - WorkshopDirectory = new List() { "C:\\fake" } + Type = "Should_reset_ignored_conflict", UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), WorkshopDirectory = new List { "C:\\fake" } }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); var ignored = new IndexedDefinitions(); - await ignored.InitMapAsync(new List() - { - new Definition() - { - Type = "test", - Id = "1", - ModName = "test" - } - }); + await ignored.InitMapAsync(new List { new Definition { Type = "test", Id = "1", ModName = "test" } }); - var c = new ConflictResult() - { - AllConflicts = new IndexedDefinitions(), - IgnoredConflicts = ignored - }; + var c = new ConflictResult { AllConflicts = new IndexedDefinitions(), IgnoredConflicts = ignored }; var result = await service.ResetIgnoredConflictAsync(c, "test-1", "fake"); result.Should().BeTrue(); } @@ -3750,29 +3248,16 @@ public async Task Should_not_reset_ignored_conflict() var gameService = new Mock(); var mapper = new Mock(); var modPatchExporter = new Mock(); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { - Type = "Should_not_reset_ignored_conflict", - UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), - WorkshopDirectory = new List() { "C:\\fake" } + Type = "Should_not_reset_ignored_conflict", UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), WorkshopDirectory = new List { "C:\\fake" } }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); var ignored = new IndexedDefinitions(); - await ignored.InitMapAsync(new List() - { - new Definition() - { - Type = "test", - Id = "1", - ModName = "test" - } - }); + await ignored.InitMapAsync(new List { new Definition { Type = "test", Id = "1", ModName = "test" } }); - var c = new ConflictResult() - { - IgnoredConflicts = ignored - }; + var c = new ConflictResult { IgnoredConflicts = ignored }; var result = await service.ResetIgnoredConflictAsync(c, "test-2", "fake"); result.Should().BeFalse(); } @@ -3828,20 +3313,13 @@ public async Task Should_not_apply_custom_mod_patch_when_nothing_to_merge() var gameService = new Mock(); var mapper = new Mock(); var modPatchExporter = new Mock(); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { - Type = "Should_not_apply_custom_mod_patch_when_nothing_to_merge", - UserDirectory = "C:\\Users\\Fake", - WorkshopDirectory = new List() { "C:\\fake" }, - CustomModDirectory = string.Empty + Type = "Should_not_apply_custom_mod_patch_when_nothing_to_merge", UserDirectory = "C:\\Users\\Fake", WorkshopDirectory = new List { "C:\\fake" }, CustomModDirectory = string.Empty }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; + return new Mod { FileName = o.FileName, Name = o.Name }; }); SetupMockCase(reader, parserManager, modParser); modWriter.Setup(p => p.ModDirectoryExists(It.IsAny())).Returns((ModWriterParameters p) => @@ -3853,14 +3331,8 @@ public async Task Should_not_apply_custom_mod_patch_when_nothing_to_merge() var indexed = new IndexedDefinitions(); await indexed.InitMapAsync(new List()); - var c = new ConflictResult() - { - AllConflicts = indexed, - Conflicts = indexed, - ResolvedConflicts = indexed, - CustomConflicts = indexed, - }; - var result = await service.AddCustomModPatchAsync(c, new Definition() { ModName = "test", ValueType = ValueType.Object }, "colname"); + var c = new ConflictResult { AllConflicts = indexed, Conflicts = indexed, ResolvedConflicts = indexed, CustomConflicts = indexed }; + var result = await service.AddCustomModPatchAsync(c, new Definition { ModName = "test", ValueType = ValueType.Object }, "colname"); result.Should().BeFalse(); } @@ -3881,31 +3353,15 @@ public async Task Should_return_true_when_applying_custom_patches() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { - Type = "Should_return_true_when_applying_custom_patches", - UserDirectory = "C:\\Users\\Fake", - WorkshopDirectory = new List() { "C:\\fake" }, - CustomModDirectory = string.Empty + Type = "Should_return_true_when_applying_custom_patches", UserDirectory = "C:\\Users\\Fake", WorkshopDirectory = new List { "C:\\fake" }, CustomModDirectory = string.Empty }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; + return new Mod { FileName = o.FileName, Name = o.Name }; }); - var collections = new List() - { - new ModCollection() - { - IsSelected = true, - Mods = new List() { "mod/fake1.txt", "mod/fake2.txt"}, - Name = "test", - Game = "Should_return_true_when_applying_custom_patches" - } - }; + var collections = new List { new ModCollection { IsSelected = true, Mods = new List { "mod/fake1.txt", "mod/fake2.txt" }, Name = "test", Game = "Should_return_true_when_applying_custom_patches" } }; storageProvider.Setup(s => s.GetModCollections()).Returns(() => { return collections; @@ -3920,6 +3376,7 @@ public async Task Should_return_true_when_applying_custom_patches() { return true; } + return false; }); modWriter.Setup(p => p.ModDirectoryExists(It.IsAny())).Returns((ModWriterParameters p) => @@ -3928,18 +3385,18 @@ public async Task Should_return_true_when_applying_custom_patches() }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var definitions = new List() + var definitions = new List { - new Definition() + new Definition { File = "events\\1.txt", Code = "a", Id = "a", - Type= "events", + Type = "events", ModName = "test1", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\2.txt", Code = "b", @@ -3947,7 +3404,7 @@ public async Task Should_return_true_when_applying_custom_patches() Id = "a", ModName = "test2", ValueType = ValueType.Object - }, + } }; var all = new IndexedDefinitions(); await all.InitMapAsync(definitions); @@ -3958,7 +3415,7 @@ public async Task Should_return_true_when_applying_custom_patches() var custom = new IndexedDefinitions(); await custom.InitMapAsync(new List()); - var c = new ConflictResult() + var c = new ConflictResult { AllConflicts = all, Conflicts = all, @@ -3966,7 +3423,7 @@ public async Task Should_return_true_when_applying_custom_patches() CustomConflicts = custom, OverwrittenConflicts = custom }; - var result = await service.AddCustomModPatchAsync(c, new Definition() { ModName = "1" }, "colname"); + var result = await service.AddCustomModPatchAsync(c, new Definition { ModName = "1" }, "colname"); result.Should().BeTrue(); } @@ -3987,31 +3444,15 @@ public async Task Should_throw_exception_when_applying_custom_patches_due_to_rea var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { - Type = "Should_throw_exception_when_applying_custom_patches_due_to_readonly_mode", - UserDirectory = "C:\\Users\\Fake", - WorkshopDirectory = new List() { "C:\\fake" }, - CustomModDirectory = string.Empty + Type = "Should_throw_exception_when_applying_custom_patches_due_to_readonly_mode", UserDirectory = "C:\\Users\\Fake", WorkshopDirectory = new List { "C:\\fake" }, CustomModDirectory = string.Empty }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; + return new Mod { FileName = o.FileName, Name = o.Name }; }); - var collections = new List() - { - new ModCollection() - { - IsSelected = true, - Mods = new List() { "mod/fake1.txt", "mod/fake2.txt"}, - Name = "test", - Game = "Should_return_true_when_applying_custom_patches" - } - }; + var collections = new List { new ModCollection { IsSelected = true, Mods = new List { "mod/fake1.txt", "mod/fake2.txt" }, Name = "test", Game = "Should_return_true_when_applying_custom_patches" } }; storageProvider.Setup(s => s.GetModCollections()).Returns(() => { return collections; @@ -4026,6 +3467,7 @@ public async Task Should_throw_exception_when_applying_custom_patches_due_to_rea { return true; } + return false; }); modWriter.Setup(p => p.ModDirectoryExists(It.IsAny())).Returns((ModWriterParameters p) => @@ -4034,18 +3476,18 @@ public async Task Should_throw_exception_when_applying_custom_patches_due_to_rea }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var definitions = new List() + var definitions = new List { - new Definition() + new Definition { File = "events\\1.txt", Code = "a", Id = "a", - Type= "events", + Type = "events", ModName = "test1", ValueType = ValueType.Object }, - new Definition() + new Definition { File = "events\\2.txt", Code = "b", @@ -4053,7 +3495,7 @@ public async Task Should_throw_exception_when_applying_custom_patches_due_to_rea Id = "a", ModName = "test2", ValueType = ValueType.Object - }, + } }; var all = new IndexedDefinitions(); await all.InitMapAsync(definitions); @@ -4064,25 +3506,26 @@ public async Task Should_throw_exception_when_applying_custom_patches_due_to_rea var custom = new IndexedDefinitions(); await custom.InitMapAsync(new List()); - var c = new ConflictResult() + var c = new ConflictResult { AllConflicts = all, Conflicts = all, ResolvedConflicts = resolved, CustomConflicts = custom, OverwrittenConflicts = custom, - Mode = IronyModManager.Models.Common.PatchStateMode.ReadOnly + Mode = PatchStateMode.ReadOnly }; var exceptionThrown = false; try { - var result = await service.AddCustomModPatchAsync(c, new Definition() { ModName = "1" }, "colname"); + var result = await service.AddCustomModPatchAsync(c, new Definition { ModName = "1" }, "colname"); } catch (Exception ex) { ex.GetType().Should().Be(typeof(ArgumentException)); exceptionThrown = true; } + exceptionThrown.Should().BeTrue(); } @@ -4100,30 +3543,16 @@ public async Task Should_reset_custom_conflict() var gameService = new Mock(); var mapper = new Mock(); var modPatchExporter = new Mock(); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { - Type = "Should_reset_custom_conflict", - UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), - WorkshopDirectory = new List() { "C:\\fake" } + Type = "Should_reset_custom_conflict", UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), WorkshopDirectory = new List { "C:\\fake" } }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); var custom = new IndexedDefinitions(); - await custom.InitMapAsync(new List() - { - new Definition() - { - Type = "test", - Id = "1", - ModName = "test" - } - }); + await custom.InitMapAsync(new List { new Definition { Type = "test", Id = "1", ModName = "test" } }); - var c = new ConflictResult() - { - AllConflicts = new IndexedDefinitions(), - CustomConflicts = custom - }; + var c = new ConflictResult { AllConflicts = new IndexedDefinitions(), CustomConflicts = custom }; var result = await service.ResetCustomConflictAsync(c, "test-1", "fake"); result.Should().BeTrue(); } @@ -4142,31 +3571,16 @@ public async Task Should_not_reset_custom_conflict_due_to_readonly_mode() var gameService = new Mock(); var mapper = new Mock(); var modPatchExporter = new Mock(); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { - Type = "Should_reset_custom_conflict", - UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), - WorkshopDirectory = new List() { "C:\\fake" } + Type = "Should_reset_custom_conflict", UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), WorkshopDirectory = new List { "C:\\fake" } }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); var custom = new IndexedDefinitions(); - await custom.InitMapAsync(new List() - { - new Definition() - { - Type = "test", - Id = "1", - ModName = "test" - } - }); + await custom.InitMapAsync(new List { new Definition { Type = "test", Id = "1", ModName = "test" } }); - var c = new ConflictResult() - { - AllConflicts = new IndexedDefinitions(), - CustomConflicts = custom, - Mode = IronyModManager.Models.Common.PatchStateMode.ReadOnly - }; + var c = new ConflictResult { AllConflicts = new IndexedDefinitions(), CustomConflicts = custom, Mode = PatchStateMode.ReadOnly }; var exceptionThrown = false; try { @@ -4177,6 +3591,7 @@ await custom.InitMapAsync(new List() ex.GetType().Should().Be(typeof(ArgumentException)); exceptionThrown = true; } + exceptionThrown.Should().BeTrue(); } @@ -4194,28 +3609,15 @@ public async Task Should_not_reset_custom_conflict() var gameService = new Mock(); var mapper = new Mock(); var modPatchExporter = new Mock(); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { - Type = "Should_not_reset_custom_conflict", - UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), - WorkshopDirectory = new List() { "C:\\fake" } + Type = "Should_not_reset_custom_conflict", UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), WorkshopDirectory = new List { "C:\\fake" } }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); var custom = new IndexedDefinitions(); - await custom.InitMapAsync(new List() - { - new Definition() - { - Type = "test", - Id = "1", - ModName = "test" - } - }); + await custom.InitMapAsync(new List { new Definition { Type = "test", Id = "1", ModName = "test" } }); - var c = new ConflictResult() - { - CustomConflicts = custom - }; + var c = new ConflictResult { CustomConflicts = custom }; var result = await service.ResetCustomConflictAsync(c, "test-2", "fake"); result.Should().BeFalse(); } @@ -4254,11 +3656,9 @@ public void Should_invalidate_mod_patch_state() var gameService = new Mock(); var mapper = new Mock(); var modPatchExporter = new Mock(); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { - Type = "Should_invalidate_mod_patch_state", - UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), - WorkshopDirectory = new List() { "C:\\fake" } + Type = "Should_invalidate_mod_patch_state", UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), WorkshopDirectory = new List { "C:\\fake" } }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); var result = service.ResetPatchStateCache(); @@ -4284,6 +3684,7 @@ public async Task Patch_mod_should_not_need_update_when_no_game() var result = await service.PatchModNeedsUpdateAsync("test", null); result.Should().BeFalse(); } + /// /// Defines the test method Patch_mod_should_not_need_update_when_no_collection. /// @@ -4298,11 +3699,9 @@ public async Task Patch_mod_should_not_need_update_when_no_collection() var gameService = new Mock(); var mapper = new Mock(); var modPatchExporter = new Mock(); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { - Type = "Patch_mod_should_not_need_update_when_no_collection", - UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), - WorkshopDirectory = new List() { "C:\\fake" } + Type = "Patch_mod_should_not_need_update_when_no_collection", UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), WorkshopDirectory = new List { "C:\\fake" } }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); var result = await service.PatchModNeedsUpdateAsync(null, null); @@ -4324,42 +3723,39 @@ public async Task Patch_mod_should_need_update_when_mod_not_present() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Patch_mod_should_not_need_update_when_mod_not_present", UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), - WorkshopDirectory = new List() { "C:\\fake" }, + WorkshopDirectory = new List { "C:\\fake" }, CustomModDirectory = string.Empty }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; + return new Mod { FileName = o.FileName, Name = o.Name }; }); - var collections = new List() - { - new ModCollection() - { - IsSelected = true, - Mods = new List() { "mod/fake1.txt", "mod/fake2.txt"}, - Name = "test", - Game = "Patch_mod_should_not_need_update_when_mod_not_present" - } - }; + var collections = new List { new ModCollection { IsSelected = true, Mods = new List { "mod/fake1.txt", "mod/fake2.txt" }, Name = "test", Game = "Patch_mod_should_not_need_update_when_mod_not_present" } }; storageProvider.Setup(s => s.GetModCollections()).Returns(() => { return collections; }); modPatchExporter.Setup(p => p.GetPatchStateAsync(It.IsAny(), It.IsAny())).ReturnsAsync((ModPatchExporterParameters p, bool load) => { - var res = new PatchState() + var res = new PatchState { - Conflicts = new List() { new Definition() { File = "1", Id = "test", Type = "events", Code = "ab", ModName = "1" } }, + Conflicts = new List + { + new Definition + { + File = "1", + Id = "test", + Type = "events", + Code = "ab", + ModName = "1" + } + }, OverwrittenConflicts = new List(), - LoadOrder = new List() { "mod/fake2.txt", "mod/fake1.txt" } + LoadOrder = new List { "mod/fake2.txt", "mod/fake1.txt" } }; return res; }); @@ -4368,7 +3764,7 @@ public async Task Patch_mod_should_need_update_when_mod_not_present() return false; }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var result = await service.PatchModNeedsUpdateAsync("colname", new List() { "mod/fake2.txt", "mod/fake1.txt" }); + var result = await service.PatchModNeedsUpdateAsync("colname", new List { "mod/fake2.txt", "mod/fake1.txt" }); result.Should().BeTrue(); } @@ -4387,42 +3783,39 @@ public async Task Patch_mod_should_need_update_when_file_not_present() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Patch_mod_should_need_update_when_file_not_present", UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), - WorkshopDirectory = new List() { "C:\\fake" }, + WorkshopDirectory = new List { "C:\\fake" }, CustomModDirectory = string.Empty }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; + return new Mod { FileName = o.FileName, Name = o.Name }; }); - var collections = new List() - { - new ModCollection() - { - IsSelected = true, - Mods = new List() { "mod/fake1.txt", "mod/fake2.txt"}, - Name = "test", - Game = "Patch_mod_should_need_update_when_file_not_present" - } - }; + var collections = new List { new ModCollection { IsSelected = true, Mods = new List { "mod/fake1.txt", "mod/fake2.txt" }, Name = "test", Game = "Patch_mod_should_need_update_when_file_not_present" } }; storageProvider.Setup(s => s.GetModCollections()).Returns(() => { return collections; }); modPatchExporter.Setup(p => p.GetPatchStateAsync(It.IsAny(), It.IsAny())).ReturnsAsync((ModPatchExporterParameters p, bool load) => { - var res = new PatchState() + var res = new PatchState { - Conflicts = new List() { new Definition() { File = "1", Id = "test", Type = "events", Code = "ab", ModName = "1" } }, + Conflicts = new List + { + new Definition + { + File = "1", + Id = "test", + Type = "events", + Code = "ab", + ModName = "1" + } + }, OverwrittenConflicts = new List(), - LoadOrder = new List() { "mod/fake2.txt", "mod/fake1.txt" } + LoadOrder = new List { "mod/fake2.txt", "mod/fake1.txt" } }; return res; }); @@ -4431,7 +3824,7 @@ public async Task Patch_mod_should_need_update_when_file_not_present() return false; }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var result = await service.PatchModNeedsUpdateAsync("colname", new List() { "mod/fake2.txt", "mod/fake1.txt" }); + var result = await service.PatchModNeedsUpdateAsync("colname", new List { "mod/fake2.txt", "mod/fake1.txt" }); result.Should().BeTrue(); } @@ -4450,55 +3843,49 @@ public async Task Patch_mod_should_need_update_when_sha_not_same() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Patch_mod_should_need_update_when_sha_not_same", UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), - WorkshopDirectory = new List() { "C:\\fake" }, + WorkshopDirectory = new List { "C:\\fake" }, CustomModDirectory = string.Empty }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; + return new Mod { FileName = o.FileName, Name = o.Name }; }); - var collections = new List() - { - new ModCollection() - { - IsSelected = true, - Mods = new List() { "mod/fake1.txt", "mod/fake2.txt"}, - Name = "test", - Game = "Patch_mod_should_need_update_when_sha_not_same" - } - }; + var collections = new List { new ModCollection { IsSelected = true, Mods = new List { "mod/fake1.txt", "mod/fake2.txt" }, Name = "test", Game = "Patch_mod_should_need_update_when_sha_not_same" } }; storageProvider.Setup(s => s.GetModCollections()).Returns(() => { return collections; }); modPatchExporter.Setup(p => p.GetPatchStateAsync(It.IsAny(), It.IsAny())).ReturnsAsync((ModPatchExporterParameters p, bool load) => { - var res = new PatchState() + var res = new PatchState { - Conflicts = new List() { new Definition() { File = "1", Id = "test", Type = "events", Code = "ab", ModName = "1" } }, + Conflicts = new List + { + new Definition + { + File = "1", + Id = "test", + Type = "events", + Code = "ab", + ModName = "1" + } + }, OverwrittenConflicts = new List(), - LoadOrder = new List() { "mod/fake2.txt", "mod/fake1.txt" } + LoadOrder = new List { "mod/fake2.txt", "mod/fake1.txt" } }; return res; }); - reader.Setup(p => p.GetFileInfo(It.IsAny(), It.IsAny())).Returns(new FileInfo() - { - ContentSHA = "2" - }); + reader.Setup(p => p.GetFileInfo(It.IsAny(), It.IsAny())).Returns(new FileInfo { ContentSHA = "2" }); modWriter.Setup(p => p.ModDirectoryExists(It.IsAny())).Returns((ModWriterParameters p) => { return false; }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var result = await service.PatchModNeedsUpdateAsync("colname", new List() { "mod/fake2.txt", "mod/fake1.txt" }); + var result = await service.PatchModNeedsUpdateAsync("colname", new List { "mod/fake2.txt", "mod/fake1.txt" }); result.Should().BeTrue(); } @@ -4517,30 +3904,20 @@ public async Task Patch_mod_should_need_update_when_overwritten_sha_not_same() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Patch_mod_should_need_update_when_overwritten_sha_not_same", UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), - WorkshopDirectory = new List() { "C:\\fake" }, + WorkshopDirectory = new List { "C:\\fake" }, CustomModDirectory = string.Empty }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; + return new Mod { FileName = o.FileName, Name = o.Name }; }); - var collections = new List() + var collections = new List { - new ModCollection() - { - IsSelected = true, - Mods = new List() { "mod/fake1.txt", "mod/fake2.txt"}, - Name = "test", - Game = "Patch_mod_should_need_update_when_overwritten_sha_not_same" - } + new ModCollection { IsSelected = true, Mods = new List { "mod/fake1.txt", "mod/fake2.txt" }, Name = "test", Game = "Patch_mod_should_need_update_when_overwritten_sha_not_same" } }; storageProvider.Setup(s => s.GetModCollections()).Returns(() => { @@ -4548,24 +3925,32 @@ public async Task Patch_mod_should_need_update_when_overwritten_sha_not_same() }); modPatchExporter.Setup(p => p.GetPatchStateAsync(It.IsAny(), It.IsAny())).ReturnsAsync((ModPatchExporterParameters p, bool load) => { - var res = new PatchState() + var res = new PatchState { Conflicts = new List(), - OverwrittenConflicts = new List() { new Definition() { File = "1", Id = "test", Type = "events", Code = "ab", ModName = "1", OriginalFileName = "1" } }, - LoadOrder = new List() { "mod/fake2.txt", "mod/fake1.txt" } + OverwrittenConflicts = new List + { + new Definition + { + File = "1", + Id = "test", + Type = "events", + Code = "ab", + ModName = "1", + OriginalFileName = "1" + } + }, + LoadOrder = new List { "mod/fake2.txt", "mod/fake1.txt" } }; return res; }); - reader.Setup(p => p.GetFileInfo(It.IsAny(), It.IsAny())).Returns(new FileInfo() - { - ContentSHA = "2" - }); + reader.Setup(p => p.GetFileInfo(It.IsAny(), It.IsAny())).Returns(new FileInfo { ContentSHA = "2" }); modWriter.Setup(p => p.ModDirectoryExists(It.IsAny())).Returns((ModWriterParameters p) => { return false; }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var result = await service.PatchModNeedsUpdateAsync("colname", new List() { "mod/fake2.txt", "mod/fake1.txt" }); + var result = await service.PatchModNeedsUpdateAsync("colname", new List { "mod/fake2.txt", "mod/fake1.txt" }); result.Should().BeTrue(); } @@ -4584,55 +3969,49 @@ public async Task Patch_mod_should_need_update_when_load_order_not_same() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Patch_mod_should_need_update_when_load_order_not_same", UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), - WorkshopDirectory = new List() { "C:\\fake" }, + WorkshopDirectory = new List { "C:\\fake" }, CustomModDirectory = string.Empty }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; + return new Mod { FileName = o.FileName, Name = o.Name }; }); - var collections = new List() - { - new ModCollection() - { - IsSelected = true, - Mods = new List() { "mod/fake1.txt", "mod/fake2.txt"}, - Name = "test", - Game = "Patch_mod_should_need_update_when_load_order_not_same" - } - }; + var collections = new List { new ModCollection { IsSelected = true, Mods = new List { "mod/fake1.txt", "mod/fake2.txt" }, Name = "test", Game = "Patch_mod_should_need_update_when_load_order_not_same" } }; storageProvider.Setup(s => s.GetModCollections()).Returns(() => { return collections; }); modPatchExporter.Setup(p => p.GetPatchStateAsync(It.IsAny(), It.IsAny())).ReturnsAsync((ModPatchExporterParameters p, bool load) => { - var res = new PatchState() + var res = new PatchState { - Conflicts = new List() { new Definition() { File = "1", Id = "test", Type = "events", Code = "ab", ModName = "1" } }, + Conflicts = new List + { + new Definition + { + File = "1", + Id = "test", + Type = "events", + Code = "ab", + ModName = "1" + } + }, OverwrittenConflicts = new List(), - LoadOrder = new List() { "mod/fake1.txt", "mod/fake2.txt" } + LoadOrder = new List { "mod/fake1.txt", "mod/fake2.txt" } }; return res; }); - reader.Setup(p => p.GetFileInfo(It.IsAny(), It.IsAny())).Returns(new FileInfo() - { - ContentSHA = "2" - }); + reader.Setup(p => p.GetFileInfo(It.IsAny(), It.IsAny())).Returns(new FileInfo { ContentSHA = "2" }); modWriter.Setup(p => p.ModDirectoryExists(It.IsAny())).Returns((ModWriterParameters p) => { return false; }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var result = await service.PatchModNeedsUpdateAsync("colname", new List() { "mod/fake2.txt", "mod/fake1.txt" }); + var result = await service.PatchModNeedsUpdateAsync("colname", new List { "mod/fake2.txt", "mod/fake1.txt" }); result.Should().BeTrue(); } @@ -4651,55 +4030,50 @@ public async Task Patch_mod_should_not_need_update() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Patch_mod_should_not_need_update", UserDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mod"), - WorkshopDirectory = new List() { "C:\\fake" }, + WorkshopDirectory = new List { "C:\\fake" }, CustomModDirectory = string.Empty }); mapper.Setup(s => s.Map(It.IsAny())).Returns((IModObject o) => { - return new Mod() - { - FileName = o.FileName, - Name = o.Name - }; + return new Mod { FileName = o.FileName, Name = o.Name }; }); - var collections = new List() - { - new ModCollection() - { - IsSelected = true, - Mods = new List() { "mod/fake1.txt", "mod/fake2.txt"}, - Name = "test", - Game = "Patch_mod_should_not_need_update" - } - }; + var collections = new List { new ModCollection { IsSelected = true, Mods = new List { "mod/fake1.txt", "mod/fake2.txt" }, Name = "test", Game = "Patch_mod_should_not_need_update" } }; storageProvider.Setup(s => s.GetModCollections()).Returns(() => { return collections; }); modPatchExporter.Setup(p => p.GetPatchStateAsync(It.IsAny(), It.IsAny())).ReturnsAsync((ModPatchExporterParameters p, bool load) => { - var res = new PatchState() + var res = new PatchState { - Conflicts = new List() { new Definition() { File = "1", Id = "test", Type = "events", Code = "ab", ModName = "1", ContentSHA = "1" } }, + Conflicts = new List + { + new Definition + { + File = "1", + Id = "test", + Type = "events", + Code = "ab", + ModName = "1", + ContentSHA = "1" + } + }, OverwrittenConflicts = new List(), - LoadOrder = new List() { "mod/fake2.txt", "mod/fake1.txt" } + LoadOrder = new List { "mod/fake2.txt", "mod/fake1.txt" } }; return res; }); - reader.Setup(p => p.GetFileInfo(It.IsAny(), It.IsAny())).Returns(new FileInfo() - { - ContentSHA = "1" - }); + reader.Setup(p => p.GetFileInfo(It.IsAny(), It.IsAny())).Returns(new FileInfo { ContentSHA = "1" }); modWriter.Setup(p => p.ModDirectoryExists(It.IsAny())).Returns((ModWriterParameters p) => { return false; }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var result = await service.PatchModNeedsUpdateAsync("colname", new List() { "mod/fake2.txt", "mod/fake1.txt" }); + var result = await service.PatchModNeedsUpdateAsync("colname", new List { "mod/fake2.txt", "mod/fake1.txt" }); result.Should().BeFalse(); } @@ -4724,7 +4098,7 @@ public async Task LoadDefinitionContent_should_be_null_when_no_game() modPatchExporter.Setup(p => p.LoadDefinitionContentsAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult("test-response")); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, null); - var result = await service.LoadDefinitionContentsAsync(new Definition() { File = "test.txt" }, "test"); + var result = await service.LoadDefinitionContentsAsync(new Definition { File = "test.txt" }, "test"); result.Should().BeNullOrWhiteSpace(); } @@ -4745,18 +4119,14 @@ public async Task LoadDefinitionContent_should_be_null() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "LoadDefinitionContent_should_be_false_when_no_game", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "LoadDefinitionContent_should_be_false_when_no_game", UserDirectory = "C:\\Users\\Fake" }); modPatchExporter.Setup(p => p.LoadDefinitionContentsAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult("test-response")); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, null); var result = await service.LoadDefinitionContentsAsync(null, "test"); result.Should().BeNullOrWhiteSpace(); - result = await service.LoadDefinitionContentsAsync(new Definition() { File = "test.txt" }, string.Empty); + result = await service.LoadDefinitionContentsAsync(new Definition { File = "test.txt" }, string.Empty); result.Should().BeNullOrWhiteSpace(); } @@ -4777,15 +4147,11 @@ public async Task LoadDefinitionContent_should_not_be_null() var mapper = new Mock(); var modPatchExporter = new Mock(); SetupMockCase(reader, parserManager, modParser); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "LoadDefinitionContent_should_be_false_when_no_game", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "LoadDefinitionContent_should_be_false_when_no_game", UserDirectory = "C:\\Users\\Fake" }); modPatchExporter.Setup(p => p.LoadDefinitionContentsAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult("test-response")); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, null); - var result = await service.LoadDefinitionContentsAsync(new Definition() { File = "test.txt" }, "test"); + var result = await service.LoadDefinitionContentsAsync(new Definition { File = "test.txt" }, "test"); result.Should().Be("test-response"); } @@ -4853,7 +4219,7 @@ public async Task Should_have_game_files_included() var modPatchExporter = new Mock(); modPatchExporter.Setup(p => p.GetPatchStateAsync(It.IsAny(), It.IsAny())).ReturnsAsync((ModPatchExporterParameters p, bool load) => { - var res = new PatchState() + var res = new PatchState { Conflicts = new List(), ResolvedConflicts = new List(), @@ -4863,11 +4229,7 @@ public async Task Should_have_game_files_included() }; return res; }); - gameService.Setup(p => p.GetSelected()).Returns(new Game() - { - Type = "Should_have_game_files_included", - UserDirectory = "C:\\Users\\Fake" - }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Should_have_game_files_included", UserDirectory = "C:\\Users\\Fake" }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); var result = await service.PatchHasGameDefinitionsAsync("fake"); @@ -4908,10 +4270,7 @@ public void Should_ignore_game_mods() var mapper = new Mock(); var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var c = new ConflictResult() - { - IgnoredPaths = "" - }; + var c = new ConflictResult { IgnoredPaths = "" }; var result = service.ShouldIgnoreGameMods(c); result.Should().BeTrue(); } @@ -4931,10 +4290,7 @@ public void Should_not_ignore_game_mods() var mapper = new Mock(); var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var c = new ConflictResult() - { - IgnoredPaths = "--showGameMods" - }; + var c = new ConflictResult { IgnoredPaths = "--showGameMods" }; var result = service.ShouldIgnoreGameMods(c); result.Should().BeFalse(); } @@ -4973,10 +4329,7 @@ public void Should_not_toggle_ignore_game_mods() var mapper = new Mock(); var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var c = new ConflictResult() - { - IgnoredPaths = "" - }; + var c = new ConflictResult { IgnoredPaths = "" }; var result = service.ToggleIgnoreGameMods(c); result.Should().BeFalse(); c.IgnoredPaths.Should().Contain("--showGameMods"); @@ -4997,10 +4350,7 @@ public void Should_toggle_ignore_game_mods() var mapper = new Mock(); var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var c = new ConflictResult() - { - IgnoredPaths = "--showGameMods" - }; + var c = new ConflictResult { IgnoredPaths = "--showGameMods" }; var result = service.ToggleIgnoreGameMods(c); result.Should().BeTrue(); c.IgnoredPaths.Should().BeNullOrWhiteSpace(); @@ -5040,10 +4390,7 @@ public void Should_show_self_conflicts() var mapper = new Mock(); var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var c = new ConflictResult() - { - IgnoredPaths = "--showSelfConflicts" - }; + var c = new ConflictResult { IgnoredPaths = "--showSelfConflicts" }; var result = service.ShouldShowSelfConflicts(c); result.Should().BeTrue(); } @@ -5063,10 +4410,7 @@ public void Should_not_show_self_conflicts() var mapper = new Mock(); var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var c = new ConflictResult() - { - IgnoredPaths = "" - }; + var c = new ConflictResult { IgnoredPaths = "" }; var result = service.ShouldShowSelfConflicts(c); result.Should().BeFalse(); } @@ -5105,10 +4449,7 @@ public void Should_not_toggle_self_conflicts() var mapper = new Mock(); var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var c = new ConflictResult() - { - IgnoredPaths = "--showSelfConflicts" - }; + var c = new ConflictResult { IgnoredPaths = "--showSelfConflicts" }; var result = service.ToggleSelfModConflicts(c); result.Should().BeFalse(); c.IgnoredPaths.Should().BeNullOrWhiteSpace(); @@ -5129,10 +4470,7 @@ public void Should_toggle_self_conflicts() var mapper = new Mock(); var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var c = new ConflictResult() - { - IgnoredPaths = "" - }; + var c = new ConflictResult { IgnoredPaths = "" }; var result = service.ToggleSelfModConflicts(c); result.Should().BeTrue(); c.IgnoredPaths.Should().Contain("--showSelfConflicts"); @@ -5172,10 +4510,7 @@ public void Should_show_reset_conflicts() var mapper = new Mock(); var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var c = new ConflictResult() - { - IgnoredPaths = "--showResetConflicts" - }; + var c = new ConflictResult { IgnoredPaths = "--showResetConflicts" }; var result = service.ShouldShowResetConflicts(c); result.Should().BeTrue(); } @@ -5195,10 +4530,7 @@ public void Should_not_show_reset_conflicts() var mapper = new Mock(); var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var c = new ConflictResult() - { - IgnoredPaths = "" - }; + var c = new ConflictResult { IgnoredPaths = "" }; var result = service.ShouldShowResetConflicts(c); result.Should().BeFalse(); } @@ -5237,10 +4569,7 @@ public void Should_not_toggle_reset_conflicts() var mapper = new Mock(); var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var c = new ConflictResult() - { - IgnoredPaths = "--showResetConflicts" - }; + var c = new ConflictResult { IgnoredPaths = "--showResetConflicts" }; var result = service.ToggleShowResetConflicts(c); result.Should().BeFalse(); c.IgnoredPaths.Should().BeNullOrWhiteSpace(); @@ -5261,10 +4590,7 @@ public void Should_toggle_reset_conflicts() var mapper = new Mock(); var modPatchExporter = new Mock(); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); - var c = new ConflictResult() - { - IgnoredPaths = "" - }; + var c = new ConflictResult { IgnoredPaths = "" }; var result = service.ToggleShowResetConflicts(c); result.Should().BeTrue(); c.IgnoredPaths.Should().Contain("--showResetConflicts"); @@ -5285,7 +4611,7 @@ public void Should_return_bracket_count_result() var mapper = new Mock(); var modPatchExporter = new Mock(); var validateParser = new Mock(); - validateParser.Setup(p => p.GetBracketCount(It.IsAny(), It.IsAny())).Returns(new BracketValidateResult() { CloseBracketCount = 1, OpenBracketCount = 1 }); + validateParser.Setup(p => p.GetBracketCount(It.IsAny(), It.IsAny())).Returns(new BracketValidateResult { CloseBracketCount = 1, OpenBracketCount = 1 }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, null, validateParser); var result = service.GetBracketCount("test.txt", "test"); result.Should().NotBeNull(); @@ -5308,9 +4634,9 @@ public void Should_validate_definition_and_treat_as_invalid() var mapper = new Mock(); var modPatchExporter = new Mock(); var validateParser = new Mock(); - validateParser.Setup(p => p.Validate(It.IsAny())).Returns(new List() { new Definition() { ErrorMessage = "test" } }); + validateParser.Setup(p => p.Validate(It.IsAny())).Returns(new List { new Definition { ErrorMessage = "test" } }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, null, validateParser); - var result = service.Validate(new Definition() { ValueType = ValueType.Object }); + var result = service.Validate(new Definition { ValueType = ValueType.Object }); result.Should().NotBeNull(); result.IsValid.Should().BeFalse(); result.ErrorMessage.Should().Be("test"); @@ -5334,7 +4660,7 @@ public void Should_validate_definition_and_treat_as_valid() var validateParser = new Mock(); validateParser.Setup(p => p.Validate(It.IsAny())).Returns((IEnumerable)null); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, null, validateParser); - var result = service.Validate(new Definition() { ValueType = ValueType.Object }); + var result = service.Validate(new Definition { ValueType = ValueType.Object }); result.Should().NotBeNull(); result.IsValid.Should().BeTrue(); } @@ -5354,13 +4680,123 @@ public void Should_not_validate_binary_definition_and_treat_as_valid() var mapper = new Mock(); var modPatchExporter = new Mock(); var validateParser = new Mock(); - validateParser.Setup(p => p.Validate(It.IsAny())).Returns(new List() { new Definition() { ErrorMessage = "test" } }); + validateParser.Setup(p => p.Validate(It.IsAny())).Returns(new List { new Definition { ErrorMessage = "test" } }); var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter, null, validateParser); - var result = service.Validate(new Definition() { ValueType = ValueType.Binary }); + var result = service.Validate(new Definition { ValueType = ValueType.Binary }); result.Should().NotBeNull(); result.IsValid.Should().BeTrue(); } + + /// + /// Defines the test method Should_not_get_allowed_game_language_when_no_selected_game. + /// + [Fact] + public async Task Should_not_get_allowed_game_language_when_no_selected_game() + { + DISetup.SetupContainer(); + + var storageProvider = new Mock(); + var modParser = new Mock(); + var parserManager = new Mock(); + var reader = new Mock(); + var modWriter = new Mock(); + var gameService = new Mock(); + var mapper = new Mock(); + var modPatchExporter = new Mock(); + gameService.Setup(p => p.GetSelected()).Returns((IGame)null); + + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); + var result = await service.GetAllowedLanguagesAsync("fake"); + result.Should().BeNull(); + } + + + /// + /// Defines the test method Should_not_get_allowed_game_language_when_no_collection. + /// + [Fact] + public async Task Should_not_get_allowed_game_language_when_no_collection() + { + DISetup.SetupContainer(); + + var storageProvider = new Mock(); + var modParser = new Mock(); + var parserManager = new Mock(); + var reader = new Mock(); + var modWriter = new Mock(); + var gameService = new Mock(); + var mapper = new Mock(); + var modPatchExporter = new Mock(); + gameService.Setup(p => p.GetSelected()).Returns((IGame)null); + + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); + var result = await service.GetAllowedLanguagesAsync(null); + result.Should().BeNull(); + } + + + /// + /// Defines the test method Should_get_allowed_game_language. + /// + [Fact] + public async Task Should_get_allowed_game_language() + { + DISetup.SetupContainer(); + + var storageProvider = new Mock(); + var modParser = new Mock(); + var parserManager = new Mock(); + var reader = new Mock(); + var modWriter = new Mock(); + var gameService = new Mock(); + var mapper = new Mock(); + var modPatchExporter = new Mock(); + modPatchExporter.Setup(p => p.GetPatchStateAsync(It.IsAny(), It.IsAny())).ReturnsAsync((ModPatchExporterParameters p, bool load) => + { + var res = new PatchState + { + Conflicts = new List(), + ResolvedConflicts = new List(), + OverwrittenConflicts = new List(), + Mode = IO.Common.PatchStateMode.Default, + AllowedLanguages = new List { "test" } + }; + return res; + }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Should_get_allowed_game_language", UserDirectory = "C:\\Users\\Fake" }); + + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); + var result = await service.GetAllowedLanguagesAsync("fake"); + result.Count.Should().Be(1); + result.FirstOrDefault().Should().Be("test"); + } + + /// + /// Defines the test method Should_get_allowed_game_language_from_mode_text. + /// + [Fact] + public async Task Should_get_allowed_game_language_from_mode_text() + { + DISetup.SetupContainer(); + + var storageProvider = new Mock(); + var modParser = new Mock(); + var parserManager = new Mock(); + var reader = new Mock(); + var modWriter = new Mock(); + var gameService = new Mock(); + var mapper = new Mock(); + var modPatchExporter = new Mock(); + modPatchExporter.Setup(p => p.GetAllowedLanguagesAsync(It.IsAny())).ReturnsAsync((ModPatchExporterParameters p) => new List { "test_file" }); + gameService.Setup(p => p.GetSelected()).Returns(new Game { Type = "Should_get_patch_state", UserDirectory = "C:\\Users\\Fake" }); + + var service = GetService(storageProvider, modParser, parserManager, reader, mapper, modWriter, gameService, modPatchExporter); + var result = await service.GetAllowedLanguagesAsync("fake"); + result.Count.Should().Be(1); + result.FirstOrDefault().Should().Be("test_file"); + } + /// /// Defines the test method Stellaris_Performance_profiling. /// @@ -5374,11 +4810,11 @@ public async Task Stellaris_Performance_profiling() { DISetup.SetupContainer(); - var registration = new Services.Registrations.GameRegistration(); + var registration = new Registrations.GameRegistration(); registration.OnPostStartup(); var game = DISetup.Container.GetInstance().Get().First(s => s.Type == "Stellaris"); var mods = await DISetup.Container.GetInstance().GetInstalledModsAsync(game); - var defs = await DISetup.Container.GetInstance().GetModObjectsAsync(game, mods, string.Empty, IronyModManager.Models.Common.PatchStateMode.Advanced); + var defs = await DISetup.Container.GetInstance().GetModObjectsAsync(game, mods, string.Empty, PatchStateMode.Advanced, null); } /// @@ -5386,12 +4822,13 @@ public async Task Stellaris_Performance_profiling() /// Implements the /// /// - private class DomainConfigDummy : Shared.Configuration.IDomainConfiguration + private class DomainConfigDummy : IDomainConfiguration { /// /// The domain /// - DomainConfigurationOptions domain = new(); + private readonly DomainConfigurationOptions domain = new(); + /// /// Initializes a new instance of the class. /// diff --git a/src/IronyModManager.Services/DIPackage.cs b/src/IronyModManager.Services/DIPackage.cs index daef7090a..8f30bfca8 100644 --- a/src/IronyModManager.Services/DIPackage.cs +++ b/src/IronyModManager.Services/DIPackage.cs @@ -4,15 +4,17 @@ // Created : 01-11-2020 // // Last Modified By : Mario -// Last Modified On : 05-14-2023 +// Last Modified On : 02-25-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; +using System.Linq; using IronyModManager.DI.Extensions; using IronyModManager.Services.Common; using IronyModManager.Shared; @@ -58,6 +60,7 @@ public void RegisterServices(Container container) container.Register(); container.Register(); container.Register(); + container.Register(); } #endregion Methods diff --git a/src/IronyModManager.Services/GameIndexService.cs b/src/IronyModManager.Services/GameIndexService.cs index a0ea53e04..e8797218e 100644 --- a/src/IronyModManager.Services/GameIndexService.cs +++ b/src/IronyModManager.Services/GameIndexService.cs @@ -1,17 +1,17 @@ - -// *********************************************************************** +// *********************************************************************** // Assembly : IronyModManager.Services // Author : Mario // Created : 05-27-2021 // // Last Modified By : Mario -// Last Modified On : 11-27-2023 +// Last Modified On : 02-25-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -29,6 +29,7 @@ using IronyModManager.Parser.Common.Mod; using IronyModManager.Services.Common; using IronyModManager.Services.Common.MessageBus; +using IronyModManager.Shared; using IronyModManager.Shared.Cache; using IronyModManager.Shared.MessageBus; using IronyModManager.Shared.Models; @@ -37,15 +38,25 @@ namespace IronyModManager.Services { - /// /// Class GameIndexService. /// Implements the - /// Implements the + /// Implements the /// /// - /// - public class GameIndexService : ModBaseService, IGameIndexService + /// + public class GameIndexService( + IMessageBus messageBus, + IParserManager parserManager, + IGameIndexer gameIndexer, + ICache cache, + IEnumerable definitionInfoProviders, + IReader reader, + IModWriter modWriter, + IModParser modParser, + IGameService gameService, + IStorageProvider storageProvider, + IMapper mapper) : ModBaseService(cache, definitionInfoProviders, reader, modWriter, modParser, gameService, storageProvider, mapper), IGameIndexService { #region Fields @@ -62,47 +73,20 @@ public class GameIndexService : ModBaseService, IGameIndexService /// /// The game indexer /// - private readonly IGameIndexer gameIndexer; + private readonly IGameIndexer gameIndexer = gameIndexer; /// /// The message bus /// - private readonly IMessageBus messageBus; + private readonly IMessageBus messageBus = messageBus; /// /// The parser manager /// - private readonly IParserManager parserManager; + private readonly IParserManager parserManager = parserManager; #endregion Fields - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The message bus. - /// The parser manager. - /// The game indexer. - /// The cache. - /// The definition information providers. - /// The reader. - /// The mod writer. - /// The mod parser. - /// The game service. - /// The storage provider. - /// The mapper. - public GameIndexService(IMessageBus messageBus, IParserManager parserManager, IGameIndexer gameIndexer, ICache cache, IEnumerable definitionInfoProviders, IReader reader, - IModWriter modWriter, IModParser modParser, IGameService gameService, IStorageProvider storageProvider, IMapper mapper) : - base(cache, definitionInfoProviders, reader, modWriter, modParser, gameService, storageProvider, mapper) - { - this.gameIndexer = gameIndexer; - this.messageBus = messageBus; - this.parserManager = parserManager; - } - - #endregion Constructors - #region Methods /// @@ -111,7 +95,7 @@ public GameIndexService(IMessageBus messageBus, IParserManager parserManager, IG /// The game. /// The versions. /// The indexed definitions. - /// true if XXXX, false otherwise. + /// true if indexed, false otherwise. public virtual async Task IndexDefinitionsAsync(IGame game, IEnumerable versions, IIndexedDefinitions indexedDefinitions) { if (game != null && versions != null && versions.Any()) @@ -122,15 +106,16 @@ public virtual async Task IndexDefinitionsAsync(IGame game, IEnumerable game.GameFolders.Any(x => p.StartsWith(x))); + files = files.Where(p => game.GameFolders.Any(p.StartsWith)); var indexedFolders = (await indexedDefinitions.GetAllDirectoryKeysAsync()).Select(p => p.ToLowerInvariant()); - var validFolders = files.Select(p => Path.GetDirectoryName(p)).GroupBy(p => p).Select(p => p.FirstOrDefault()).Where(p => indexedFolders.Any(a => a.ToLowerInvariant().Equals(p.ToLowerInvariant()))); + var validFolders = files.Select(Path.GetDirectoryName).GroupBy(p => p).Select(p => p.FirstOrDefault()).Where(p => indexedFolders.Any(a => a.ToLowerInvariant().Equals(p!.ToLowerInvariant()))); var folders = new List(); foreach (var item in validFolders) { @@ -139,7 +124,8 @@ public virtual async Task IndexDefinitionsAsync(IGame game, IEnumerable using var mutex = await asyncServiceLock.LockAsync(); processed++; var perc = GetProgressPercentage(total, processed, 100); - if (perc != previousProgress) + if (perc.IsNotNearlyEqual(previousProgress)) { await messageBus.PublishAsync(new GameIndexProgressEvent(perc)); previousProgress = perc; } - GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized); - GC.WaitForPendingFinalizers(); - GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized); + + GCRunner.RunGC(GCCollectionMode.Optimized); mutex.Dispose(); } @@ -179,10 +164,13 @@ await Task.Run(async () => }); await Task.WhenAll(tasks); } + return true; } + return false; } + return false; } @@ -192,13 +180,52 @@ await Task.Run(async () => /// The mod definitions. /// The game. /// The versions. + /// The game languages. /// IIndexedDefinitions. - public virtual async Task LoadDefinitionsAsync(IIndexedDefinitions modDefinitions, IGame game, IEnumerable versions) + public virtual async Task LoadDefinitionsAsync(IIndexedDefinitions modDefinitions, IGame game, IEnumerable versions, IReadOnlyCollection gameLanguages) { if (game != null && versions != null && versions.Any() && await gameIndexer.GameVersionsSameAsync(GetStoragePath(), game, versions)) { var gameDefinitions = new ConcurrentBag(); var directories = await modDefinitions.GetAllDirectoryKeysAsync(); + // Kinda need to force insert localisation directory itself + if (directories.Any(p => p.StartsWith(Shared.Constants.LocalizationDirectory, StringComparison.OrdinalIgnoreCase)) && + !directories.Any(p => p.Equals(Shared.Constants.LocalizationDirectory, StringComparison.OrdinalIgnoreCase))) + { + var newDirs = new List(directories) { Shared.Constants.LocalizationDirectory }; + directories = newDirs; + } + + if (gameLanguages != null && gameLanguages.Count != 0) + { + var folders = gameLanguages.Select(p => p.Type[2..]); + var filtered = new List(); + foreach (var dir in directories) + { + // So far games that support CS have structure localisation -> l_english -> files though language id part is stripped (l_ part) + if (dir.StartsWith(Shared.Constants.LocalizationDirectory, StringComparison.OrdinalIgnoreCase)) + { + if (dir.StandardizeDirectorySeparator().Any(p => p == Path.DirectorySeparatorChar)) + { + if (folders.Any(p => dir!.EndsWith(p, StringComparison.OrdinalIgnoreCase))) + { + filtered.Add(dir); + } + } + else + { + filtered.Add(dir); + } + } + else + { + filtered.Add(dir); + } + } + + directories = filtered; + } + double processed = 0; double total = directories.Count(); double previousProgress = 0; @@ -211,11 +238,12 @@ public virtual async Task LoadDefinitionsAsync(IIndexedDefi var definitions = await Task.Run(async () => await gameIndexer.GetDefinitionsAsync(GetStoragePath(), game, directory)); if ((definitions?.Any()).GetValueOrDefault()) { - foreach (var def in definitions) + foreach (var def in definitions!) { def.ModName = game.Name; def.IsFromGame = true; } + return definitions; } } @@ -223,9 +251,10 @@ public virtual async Task LoadDefinitionsAsync(IIndexedDefi { semaphore.Release(); } + return null; }).ToList(); - while (tasks.Any()) + while (tasks.Count != 0) { var result = await Task.WhenAny(tasks); tasks.Remove(result); @@ -236,22 +265,24 @@ public virtual async Task LoadDefinitionsAsync(IIndexedDefi gameDefinitions.Add(item); } } + processed++; var perc = GetProgressPercentage(total, processed, 100); - if (perc != previousProgress) + if (perc.IsNotNearlyEqual(previousProgress)) { await messageBus.PublishAsync(new GameDefinitionLoadProgressEvent(perc)); previousProgress = perc; } - GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized); - GC.WaitForPendingFinalizers(); - GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized); + + GCRunner.RunGC(GCCollectionMode.Optimized); } + foreach (var item in gameDefinitions) { await modDefinitions.AddToMapAsync(item); } } + return modDefinitions; } @@ -273,6 +304,7 @@ protected virtual double GetProgressPercentage(double total, double processed, d { perc = maxPerc; } + return perc; } @@ -298,10 +330,11 @@ protected virtual IEnumerable ParseGameFiles(IGame game, IEnumerabl { return null; } + var definitions = new List(); foreach (var fileInfo in fileInfos) { - var fileDefs = parserManager.Parse(new ParserManagerArgs() + var fileDefs = parserManager.Parse(new ParserManagerArgs { ContentSHA = fileInfo.ContentSHA, File = Path.Combine(folder, fileInfo.FileName), @@ -313,6 +346,7 @@ protected virtual IEnumerable ParseGameFiles(IGame game, IEnumerabl MergeDefinitions(fileDefs); definitions.AddRange(fileDefs); } + return definitions; } diff --git a/src/IronyModManager.Services/GameLanguageService.cs b/src/IronyModManager.Services/GameLanguageService.cs new file mode 100644 index 000000000..4bcc225f5 --- /dev/null +++ b/src/IronyModManager.Services/GameLanguageService.cs @@ -0,0 +1,143 @@ +// *********************************************************************** +// Assembly : IronyModManager.Services +// Author : Mario +// Created : 02-25-2024 +// +// Last Modified By : Mario +// Last Modified On : 02-26-2024 +// *********************************************************************** +// +// Copyright (c) . All rights reserved. +// +// +// *********************************************************************** + +using System; +using System.Collections.Generic; +using System.Linq; +using AutoMapper; +using IronyModManager.Models.Common; +using IronyModManager.Services.Common; +using IronyModManager.Storage.Common; + +namespace IronyModManager.Services +{ + /// + /// The game language service. + /// + /// + /// + /// The storage provider. + /// The mapper. + /// The preferences service. + /// Initializes a new instance of the class. + public class GameLanguageService(IStorageProvider storageProvider, IMapper mapper, IPreferencesService preferencesService) : BaseService(storageProvider, mapper), IGameLanguageService + { + #region Fields + + /// + /// A private string named defaultLanguage. + /// + private const string DefaultLanguage = "l_default"; + + /// + /// A private static object named objLock. + /// + private static readonly object objLock = new(); + + /// + /// A private readonly IPreferencesService named preferencesService. + /// + private readonly IPreferencesService preferencesService = preferencesService; + + #endregion Fields + + #region Methods + + /// + /// Get. + /// + /// A list of IGameLanguages. + public IEnumerable Get() + { + var result = new List(); + var prefs = preferencesService.Get(); + var allSelected = !prefs.ConflictSolverLanguagesSet; + var selectedLanguages = preferencesService.Get().ConflictSolverLanguages; + var locales = Parser.Common.Constants.Localization.Locales.Where(p => p != DefaultLanguage); + foreach (var locale in locales) + { + var model = GetModelInstance(); + InitModel(model, locale, selectedLanguages, allSelected); + result.Add(model); + } + + return result; + } + + /// + /// Gets the by abrv. + /// + /// The languages. + /// IReadOnlyCollection<IGameLanguage>. + /// + public IReadOnlyCollection GetByAbrv(IReadOnlyCollection languages) + { + var models = Get(); + var filtered = new List(); + if (languages != null) + { + foreach (var lang in models) + { + if (languages.Any(p => p.Equals(lang.Type))) + { + lang.IsSelected = true; + filtered.Add(lang); + } + } + } + + return filtered; + } + + /// + /// Get selected. + /// + /// A read only collection of IGameLanguages. + public IReadOnlyCollection GetSelected() + { + return Get().Where(p => p.IsSelected).ToList(); + } + + /// + /// Save. + /// + /// The languages. + public void Save(IEnumerable languages) + { + lock (objLock) + { + var preferences = preferencesService.Get(); + preferences.ConflictSolverLanguagesSet = true; + preferences.ConflictSolverLanguages = languages.Where(p => p.IsSelected).Select(p => p.Type).ToList(); + preferencesService.Save(preferences); + } + } + + /// + /// Init model. + /// + /// The language. + /// The type. + /// A list of strings + /// if set to true [all selected]. + private void InitModel(IGameLanguage language, string type, List selected, bool allSelected) + { + language.Type = type; + language.IsSelected = selected.Contains(type) || allSelected; + language.DisplayName = type; + } + + #endregion Methods + } +} diff --git a/src/IronyModManager.Services/IronyModManager.Services.csproj b/src/IronyModManager.Services/IronyModManager.Services.csproj index e0f95c0fb..e52900452 100644 --- a/src/IronyModManager.Services/IronyModManager.Services.csproj +++ b/src/IronyModManager.Services/IronyModManager.Services.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 Irony Mod Manager Services Component Mario LICENSE @@ -45,7 +45,7 @@ - + diff --git a/src/IronyModManager.Services/ModBaseService.cs b/src/IronyModManager.Services/ModBaseService.cs index a1a6fa733..57bcfce7c 100644 --- a/src/IronyModManager.Services/ModBaseService.cs +++ b/src/IronyModManager.Services/ModBaseService.cs @@ -1,11 +1,10 @@ - -// *********************************************************************** +// *********************************************************************** // Assembly : IronyModManager.Services // Author : Mario // Created : 04-07-2020 // // Last Modified By : Mario -// Last Modified On : 11-30-2023 +// Last Modified On : 03-04-2024 // *********************************************************************** // // Mario @@ -34,16 +33,25 @@ using IronyModManager.Shared.Cache; using IronyModManager.Shared.Models; using IronyModManager.Storage.Common; +using NaturalSort.Extension; using ValueType = IronyModManager.Shared.Models.ValueType; namespace IronyModManager.Services { - /// - /// Class ModBaseService. Implements the + /// Class ModBaseService. + /// Implements the /// /// - public abstract class ModBaseService : BaseService + public abstract class ModBaseService( + ICache cache, + IEnumerable definitionInfoProviders, + IReader reader, + IModWriter modWriter, + IModParser modParser, + IGameService gameService, + IStorageProvider storageProvider, + IMapper mapper) : BaseService(storageProvider, mapper) { #region Fields @@ -75,7 +83,7 @@ public abstract class ModBaseService : BaseService /// /// The path resolver /// - protected readonly IGameRootPathResolver pathResolver; + protected readonly IGameRootPathResolver PathResolver = new GameRootPathResolver(); /// /// The clean variables pattern @@ -84,71 +92,43 @@ public abstract class ModBaseService : BaseService #endregion Fields - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The cache. - /// The definition information providers. - /// The reader. - /// The mod writer. - /// The mod parser. - /// The game service. - /// The storage provider. - /// The mapper. - public ModBaseService(ICache cache, IEnumerable definitionInfoProviders, IReader reader, IModWriter modWriter, - IModParser modParser, IGameService gameService, - IStorageProvider storageProvider, IMapper mapper) : base(storageProvider, mapper) - { - Cache = cache; - DefinitionInfoProviders = definitionInfoProviders; - GameService = gameService; - Reader = reader; - ModParser = modParser; - ModWriter = modWriter; - pathResolver = new GameRootPathResolver(); - } - - #endregion Constructors - #region Properties /// /// Gets the cache. /// /// The cache. - protected ICache Cache { get; private set; } + protected ICache Cache { get; } = cache; /// /// Gets the definition information providers. /// /// The definition information providers. - protected IEnumerable DefinitionInfoProviders { get; private set; } + protected IEnumerable DefinitionInfoProviders { get; } = definitionInfoProviders; /// /// Gets the game service. /// /// The game service. - protected IGameService GameService { get; private set; } + protected IGameService GameService { get; } = gameService; /// /// Gets the mod parser. /// /// The mod parser. - protected IModParser ModParser { get; private set; } + protected IModParser ModParser { get; } = modParser; /// /// Gets the mod writer. /// /// The mod writer. - protected IModWriter ModWriter { get; private set; } + protected IModWriter ModWriter { get; } = modWriter; /// /// Gets the reader. /// /// The reader. - protected IReader Reader { get; private set; } + protected IReader Reader { get; } = reader; #endregion Properties @@ -166,9 +146,10 @@ protected virtual bool CheckIfModShouldBeLocked(IGame game, IMod mod) { var fullPath = mod.FullPath ?? string.Empty; return IsPatchModInternal(mod.Name) || (mod.Source == ModSource.Local && - (fullPath.EndsWith(Shared.Constants.ZipExtension, StringComparison.OrdinalIgnoreCase) || fullPath.EndsWith(Shared.Constants.BinExtension, StringComparison.OrdinalIgnoreCase)) && - (fullPath.StartsWith(game.UserDirectory) || fullPath.StartsWith(game.CustomModDirectory))); + (fullPath.EndsWith(Shared.Constants.ZipExtension, StringComparison.OrdinalIgnoreCase) || fullPath.EndsWith(Shared.Constants.BinExtension, StringComparison.OrdinalIgnoreCase)) && + (fullPath.StartsWith(game.UserDirectory) || fullPath.StartsWith(game.CustomModDirectory))); } + return false; } @@ -187,7 +168,7 @@ protected virtual IDefinition CopyDefinition(IDefinition definition) /// delete descriptors internal as an asynchronous operation. /// /// The mods. - /// true if XXXX, false otherwise. + /// true if deleted, false otherwise. protected virtual async Task DeleteDescriptorsInternalAsync(IEnumerable mods) { var game = GameService.GetSelected(); @@ -196,22 +177,20 @@ protected virtual async Task DeleteDescriptorsInternalAsync(IEnumerable(); foreach (var item in mods) { - var task = ModWriter.DeleteDescriptorAsync(new ModWriterParameters() - { - Mod = item, - RootDirectory = game.UserDirectory - }); + var task = ModWriter.DeleteDescriptorAsync(new ModWriterParameters { Mod = item, RootDirectory = game.UserDirectory }); tasks.Add(task); } + await Task.WhenAll(tasks); - Cache.Invalidate(new CacheInvalidateParameters() { Region = ModsCacheRegion, Prefix = game.Type, Keys = new List { GetModsCacheKey(true), GetModsCacheKey(false) } }); + Cache.Invalidate(new CacheInvalidateParameters { Region = ModsCacheRegion, Prefix = game.Type, Keys = [GetModsCacheKey(true), GetModsCacheKey(false)] }); return true; } + return false; } /// - /// Evals the definition priority internal. + /// Evaluates the definition priority internal. /// /// The definitions. /// if set to true [force fios]. @@ -232,7 +211,7 @@ protected virtual IPriorityDefinitionResult EvalDefinitionPriorityInternal(IEnum if (!noProvider) { // Handle localizations differently - var file = definitions.FirstOrDefault().File ?? string.Empty; + var file = definitions.FirstOrDefault()!.File ?? string.Empty; if (file.StartsWith(Shared.Constants.LocalizationDirectory)) { IEnumerable filtered = null; @@ -245,7 +224,7 @@ protected virtual IPriorityDefinitionResult EvalDefinitionPriorityInternal(IEnum } else { - var topPriority = replaceDefinitions.OrderByDescending(p => p.CustomPriorityOrder).FirstOrDefault().CustomPriorityOrder; + var topPriority = replaceDefinitions.MaxBy(p => p.CustomPriorityOrder)!.CustomPriorityOrder; filtered = replaceDefinitions.Where(p => p.CustomPriorityOrder == topPriority); } } @@ -257,28 +236,35 @@ protected virtual IPriorityDefinitionResult EvalDefinitionPriorityInternal(IEnum } else { - var topPriority = definitions.OrderByDescending(p => p.CustomPriorityOrder).FirstOrDefault().CustomPriorityOrder; + var topPriority = definitions.MaxBy(p => p.CustomPriorityOrder)!.CustomPriorityOrder; filtered = definitions.Where(p => p.CustomPriorityOrder == topPriority); } } + var uniqueDefinitions = filtered.GroupBy(p => p.ModName).Select(p => p.OrderBy(f => Path.GetFileNameWithoutExtension(f.File), StringComparer.Ordinal).Last()); - if (uniqueDefinitions.Count() == 1) - { - var definition = uniqueDefinitions.FirstOrDefault(p => !p.IsFromGame); - definition ??= uniqueDefinitions.FirstOrDefault(); - result.Definition = definition; - result.FileName = definition.File; - } - else if (uniqueDefinitions.Count() > 1) + switch (uniqueDefinitions.Count()) { - var modDefinitions = uniqueDefinitions.Where(p => !p.IsFromGame); - if (!modDefinitions.Any()) + case 1: { - definitions = uniqueDefinitions; + var definition = uniqueDefinitions.FirstOrDefault(p => !p.IsFromGame); + definition ??= uniqueDefinitions.FirstOrDefault(); + result.Definition = definition; + result.FileName = definition!.File; + break; + } + case > 1: + { + var modDefinitions = uniqueDefinitions.Where(p => !p.IsFromGame); + if (!modDefinitions.Any()) + { + definitions = uniqueDefinitions; + } + + var definition = modDefinitions.OrderBy(p => Path.GetFileNameWithoutExtension(p.File), StringComparer.Ordinal).Last(); + result.Definition = definition; + result.FileName = definition.File; + break; } - var definition = modDefinitions.OrderBy(p => Path.GetFileNameWithoutExtension(p.File), StringComparer.Ordinal).Last(); - result.Definition = definition; - result.FileName = definition.File; } } else @@ -288,123 +274,132 @@ protected virtual IPriorityDefinitionResult EvalDefinitionPriorityInternal(IEnum { validDefinitions = definitions.Where(d => !string.IsNullOrWhiteSpace(d.VirtualPath)).ToList(); } - if (validDefinitions.Count == 1) - { - result.Definition = validDefinitions.FirstOrDefault(); - // If it's the only valid one assume load order is responsible - result.PriorityType = DefinitionPriorityType.ModOrder; - result.FileName = validDefinitions.FirstOrDefault().File; - } - else if (validDefinitions.Count > 1) + switch (validDefinitions.Count) { - var definitionEvals = new List(); - bool isFios = false; - - bool overrideSkipped = false; - isFios = forceFios || provider.DefinitionUsesFIOSRules(validDefinitions.First()); - foreach (var item in validDefinitions) + case 1: + result.Definition = validDefinitions.FirstOrDefault(); + + // If it's the only valid one assume load order is responsible + result.PriorityType = DefinitionPriorityType.ModOrder; + result.FileName = validDefinitions.FirstOrDefault()!.File; + break; + case > 1: { - var fileName = isFios ? item.AdditionalFileNames.OrderBy(p => Path.GetFileNameWithoutExtension(p), StringComparer.Ordinal).First() : item.AdditionalFileNames.OrderBy(p => Path.GetFileNameWithoutExtension(p), StringComparer.Ordinal).Last(); - var hasOverrides = validDefinitions.Any(p => !p.IsCustomPatch && p.Dependencies != null && p.Dependencies.Any(d => d.Equals(item.ModName)) && - (isFios ? p.AdditionalFileNames.OrderBy(p => Path.GetFileNameWithoutExtension(p), StringComparer.Ordinal).First().Equals(fileName) : p.AdditionalFileNames.OrderBy(p => Path.GetFileNameWithoutExtension(p), StringComparer.Ordinal).Last().Equals(fileName))); - if (hasOverrides) - { - overrideSkipped = true; - continue; - } - definitionEvals.Add(new DefinitionEval() - { - Definition = item, - FileName = fileName - }); - } - List uniqueDefinitions; - if (isFios) - { - uniqueDefinitions = definitionEvals.GroupBy(p => p.Definition.ModName).Select(p => p.OrderBy(f => Path.GetFileNameWithoutExtension(f.FileName), StringComparer.Ordinal).First()).ToList(); - } - else - { - uniqueDefinitions = definitionEvals.GroupBy(p => p.Definition.ModName).Select(p => p.OrderBy(f => Path.GetFileNameWithoutExtension(f.FileName), StringComparer.Ordinal).Last()).ToList(); - } + var definitionEvals = new List(); + var isFios = false; - // Filter out game definitions which might have the same filename - var filteredGameDefinitions = false; - var gameDefinitions = uniqueDefinitions.GroupBy(p => p.FileNameCI).Where(p => p.Any(a => a.Definition.IsFromGame) && p.Count() > 1).SelectMany(p => p.Where(w => w.Definition.IsFromGame)); - if (gameDefinitions.Any()) - { - filteredGameDefinitions = true; - foreach (var gameDef in gameDefinitions) - { - uniqueDefinitions.Remove(gameDef); - } - } - if (uniqueDefinitions.Count == 1 && (overrideSkipped || filteredGameDefinitions)) - { - var definition = definitionEvals.FirstOrDefault(p => !p.Definition.IsFromGame); - definition ??= definitionEvals.FirstOrDefault(); - result.Definition = definition.Definition; - result.FileName = definition.FileName; - if (overrideSkipped) + var overrideSkipped = false; + isFios = forceFios || provider.DefinitionUsesFIOSRules(validDefinitions.First()); + foreach (var item in validDefinitions) { - result.PriorityType = DefinitionPriorityType.ModOverride; + var fileName = isFios + ? item.AdditionalFileNames.OrderBy(Path.GetFileNameWithoutExtension, StringComparer.Ordinal).First() + : item.AdditionalFileNames.OrderBy(Path.GetFileNameWithoutExtension, StringComparer.Ordinal).Last(); + var hasOverrides = validDefinitions.Any(p => !p.IsCustomPatch && p.Dependencies != null && p.Dependencies.Any(d => d.Equals(item.ModName)) && + (isFios + ? p.AdditionalFileNames.OrderBy(Path.GetFileNameWithoutExtension, StringComparer.Ordinal).First().Equals(fileName) + : p.AdditionalFileNames.OrderBy(Path.GetFileNameWithoutExtension, StringComparer.Ordinal).Last().Equals(fileName))); + if (hasOverrides) + { + overrideSkipped = true; + continue; + } + + definitionEvals.Add(new DefinitionEval { Definition = item, FileName = fileName }); } - else if (filteredGameDefinitions) + + var uniqueDefinitions = isFios + ? definitionEvals.GroupBy(p => p.Definition.ModName).Select(p => p.OrderBy(f => Path.GetFileNameWithoutExtension(f.FileName), StringComparer.Ordinal).First()).ToList() + : definitionEvals.GroupBy(p => p.Definition.ModName).Select(p => p.OrderBy(f => Path.GetFileNameWithoutExtension(f.FileName), StringComparer.Ordinal).Last()).ToList(); + + // Filter out game definitions which might have the same filename + var filteredGameDefinitions = false; + var gameDefinitions = uniqueDefinitions.GroupBy(p => p.FileNameCI).Where(p => p.Any(a => a.Definition.IsFromGame) && p.Count() > 1).SelectMany(p => p.Where(w => w.Definition.IsFromGame)); + if (gameDefinitions.Any()) { - result.PriorityType = DefinitionPriorityType.ModOrder; + filteredGameDefinitions = true; + foreach (var gameDef in gameDefinitions) + { + uniqueDefinitions.Remove(gameDef); + } } - } - else if (uniqueDefinitions.Count > 1) - { - // Has same filenames? - if (uniqueDefinitions.GroupBy(p => p.FileNameCI).Count() == 1) + + switch (uniqueDefinitions.Count) { - if (uniqueDefinitions.Any(p => p.Definition.IsCustomPatch)) + case 1 when overrideSkipped || filteredGameDefinitions: { - var definition = uniqueDefinitions.FirstOrDefault(p => p.Definition.IsCustomPatch); - result.Definition = definition.Definition; + var definition = definitionEvals.FirstOrDefault(p => !p.Definition.IsFromGame); + definition ??= definitionEvals.FirstOrDefault(); + result.Definition = definition!.Definition; result.FileName = definition.FileName; - result.PriorityType = DefinitionPriorityType.ModOrder; + if (overrideSkipped) + { + result.PriorityType = DefinitionPriorityType.ModOverride; + } + else if (filteredGameDefinitions) + { + result.PriorityType = DefinitionPriorityType.ModOrder; + } + + break; } - else + + // Has same filenames? + case > 1 when uniqueDefinitions.GroupBy(p => p.FileNameCI).Count() == 1: { - var definition = uniqueDefinitions.Last(); - result.Definition = definition.Definition; - result.FileName = definition.FileName; - result.PriorityType = DefinitionPriorityType.ModOrder; + if (uniqueDefinitions.Any(p => p.Definition.IsCustomPatch)) + { + var definition = uniqueDefinitions.FirstOrDefault(p => p.Definition.IsCustomPatch); + result.Definition = definition!.Definition; + result.FileName = definition.FileName; + result.PriorityType = DefinitionPriorityType.ModOrder; + } + else + { + var definition = uniqueDefinitions.Last(); + result.Definition = definition.Definition; + result.FileName = definition.FileName; + result.PriorityType = DefinitionPriorityType.ModOrder; + } + + break; } - } - else - { + // Using FIOS or LIOS? - if (isFios) + case > 1 when isFios: { var definition = uniqueDefinitions.OrderBy(p => Path.GetFileNameWithoutExtension(p.FileName), StringComparer.Ordinal).First(); result.Definition = definition.Definition; result.FileName = definition.FileName; result.PriorityType = DefinitionPriorityType.FIOS; + break; } - else + case > 1: { var definition = uniqueDefinitions.OrderBy(p => Path.GetFileNameWithoutExtension(p.FileName), StringComparer.Ordinal).Last(); result.Definition = definition.Definition; result.FileName = definition.FileName; result.PriorityType = DefinitionPriorityType.LIOS; + break; } } + + break; } } } } } + if (result.Definition == null) { var definition = definitions?.FirstOrDefault(p => !p.IsFromGame); if (definition == null && (definitions?.Any()).GetValueOrDefault()) { - definition = definitions.FirstOrDefault(); + definition = definitions!.FirstOrDefault(); } + result.Definition = definition; result.FileName = definition?.File; if (noProvider) @@ -412,6 +407,7 @@ protected virtual IPriorityDefinitionResult EvalDefinitionPriorityInternal(IEnum result.PriorityType = DefinitionPriorityType.NoProvider; } } + return result; } @@ -428,12 +424,14 @@ protected virtual string EvaluatePatchNamePath(IGame game, string patchName, str { modDirectoryRootPath = GetModDirectoryRootPath(game); } + modDirectoryRootPath = modDirectoryRootPath.StandardizeDirectorySeparator(); var patchNamePath = GetPatchModDirectory(game, patchName).StandardizeDirectorySeparator(); - if (Path.GetDirectoryName(patchNamePath).Equals(modDirectoryRootPath)) + if (Path.GetDirectoryName(patchNamePath)!.Equals(modDirectoryRootPath)) { patchNamePath = patchName; } + return patchNamePath; } @@ -458,14 +456,9 @@ protected virtual string GenerateCollectionPatchName(string collectionName) protected virtual IMod GeneratePatchModDescriptor(IEnumerable allMods, IGame game, string patchName) { var mod = DIResolver.Get(); - if (game.ModDescriptorType == ModDescriptorType.DescriptorMod) - { - mod.DescriptorFile = $"{Shared.Constants.ModDirectory}/{patchName}{Shared.Constants.ModExtension}"; - } - else - { - mod.DescriptorFile = $"{Shared.Constants.JsonModDirectory}/{patchName}{Shared.Constants.JsonExtension}"; - } + mod.DescriptorFile = game.ModDescriptorType == ModDescriptorType.DescriptorMod + ? $"{Shared.Constants.ModDirectory}/{patchName}{Shared.Constants.ModExtension}" + : $"{Shared.Constants.JsonModDirectory}/{patchName}{Shared.Constants.JsonExtension}"; mod.FileName = GetPatchModDirectory(game, patchName).Replace("\\", "/"); mod.Name = patchName; mod.Source = ModSource.Local; @@ -476,9 +469,10 @@ protected virtual IMod GeneratePatchModDescriptor(IEnumerable allMods, IGa } else { - mod.Version = allMods.OrderByDescending(p => p.VersionData).FirstOrDefault() != null ? allMods.OrderByDescending(p => p.VersionData).FirstOrDefault().Version : string.Empty; + mod.Version = allMods.MaxBy(p => p.VersionData) != null ? allMods.MaxBy(p => p.VersionData)!.Version : string.Empty; } - mod.Tags = new List() { "Fixes" }; + + mod.Tags = ["Fixes"]; mod.IsValid = true; mod.FullPath = mod.FileName.StandardizeDirectorySeparator(); return mod; @@ -493,14 +487,16 @@ protected virtual IEnumerable GetAllModCollectionsInternal() var game = GameService.GetSelected(); if (game == null) { - return new List(); + return []; } + var collections = StorageProvider.GetModCollections().Where(s => s.Game.Equals(game.Type)); if (collections.Any()) { - return collections.OrderBy(p => p.Name); + return collections.OrderBy(p => p.Name, StringComparer.OrdinalIgnoreCase.WithNaturalSort()); } - return new List(); + + return []; } /// @@ -516,21 +512,13 @@ protected virtual IEnumerable GetCollectionMods(IEnumerable mods = n var collections = GetAllModCollectionsInternal(); if (collections?.Count() > 0) { - IModCollection collection; - if (!string.IsNullOrWhiteSpace(collectionName)) - { - collection = collections.FirstOrDefault(p => p.Name.Equals(collectionName, StringComparison.OrdinalIgnoreCase)); - } - else - { - collection = collections.FirstOrDefault(p => p.IsSelected); - } + var collection = !string.IsNullOrWhiteSpace(collectionName) ? collections.FirstOrDefault(p => p.Name.Equals(collectionName, StringComparison.OrdinalIgnoreCase)) : collections.FirstOrDefault(p => p.IsSelected); if (collection != null) { var colMods = collection.Mods.ToList(); var colModPaths = collection.ModPaths.ToList(); - for (int i = 0; i < colMods.Count; i++) + for (var i = 0; i < colMods.Count; i++) { var item = colMods[i]; var mod = mods.FirstOrDefault(p => p.DescriptorFile.Equals(item, StringComparison.OrdinalIgnoreCase)); @@ -539,6 +527,7 @@ protected virtual IEnumerable GetCollectionMods(IEnumerable mods = n item = colModPaths[i]; mod = mods.FirstOrDefault(p => p.FullPath.Equals(item, StringComparison.OrdinalIgnoreCase)); } + if (mod != null) { collectionMods.Add(mod); @@ -546,6 +535,7 @@ protected virtual IEnumerable GetCollectionMods(IEnumerable mods = n } } } + return collectionMods; } @@ -566,14 +556,13 @@ protected virtual IEnumerable GetInstalledModsInternal(string game, bool i /// The game. /// if set to true [ignore patch mods]. /// IEnumerable<IMod>. + /// nameof(game) /// game protected virtual IEnumerable GetInstalledModsInternal(IGame game, bool ignorePatchMods) { - if (game == null) - { - throw new ArgumentNullException(nameof(game)); - } - var mods = Cache.Get>(new CacheGetParameters() { Region = ModsCacheRegion, Prefix = game.Type, Key = GetModsCacheKey(ignorePatchMods) }); + ArgumentNullException.ThrowIfNull(game); + + var mods = Cache.Get>(new CacheGetParameters { Region = ModsCacheRegion, Prefix = game.Type, Key = GetModsCacheKey(ignorePatchMods) }); if (mods != null) { return mods; @@ -591,22 +580,17 @@ protected virtual IEnumerable GetInstalledModsInternal(IGame game, bool ig { return; } + mod.Name = string.IsNullOrWhiteSpace(mod.Name) ? string.Empty : mod.Name; mod.Version = string.IsNullOrWhiteSpace(mod.Version) ? string.Empty : mod.Version; mod.IsLocked = installedMod.IsReadOnly; - if (game.ModDescriptorType == ModDescriptorType.DescriptorMod) - { - mod.DescriptorFile = $"{Shared.Constants.ModDirectory}/{installedMod.FileName}"; - } - else - { - mod.DescriptorFile = $"{Shared.Constants.JsonModDirectory}/{installedMod.FileName}"; - } + mod.DescriptorFile = game.ModDescriptorType == ModDescriptorType.DescriptorMod ? $"{Shared.Constants.ModDirectory}/{installedMod.FileName}" : $"{Shared.Constants.JsonModDirectory}/{installedMod.FileName}"; mod.Source = GetModSource(installedMod); if (mod.Source == ModSource.Paradox) { mod.RemoteId = GetPdxModId(installedMod.FileName); } + if (string.IsNullOrWhiteSpace(mod.FileName)) { mod.FileName = string.Empty; @@ -621,7 +605,7 @@ protected virtual IEnumerable GetInstalledModsInternal(IGame game, bool ig else { // Check user directory and workshop directory. - var userDirectoryMod = new List() { Path.Combine(game.CustomModDirectory, mod.FileName), Path.Combine(game.UserDirectory, mod.FileName) }.GroupBy(p => p).Select(p => p.First()); + var userDirectoryMod = new List { Path.Combine(game.CustomModDirectory, mod.FileName), Path.Combine(game.UserDirectory, mod.FileName) }.GroupBy(p => p).Select(p => p.First()); var workshopDirectoryMod = game.WorkshopDirectory.Select(p => Path.Combine(p, mod.FileName)).GroupBy(p => p).Select(p => p.First()); if (userDirectoryMod.Any(p => File.Exists(p) || Directory.Exists(p))) { @@ -640,7 +624,8 @@ protected virtual IEnumerable GetInstalledModsInternal(IGame game, bool ig result.Add(mod); }); } - Cache.Set(new CacheAddParameters>() { Region = ModsCacheRegion, Prefix = game.Type, Key = GetModsCacheKey(ignorePatchMods), Value = result.ToList() }); + + Cache.Set(new CacheAddParameters> { Region = ModsCacheRegion, Prefix = game.Type, Key = GetModsCacheKey(ignorePatchMods), Value = [.. result] }); return result; } } @@ -680,6 +665,7 @@ protected virtual ModSource GetModSource(IFileInfo fileInfo) { return ModSource.Steam; } + return ModSource.Local; } @@ -693,14 +679,12 @@ protected virtual string GetPatchModDirectory(IGame game, string patchOrMergeNam { var path = Path.Combine(game.UserDirectory, Shared.Constants.ModDirectory, patchOrMergeName); path = path.StandardizeDirectorySeparator(); - var parameters = new ModWriterParameters() - { - Path = path - }; + var parameters = new ModWriterParameters { Path = path }; if (!ModWriter.ModDirectoryExists(parameters) && !string.IsNullOrWhiteSpace(game.CustomModDirectory)) { path = Path.Combine(game.CustomModDirectory, patchOrMergeName).StandardizeDirectorySeparator(); } + return path; } @@ -740,6 +724,7 @@ protected virtual bool IsPatchModInternal(IMod mod) { return IsPatchModInternal(mod.Name); } + return false; } @@ -754,6 +739,7 @@ protected virtual bool IsPatchModInternal(string modName) { return modName.StartsWith(PatchCollectionName); } + return false; } @@ -765,9 +751,9 @@ protected virtual bool IsPatchModInternal(string modName) protected virtual bool IsValidDefinitionType(IDefinition definition) { return definition != null && definition.ValueType != ValueType.Variable && - definition.ValueType != ValueType.Namespace && - definition.ValueType != ValueType.Invalid && - definition.ValueType != ValueType.EmptyFile; + definition.ValueType != ValueType.Namespace && + definition.ValueType != ValueType.Invalid && + definition.ValueType != ValueType.EmptyFile; } /// @@ -781,6 +767,7 @@ static string cleanCodeForVarCheck(string code) code = code.ReplaceTabs().ReplaceNewLine(); return Regex.Replace(code, CleanVariablesPattern, " "); } + static bool evalNamespace(string code, string id) { var split = code.Split(Parser.Common.Constants.Scripts.EqualsOperator, StringSplitOptions.RemoveEmptyEntries); @@ -788,8 +775,10 @@ static bool evalNamespace(string code, string id) { return id.Trim().StartsWith(split[1].Trim(), StringComparison.OrdinalIgnoreCase); } + return true; } + static void appendLine(StringBuilder sb, IEnumerable lines) { if (lines != null && lines.Any()) @@ -797,11 +786,11 @@ static void appendLine(StringBuilder sb, IEnumerable lines) sb.AppendLine(string.Join(Environment.NewLine, lines)); } } + static string mergeCode(string codeTag, string separator, IEnumerable lines) { if (Shared.Constants.CodeSeparators.ClosingSeparators.Map.TryGetValue(separator, out var value)) { - var closingTag = value; var sb = new StringBuilder(); sb.AppendLine($"{codeTag} = {separator}"); foreach (var item in lines) @@ -812,7 +801,8 @@ static string mergeCode(string codeTag, string separator, IEnumerable li sb.AppendLine($"{new string(' ', 4)}{split}"); } } - sb.Append(closingTag); + + sb.Append(value); return sb.ToString(); } else @@ -827,13 +817,14 @@ static string mergeCode(string codeTag, string separator, IEnumerable li sb.AppendLine($"{new string(' ', 4)}{split}"); } } + return sb.ToString(); } } if (definitions != null && definitions.Any()) { - var otherDefinitions = definitions.Where(p => IsValidDefinitionType(p)); + var otherDefinitions = definitions.Where(IsValidDefinitionType); var variableDefinitions = definitions.Where(p => !IsValidDefinitionType(p)); if (variableDefinitions.Any()) { @@ -847,17 +838,18 @@ static string mergeCode(string codeTag, string separator, IEnumerable li { definition.Variables = allVars.ToList(); } + if (string.IsNullOrWhiteSpace(definition.CodeTag)) { - StringBuilder sb = new StringBuilder(); - appendLine(sb, namespaces.GroupBy(p => p.Code).Select(p => p.FirstOrDefault().Code)); - appendLine(sb, variables.GroupBy(p => p.Code).Select(p => p.FirstOrDefault().Code)); - appendLine(sb, new List { definition.Code }); + var sb = new StringBuilder(); + appendLine(sb, namespaces.GroupBy(p => p.Code).Select(p => p.FirstOrDefault()!.Code)); + appendLine(sb, variables.GroupBy(p => p.Code).Select(p => p.FirstOrDefault()!.Code)); + appendLine(sb, [definition.Code]); definition.Code = sb.ToString(); } else { - definition.Code = mergeCode(definition.CodeTag, definition.CodeSeparator, namespaces.Select(p => p.OriginalCode).Concat(variables.Select(p => p.OriginalCode)).Concat(new List() { definition.OriginalCode })); + definition.Code = mergeCode(definition.CodeTag, definition.CodeSeparator, namespaces.Select(p => p.OriginalCode).Concat(variables.Select(p => p.OriginalCode)).Concat([definition.OriginalCode])); } } } @@ -868,7 +860,7 @@ static string mergeCode(string codeTag, string separator, IEnumerable li /// populate mod files internal as an asynchronous operation. /// /// The mods. - /// true if XXXX, false otherwise. + /// true if populated, false otherwise. protected virtual async Task PopulateModFilesInternalAsync(IEnumerable mods) { var logger = DIResolver.Get(); @@ -879,7 +871,7 @@ protected virtual async Task PopulateModFilesInternalAsync(IEnumerable(); + mod.Files = []; } else { @@ -889,7 +881,8 @@ protected virtual async Task PopulateModFilesInternalAsync(IEnumerable @@ -898,18 +891,18 @@ protected virtual async Task PopulateModFilesInternalAsync(IEnumerable(); + localMod.Files = []; } else if (localMod.Files == null || !localMod.Files.Any()) { try { var files = Reader.GetFiles(localMod.FullPath); - localMod.Files = files ?? new List(); + localMod.Files = files ?? []; } catch (Exception ex) { - localMod.Files = new List(); + localMod.Files = []; logger.Error(ex); } finally @@ -924,8 +917,10 @@ protected virtual async Task PopulateModFilesInternalAsync(IEnumerable PopulateModFilesInternalAsync(IEnumerableIEnumerable<IDefinition>. protected virtual IEnumerable PopulateModPath(IDefinition definition, IEnumerable collectionMods) { - return PopulateModPath(new List() { definition }, collectionMods); + return PopulateModPath([definition], collectionMods); } /// @@ -958,14 +953,15 @@ protected virtual IEnumerable PopulateModPath(IEnumerable p.Name.Equals(item.ModName)).FullPath; + item.ModPath = collectionMods.FirstOrDefault(p => p.Name.Equals(item.ModName))!.FullPath; } } } + return definitions; } diff --git a/src/IronyModManager.Services/ModCollectionService.cs b/src/IronyModManager.Services/ModCollectionService.cs index daf4cf20f..3593695a7 100644 --- a/src/IronyModManager.Services/ModCollectionService.cs +++ b/src/IronyModManager.Services/ModCollectionService.cs @@ -1,17 +1,17 @@ - -// *********************************************************************** +// *********************************************************************** // Assembly : IronyModManager.Services // Author : Mario // Created : 03-04-2020 // // Last Modified By : Mario -// Last Modified On : 07-12-2023 +// Last Modified On : 03-04-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; using System.IO; @@ -33,15 +33,23 @@ namespace IronyModManager.Services { - /// - /// Class ModCollectionService. - /// Implements the - /// Implements the + /// The mod collection service. /// /// /// - public class ModCollectionService : ModBaseService, IModCollectionService + public class ModCollectionService( + IMessageBus messageBus, + IReportExportService exportService, + ICache cache, + IEnumerable definitionInfoProviders, + IReader reader, + IModWriter modWriter, + IModParser modParser, + IGameService gameService, + IModCollectionExporter modCollectionExporter, + IStorageProvider storageProvider, + IMapper mapper) : ModBaseService(cache, definitionInfoProviders, reader, modWriter, modParser, gameService, storageProvider, mapper), IModCollectionService { #region Fields @@ -53,48 +61,20 @@ public class ModCollectionService : ModBaseService, IModCollectionService /// /// The mod report exporter /// - private readonly IReportExportService exportService; + private readonly IReportExportService exportService = exportService; /// /// The message bus /// - private readonly IMessageBus messageBus; + private readonly IMessageBus messageBus = messageBus; /// /// The mod collection exporter /// - private readonly IModCollectionExporter modCollectionExporter; + private readonly IModCollectionExporter modCollectionExporter = modCollectionExporter; #endregion Fields - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The message bus. - /// The export service. - /// The cache. - /// The definition information providers. - /// The reader. - /// The mod writer. - /// The mod parser. - /// The game service. - /// The mod collection exporter. - /// The storage provider. - /// The mapper. - public ModCollectionService(IMessageBus messageBus, IReportExportService exportService, ICache cache, - IEnumerable definitionInfoProviders, IReader reader, IModWriter modWriter, - IModParser modParser, IGameService gameService, IModCollectionExporter modCollectionExporter, - IStorageProvider storageProvider, IMapper mapper) : base(cache, definitionInfoProviders, reader, modWriter, modParser, gameService, storageProvider, mapper) - { - this.messageBus = messageBus; - this.exportService = exportService; - this.modCollectionExporter = modCollectionExporter; - } - - #endregion Constructors - #region Enums /// @@ -143,6 +123,7 @@ public virtual IModCollection Create() { return null; } + var instance = GetModelInstance(); instance.Game = game.Type; return instance; @@ -160,6 +141,7 @@ public virtual bool Delete(string name) { return false; } + lock (serviceLock) { var collections = StorageProvider.GetModCollections().ToList(); @@ -173,6 +155,7 @@ public virtual bool Delete(string name) } } } + return false; } @@ -202,14 +185,16 @@ public virtual Task ExportAsync(string file, IModCollection modCollection, { return Task.FromResult(false); } + var collection = Mapper.Map(modCollection); if (string.IsNullOrWhiteSpace(collection.MergedFolderName) && exportMods) { collection.MergedFolderName = collection.Name.GenerateValidFileName(); } + var path = GetPatchModDirectory(game, modCollection); var modNameOverride = $"({collection.Name}) "; - var parameters = new ModCollectionExporterParams() + var parameters = new ModCollectionExporterParams { File = file, Mod = collection, @@ -243,28 +228,28 @@ public virtual async Task ExportHashReportAsync(IEnumerable mods, st { var modExport = mods.ToList(); var collection = GetAllModCollectionsInternal().FirstOrDefault(p => p.IsSelected); - var patchModName = GenerateCollectionPatchName(collection.Name); + var patchModName = GenerateCollectionPatchName(collection!.Name); var allMods = GetInstalledModsInternal(GameService.GetSelected(), false); var patchMod = allMods.FirstOrDefault(p => p.Name.Equals(patchModName)); if (patchMod == null) { var game = GameService.GetSelected(); - if (await ModWriter.ModDirectoryExistsAsync(new ModWriterParameters() - { - RootDirectory = GetPatchModDirectory(game, patchModName) - })) + if (await ModWriter.ModDirectoryExistsAsync(new ModWriterParameters { RootDirectory = GetPatchModDirectory(game, patchModName) })) { patchMod = GeneratePatchModDescriptor(allMods, game, patchModName); } } + if (patchMod != null && collection.PatchModEnabled) { modExport.Add(patchMod); } + await PopulateModFilesInternalAsync(modExport); var reports = await ParseReportAsync(modExport); return await exportService.ExportAsync(reports, path); } + return false; } @@ -281,8 +266,9 @@ public virtual Task ExportParadoxLauncher202110JsonAsync(string file, IMod { return Task.FromResult(false); } + var collection = Mapper.Map(modCollection); - var parameters = new ModCollectionExporterParams() + var parameters = new ModCollectionExporterParams { File = file, Mod = collection, @@ -306,8 +292,9 @@ public virtual Task ExportParadoxLauncherJsonAsync(string file, IModCollec { return Task.FromResult(false); } + var collection = Mapper.Map(modCollection); - var parameters = new ModCollectionExporterParams() + var parameters = new ModCollectionExporterParams { File = file, Mod = collection, @@ -330,12 +317,14 @@ public virtual IModCollection Get(string name) { return null; } + var collections = StorageProvider.GetModCollections(); if (collections?.Count() > 0) { var collection = collections.FirstOrDefault(c => c.Name.Equals(name, StringComparison.OrdinalIgnoreCase) && c.Game.Equals(game.Type)); return collection; } + return null; } @@ -360,18 +349,15 @@ public virtual async Task GetImportedCollectionDetailsAsync(stri { return null; } + var instance = GetModelInstance(); - var result = await modCollectionExporter.ImportAsync(new ModCollectionExporterParams() - { - File = file, - Mod = instance, - DescriptorType = MapDescriptorType(game.ModDescriptorType) - }); + var result = await modCollectionExporter.ImportAsync(new ModCollectionExporterParams { File = file, Mod = instance, DescriptorType = MapDescriptorType(game.ModDescriptorType) }); if (result != null) { MapImportResult(instance, result, false); return instance; } + return null; } @@ -387,6 +373,7 @@ public async Task ImportAsync(string file) { return null; } + var instance = await GetImportedCollectionDetailsAsync(file); if (instance != null) { @@ -395,21 +382,23 @@ public async Task ImportAsync(string file) { game = GameService.Get().FirstOrDefault(p => p.Type.Equals(instance.Game)); } + var path = GetPatchModDirectory(game, instance); var exportPath = GetPatchModDirectory(game, !string.IsNullOrWhiteSpace(instance.MergedFolderName) ? instance.MergedFolderName : instance.Name); - var result = await modCollectionExporter.ImportModDirectoryAsync(new ModCollectionExporterParams() + var result = await modCollectionExporter.ImportModDirectoryAsync(new ModCollectionExporterParams { File = file, ModDirectory = path, Mod = instance, ExportModDirectory = exportPath, - DescriptorType = MapDescriptorType(game.ModDescriptorType) + DescriptorType = MapDescriptorType(game!.ModDescriptorType) }); if (result) { return instance; } } + return null; } @@ -426,26 +415,26 @@ public virtual async Task> ImportHashReportAsync(IEnume { return null; } + var modExport = mods.ToList(); var collection = GetAllModCollectionsInternal().FirstOrDefault(p => p.IsSelected); - var patchModName = GenerateCollectionPatchName(collection.Name); + var patchModName = GenerateCollectionPatchName(collection!.Name); var allMods = GetInstalledModsInternal(GameService.GetSelected(), false); var patchMod = allMods.FirstOrDefault(p => p.Name.Equals(patchModName)); if (patchMod == null) { var game = GameService.GetSelected(); - if (await ModWriter.ModDirectoryExistsAsync(new ModWriterParameters() - { - RootDirectory = GetPatchModDirectory(game, patchModName) - })) + if (await ModWriter.ModDirectoryExistsAsync(new ModWriterParameters { RootDirectory = GetPatchModDirectory(game, patchModName) })) { patchMod = GeneratePatchModDescriptor(allMods, game, patchModName); } } + if (patchMod != null) { modExport.Add(patchMod); } + await PopulateModFilesInternalAsync(modExport); var currentReports = await ParseReportAsync(modExport); return exportService.CompareReports(currentReports.ToList(), importedReports.ToList()); @@ -503,6 +492,7 @@ public virtual Task ImportParadoxosAsync(string file) /// /// The collection. /// true if XXXX, false otherwise. + /// nameof(collection) /// collection public virtual bool Save(IModCollection collection) { @@ -510,11 +500,13 @@ public virtual bool Save(IModCollection collection) { throw new ArgumentNullException(nameof(collection)); } + var game = GameService.GetSelected(); if (game == null) { return false; } + lock (serviceLock) { var collections = StorageProvider.GetModCollections().ToList(); @@ -525,6 +517,7 @@ public virtual bool Save(IModCollection collection) { collections.Remove(existing); } + if (collection.IsSelected) { foreach (var item in collections.Where(p => p.Game.Equals(collection.Game) && p.IsSelected)) @@ -533,6 +526,7 @@ public virtual bool Save(IModCollection collection) } } } + collections.Add(collection); return StorageProvider.SetModCollections(collections); } @@ -556,6 +550,7 @@ protected virtual double GetProgressPercentage(double total, double processed, d { perc = maxPerc; } + return perc; } @@ -570,13 +565,7 @@ protected virtual async Task ImportModsAsync(ImportType importTy async Task performImport(IGame game) { var instance = Create(); - var parameters = new ModCollectionExporterParams() - { - ModDirectory = Path.Combine(game.UserDirectory, Shared.Constants.ModDirectory), - File = file, - Mod = instance, - DescriptorType = MapDescriptorType(game.ModDescriptorType) - }; + var parameters = new ModCollectionExporterParams { ModDirectory = Path.Combine(game.UserDirectory, Shared.Constants.ModDirectory), File = file, Mod = instance, DescriptorType = MapDescriptorType(game.ModDescriptorType) }; ICollectionImportResult result = null; switch (importType) { @@ -605,17 +594,17 @@ async Task performImport(IGame game) { DIResolver.Get().Error(ex); } - break; - default: break; } + if (result != null) { // Order of operations is very important here MapImportResult(instance, result, true); return instance; } + return null; } @@ -624,6 +613,7 @@ async Task performImport(IGame game) { return null; } + return await performImport(game); } @@ -644,6 +634,7 @@ protected virtual void MapImportResult(IModCollection modCollection, ICollection modCollection.Game = collectionGame.Type; } } + modCollection.IsSelected = importResult.IsSelected; modCollection.MergedFolderName = importResult.MergedFolderName; modCollection.ModNames = importResult.ModNames; @@ -662,8 +653,11 @@ protected virtual void MapImportResult(IModCollection modCollection, ICollection // Filtering as local mods don't have priority in case of paradox launcher json import var filteredMods = mods.GroupBy(p => p.RemoteId).Select(p => p.Any(o => o.Source != ModSource.Local) ? p.FirstOrDefault(o => o.Source != ModSource.Local) : p.FirstOrDefault()); - var collectionMods = filteredMods.Where(p => importResult.ModIds.Any(x => (x.ParadoxId.HasValue && x.ParadoxId.GetValueOrDefault() == p.RemoteId.GetValueOrDefault()) || (x.SteamId.HasValue && x.SteamId.GetValueOrDefault() == p.RemoteId.GetValueOrDefault()))). - OrderBy(p => sort.IndexOf(sort.FirstOrDefault(x => (x.ParadoxId.HasValue && x.ParadoxId.GetValueOrDefault() == p.RemoteId.GetValueOrDefault()) || (x.SteamId.HasValue && x.SteamId.GetValueOrDefault() == p.RemoteId.GetValueOrDefault())))).ToList(); + var collectionMods = filteredMods + .Where(p => importResult.ModIds.Any(x => + (x.ParadoxId.HasValue && x.ParadoxId.GetValueOrDefault() == p!.RemoteId.GetValueOrDefault()) || (x.SteamId.HasValue && x.SteamId.GetValueOrDefault() == p!.RemoteId.GetValueOrDefault()))).OrderBy(p => + sort.IndexOf(sort.FirstOrDefault(x => + (x.ParadoxId.HasValue && x.ParadoxId.GetValueOrDefault() == p.RemoteId.GetValueOrDefault()) || (x.SteamId.HasValue && x.SteamId.GetValueOrDefault() == p.RemoteId.GetValueOrDefault())))).ToList(); collectionMods = collectionMods.GroupBy(p => p.FullPath).Select(p => p.FirstOrDefault()).ToList(); modCollection.Mods = collectionMods.Select(p => p.DescriptorFile).ToList(); modCollection.ModNames = collectionMods.Select(p => p.Name).ToList(); @@ -676,8 +670,8 @@ protected virtual void MapImportResult(IModCollection modCollection, ICollection if (mods.Any()) { var sort = importResult.FullPaths.ToList(); - var collectionMods = mods.Where(p => importResult.FullPaths.Any(x => x.StandardizeDirectorySeparator().Equals(p.FullPath.StandardizeDirectorySeparator(), StringComparison.OrdinalIgnoreCase))). - OrderBy(p => sort.IndexOf(sort.FirstOrDefault(x => x.StandardizeDirectorySeparator().Equals(p.FullPath.StandardizeDirectorySeparator(), StringComparison.OrdinalIgnoreCase)))).ToList(); + var collectionMods = mods.Where(p => importResult.FullPaths.Any(x => x.StandardizeDirectorySeparator().Equals(p.FullPath.StandardizeDirectorySeparator(), StringComparison.OrdinalIgnoreCase))).OrderBy(p => + sort.IndexOf(sort.FirstOrDefault(x => x.StandardizeDirectorySeparator().Equals(p.FullPath.StandardizeDirectorySeparator(), StringComparison.OrdinalIgnoreCase)))).ToList(); collectionMods = collectionMods.GroupBy(p => p.FullPath).Select(p => p.FirstOrDefault()).ToList(); modCollection.Mods = collectionMods.Select(p => p.DescriptorFile).ToList(); modCollection.ModNames = collectionMods.Select(p => p.Name).ToList(); @@ -691,12 +685,13 @@ protected virtual void MapImportResult(IModCollection modCollection, ICollection if (mods.Any() && importResult.Descriptors != null && importResult.Descriptors.Any()) { var sort = importResult.Descriptors.ToList(); - var collectionMods = mods.Where(p => importResult.Descriptors.Any(x => x.Equals(p.DescriptorFile, StringComparison.OrdinalIgnoreCase))).OrderBy(p => sort.IndexOf(sort.FirstOrDefault(x => x.Equals(p.DescriptorFile, StringComparison.OrdinalIgnoreCase)))).ToList(); + var collectionMods = mods.Where(p => importResult.Descriptors.Any(x => x.Equals(p.DescriptorFile, StringComparison.OrdinalIgnoreCase))) + .OrderBy(p => sort.IndexOf(sort.FirstOrDefault(x => x.Equals(p.DescriptorFile, StringComparison.OrdinalIgnoreCase)))).ToList(); modCollection.ModPaths = collectionMods.Select(p => p.FullPath).ToList(); if (modCollection.ModPaths.Count() != modCollection.Mods.Count() && importResult.ModNames != null && importResult.ModNames.Any()) { sort = importResult.ModNames.ToList(); - collectionMods = mods.Where(p => importResult.ModNames.Any(x => x.Equals(p.Name))).OrderBy(p => sort.IndexOf(sort.FirstOrDefault(x => x.Equals(p.Name)))).ToList(); + collectionMods = [.. mods.Where(p => importResult.ModNames.Any(x => x.Equals(p.Name))).OrderBy(p => sort.IndexOf(sort.FirstOrDefault(x => x.Equals(p.Name))))]; collectionMods = collectionMods.GroupBy(p => p.FullPath).Select(p => p.FirstOrDefault()).ToList(); modCollection.ModPaths = collectionMods.Select(p => p.FullPath).ToList(); } @@ -719,22 +714,22 @@ protected virtual async Task> ParseReportAsync(IEnumera var total = mods.SelectMany(p => p.Files).Count(p => game.GameFolders.Any(a => p.StartsWith(a))); var progress = 0; double lastPercentage = 0; - int i = 0; - var tasks = new List>>(); + var i = 0; + var tasks = new List>>(); foreach (var mod in mods) { var report = GetModelInstance(); report.Name = mod.Name; report.ReportType = HashReportType.Collection; - int j = 0; + var j = 0; - foreach (var item in mod.Files.Where(p => game.GameFolders.Any(a => p.StartsWith(a)))) + foreach (var item in mod.Files.Where(p => game.GameFolders.Any(p.StartsWith))) { - string modPath = mod.FullPath; - string filePath = item; - int modIndex = i; - int fileIndex = j; + var modPath = mod.FullPath; + var filePath = item; + var modIndex = i; + var fileIndex = j; tasks.Add(Task.Run(() => { var info = Reader.GetFileInfo(modPath, filePath); @@ -745,34 +740,40 @@ protected virtual async Task> ParseReportAsync(IEnumera fileReport.Hash = info.ContentSHA; return new Tuple(modIndex, fileIndex, fileReport); } + return null; })); j++; } + reports.Add(report); reports2.Add(new IHashFileReport[j]); i++; } + while (tasks.Count > 0) { i = Task.WaitAny(tasks.ToArray()); - Tuple result = tasks[i].Result; + var result = tasks[i].Result; tasks.RemoveAt(i); if (result != null) { reports2[result.Item1][result.Item2] = result.Item3; } + progress++; var percentage = GetProgressPercentage(total, progress); - if (percentage != lastPercentage) + if (percentage.IsNotNearlyEqual(lastPercentage)) { await messageBus.PublishAsync(new ModReportExportEvent(1, percentage)); } + lastPercentage = percentage; } + for (i = 0; i < reports.Count; i++) { - reports[i].Reports = reports2[i].ToList(); + reports[i].Reports = [.. reports2[i]]; } return reports; diff --git a/src/IronyModManager.Services/ModPatchCollectionService.cs b/src/IronyModManager.Services/ModPatchCollectionService.cs index 38ba7ad44..7cfcf2189 100644 --- a/src/IronyModManager.Services/ModPatchCollectionService.cs +++ b/src/IronyModManager.Services/ModPatchCollectionService.cs @@ -1,17 +1,17 @@ - -// *********************************************************************** +// *********************************************************************** // Assembly : IronyModManager.Services // Author : Mario // Created : 05-26-2020 // // Last Modified By : Mario -// Last Modified On : 01-23-2024 +// Last Modified On : 02-25-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -47,15 +47,25 @@ namespace IronyModManager.Services { - /// - /// Class ModPatchCollectionService. - /// Implements the - /// Implements the + /// The mod patch collection service. /// - /// - /// - public class ModPatchCollectionService : ModBaseService, IModPatchCollectionService + /// + /// + public class ModPatchCollectionService( + ICache cache, + IMessageBus messageBus, + IParserManager parserManager, + IEnumerable definitionInfoProviders, + IModPatchExporter modPatchExporter, + IReader reader, + IModWriter modWriter, + IModParser modParser, + IGameService gameService, + IStorageProvider storageProvider, + IMapper mapper, + IValidateParser validateParser, + IParametrizedParser parametrizedParser) : ModBaseService(cache, definitionInfoProviders, reader, modWriter, modParser, gameService, storageProvider, mapper), IModPatchCollectionService { #region Fields @@ -137,22 +147,22 @@ public class ModPatchCollectionService : ModBaseService, IModPatchCollectionServ /// /// The message bus /// - private readonly IMessageBus messageBus; + private readonly IMessageBus messageBus = messageBus; /// /// The mod patch exporter /// - private readonly IModPatchExporter modPatchExporter; + private readonly IModPatchExporter modPatchExporter = modPatchExporter; /// /// The parametrized parser /// - private readonly IParametrizedParser parametrizedParser; + private readonly IParametrizedParser parametrizedParser = parametrizedParser; /// /// The parser manager /// - private readonly IParserManager parserManager; + private readonly IParserManager parserManager = parserManager; /// /// The search initialize lock @@ -162,41 +172,10 @@ public class ModPatchCollectionService : ModBaseService, IModPatchCollectionServ /// /// The validate parser /// - private readonly IValidateParser validateParser; + private readonly IValidateParser validateParser = validateParser; #endregion Fields - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The cache. - /// The message bus. - /// The parser manager. - /// The definition information providers. - /// The mod patch exporter. - /// The reader. - /// The mod writer. - /// The mod parser. - /// The game service. - /// The storage provider. - /// The mapper. - /// The validate parser. - /// The parametrized parser. - public ModPatchCollectionService(ICache cache, IMessageBus messageBus, IParserManager parserManager, IEnumerable definitionInfoProviders, - IModPatchExporter modPatchExporter, IReader reader, IModWriter modWriter, IModParser modParser, IGameService gameService, - IStorageProvider storageProvider, IMapper mapper, IValidateParser validateParser, IParametrizedParser parametrizedParser) : base(cache, definitionInfoProviders, reader, modWriter, modParser, gameService, storageProvider, mapper) - { - this.messageBus = messageBus; - this.parserManager = parserManager; - this.modPatchExporter = modPatchExporter; - this.validateParser = validateParser; - this.parametrizedParser = parametrizedParser; - } - - #endregion Constructors - #region Enums /// @@ -237,6 +216,7 @@ public virtual Task AddCustomModPatchAsync(IConflictResult conflictResult, { definition.ModName = GenerateCollectionPatchName(collectionName); } + return ExportModPatchDefinitionAsync(conflictResult, definition, collectionName, ExportType.Custom); } @@ -261,6 +241,7 @@ public virtual void AddModsToIgnoreList(IConflictResult conflictResult, IEnumera sb.AppendLine(line); } } + if (mods != null) { foreach (var item in mods) @@ -268,6 +249,7 @@ public virtual void AddModsToIgnoreList(IConflictResult conflictResult, IEnumera sb.AppendLine($"{ModNameIgnoreId}{item.ModName}{IgnoreRulesSeparator}{ModNameIgnoreCounterId}{(item.Count > 1 ? item.Count : 2)}"); } } + conflictResult.IgnoredPaths = sb.ToString().Trim(Environment.NewLine.ToCharArray()); } } @@ -296,17 +278,16 @@ public virtual async Task CleanPatchCollectionAsync(string collectionName) { return false; } + var patchName = GenerateCollectionPatchName(collectionName); var allMods = GetInstalledModsInternal(game, false).ToList(); var mod = allMods.FirstOrDefault(p => p.Name.Equals(patchName)); if (mod != null) { - await DeleteDescriptorsInternalAsync(new List { mod }); + await DeleteDescriptorsInternalAsync([mod]); } - return await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters() - { - RootDirectory = GetPatchModDirectory(game, patchName) - }, true); + + return await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters { RootDirectory = GetPatchModDirectory(game, patchName) }, true); } /// @@ -322,15 +303,16 @@ public Task CopyPatchCollectionAsync(string collectionName, string newColl { return Task.FromResult(false); } + var modDirRootPath = GetModDirectoryRootPath(game); var oldPatchName = GenerateCollectionPatchName(collectionName); var newPatchName = GenerateCollectionPatchName(newCollectionName); - return modPatchExporter.CopyPatchModAsync(new ModPatchExporterParameters() + return modPatchExporter.CopyPatchModAsync(new ModPatchExporterParameters { RootPath = modDirRootPath, ModPath = EvaluatePatchNamePath(game, oldPatchName, modDirRootPath), PatchPath = EvaluatePatchNamePath(game, newPatchName, modDirRootPath), - RenamePairs = new List>() { new(oldPatchName, newPatchName) } + RenamePairs = [new KeyValuePair(oldPatchName, newPatchName)] }); } @@ -348,11 +330,7 @@ public virtual async Task CreatePatchDefinitionAsync(IDefinition co var patch = Mapper.Map(copy); patch.UseSimpleValidation = false; patch.ModName = GenerateCollectionPatchName(collectionName); - var state = await modPatchExporter.GetPatchStateAsync(new ModPatchExporterParameters() - { - RootPath = GetModDirectoryRootPath(game), - PatchPath = EvaluatePatchNamePath(game, patch.ModName) - }); + var state = await modPatchExporter.GetPatchStateAsync(new ModPatchExporterParameters { RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patch.ModName) }); if (state != null && state.IndexedConflictHistory.Any() && state.IndexedConflictHistory.TryGetValue(copy.TypeAndId, out var value)) { var history = value.FirstOrDefault(); @@ -361,13 +339,15 @@ public virtual async Task CreatePatchDefinitionAsync(IDefinition co patch.Code = history.Code; } } + return patch; } + return null; } /// - /// Evals the definition priority. + /// Evaluate the definition priority. /// /// The definitions. /// IPriorityDefinitionResult. @@ -382,8 +362,9 @@ public virtual IPriorityDefinitionResult EvalDefinitionPriority(IEnumerableThe indexed definitions. /// The mod order. /// The patch state mode. + /// The allowed languages. /// IConflictResult. - public virtual async Task FindConflictsAsync(IIndexedDefinitions indexedDefinitions, IList modOrder, PatchStateMode patchStateMode) + public virtual async Task FindConflictsAsync(IIndexedDefinitions indexedDefinitions, IList modOrder, PatchStateMode patchStateMode, IReadOnlyCollection allowedLanguages) { var actualMode = patchStateMode; if (patchStateMode == PatchStateMode.ReadOnly) @@ -394,18 +375,19 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition { actualMode = PatchStateMode.AdvancedWithoutLocalization; } + var conflicts = new HashSet(); var fileConflictCache = new Dictionary(); var fileKeys = await indexedDefinitions.GetAllFileKeysAsync(); var typeAndIdKeys = await indexedDefinitions.GetAllTypeAndIdKeysAsync(); - var overwritten = (await indexedDefinitions.GetByValueTypeAsync(ValueType.OverwrittenObject)).Concat((await indexedDefinitions.GetByValueTypeAsync(ValueType.OverwrittenObjectSingleFile))); - var empty = (await indexedDefinitions.GetByValueTypeAsync(ValueType.EmptyFile)); + var overwritten = (await indexedDefinitions.GetByValueTypeAsync(ValueType.OverwrittenObject)).Concat(await indexedDefinitions.GetByValueTypeAsync(ValueType.OverwrittenObjectSingleFile)); + var empty = await indexedDefinitions.GetByValueTypeAsync(ValueType.EmptyFile); var allCount = (await indexedDefinitions.GetAllAsync()).Count(); double total = (allCount * 2) + typeAndIdKeys.Count() + (overwritten.GroupBy(p => p.TypeAndId).Count() * 2) + empty.Count(); double processed = 0; double previousProgress = 0; - messageBus.Publish(new ModDefinitionAnalyzeEvent(0)); + await messageBus.PublishAsync(new ModDefinitionAnalyzeEvent(0)); var stopWatch = new Stopwatch(); stopWatch.Start(); @@ -438,22 +420,26 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition copy.FileNameSuffix = item.FileNameSuffix; await indexedDefinitions.AddToMapAsync(copy); } + var fileNames = copy.AdditionalFileNames; foreach (var fileName in item.AdditionalFileNames) { fileNames.Add(fileName); } + copy.AdditionalFileNames = fileNames; } } + processed++; var perc = GetProgressPercentage(total, processed, 99.99); - if (perc != previousProgress) + if (perc.IsNotNearlyEqual(previousProgress)) { - messageBus.Publish(new ModDefinitionAnalyzeEvent(perc)); + await messageBus.PublishAsync(new ModDefinitionAnalyzeEvent(perc)); previousProgress = perc; } } + Debug.WriteLine("FindConflictsAsync Empty Files Parse: " + stopWatch.Elapsed.FormatElapsed()); stopWatch.Restart(); @@ -472,22 +458,26 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition var progressMutex = await opLock.LockAsync(); processed += definitions.Count(); var perc = GetProgressPercentage(total, processed, 99.99); - if (perc != previousProgress) + if (perc.IsNotNearlyEqual(previousProgress)) { - messageBus.Publish(new ModDefinitionAnalyzeEvent(perc)); + await messageBus.PublishAsync(new ModDefinitionAnalyzeEvent(perc)); previousProgress = perc; } + progressMutex.Dispose(); } + var syncMutex = await opLock.LockAsync(); foreach (var item in localConflicts) { conflicts.Add(item); } + foreach (var item in localFileConflictCache) { fileConflictCache.TryAdd(item.Key, item.Value); } + syncMutex.Dispose(); }); }); @@ -510,22 +500,26 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition var progressMutex = await opLock.LockAsync(); processed += definitions.Count(); var perc = GetProgressPercentage(total, processed, 99.99); - if (perc != previousProgress) + if (perc.IsNotNearlyEqual(previousProgress)) { - messageBus.Publish(new ModDefinitionAnalyzeEvent(perc)); + await messageBus.PublishAsync(new ModDefinitionAnalyzeEvent(perc)); previousProgress = perc; } + progressMutex.Dispose(); } + var syncMutex = await opLock.LockAsync(); foreach (var item in localConflicts) { conflicts.Add(item); } + foreach (var item in localFileConflictCache) { fileConflictCache.TryAdd(item.Key, item.Value); } + syncMutex.Dispose(); }); }); @@ -541,6 +535,7 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition { return Task.Run(async () => { + var cache = new Dictionary(); foreach (var typeId in type) { var typeLock = await opLock.LockAsync(); @@ -548,12 +543,22 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition typeLock.Dispose(); if (items.Any() && items.All(p => !p.ExistsInLastFile)) { - var fileLock = await opLock.LockAsync(); - var fileDefs = await indexedDefinitions.GetByFileAsync(items.FirstOrDefault().FileCI); - fileLock.Dispose(); - var lastMod = fileDefs.GroupBy(p => p.ModName).Select(p => p.First()).OrderByDescending(p => modOrder.IndexOf(p.ModName)).FirstOrDefault(); + IDefinition lastMod; + if (cache.TryGetValue(items.FirstOrDefault()!.FileCI, out var value)) + { + lastMod = value; + } + else + { + var fileLock = await opLock.LockAsync(); + var fileDefs = await indexedDefinitions.GetByFileAsync(items.FirstOrDefault()!.FileCI); + fileLock.Dispose(); + lastMod = fileDefs.GroupBy(p => p.ModName).Select(p => p.First()).MaxBy(p => modOrder.IndexOf(p.ModName)); + cache[items.FirstOrDefault()!.FileCI] = lastMod; + } + var copy = CopyDefinition(items.FirstOrDefault()); - copy.Dependencies = lastMod.Dependencies; + copy.Dependencies = lastMod!.Dependencies; copy.ModName = lastMod.ModName; copy.Code = copy.OriginalCode = Comments.GetEmptyCommentType(copy.File); copy.ContentSHA = lastMod.ContentSHA; @@ -568,6 +573,7 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition { fileNames.Add(fileName); } + copy.AdditionalFileNames = fileNames; copy.ExistsInLastFile = true; copy.IsFromGame = lastMod.IsFromGame; @@ -578,11 +584,12 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition conflicts.Add(copy); processed++; var perc = GetProgressPercentage(total, processed, 99.99); - if (perc != previousProgress) + if (perc.IsNotNearlyEqual(previousProgress)) { - messageBus.Publish(new ModDefinitionAnalyzeEvent(perc)); + await messageBus.PublishAsync(new ModDefinitionAnalyzeEvent(perc)); previousProgress = perc; } + mutex.Dispose(); } else @@ -590,11 +597,12 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition var mutex = await opLock.LockAsync(); processed++; var perc = GetProgressPercentage(total, processed, 99.99); - if (perc != previousProgress) + if (perc.IsNotNearlyEqual(previousProgress)) { - messageBus.Publish(new ModDefinitionAnalyzeEvent(perc)); + await messageBus.PublishAsync(new ModDefinitionAnalyzeEvent(perc)); previousProgress = perc; } + mutex.Dispose(); } } @@ -611,28 +619,19 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition foreach (var item in overwritten.GroupBy(p => p.TypeAndId)) { - if (!overwrittenSort.TryGetValue(item.FirstOrDefault().ParentDirectoryCI, out var value)) + if (!overwrittenSort.TryGetValue(item.FirstOrDefault()!.ParentDirectoryCI, out var value)) { - var all = (await indexedDefinitions.GetByParentDirectoryAsync(item.FirstOrDefault().ParentDirectoryCI)).Where(IsValidDefinitionType); + var all = (await indexedDefinitions.GetByParentDirectoryAsync(item.FirstOrDefault()!.ParentDirectoryCI)).Where(IsValidDefinitionType); var ordered = all.GroupBy(p => p.TypeAndId).Select(p => { var partialCopy = new List(); p.ToList().ForEach(x => partialCopy.Add(PartialDefinitionCopy(x, false))); var priority = EvalDefinitionPriorityInternal(partialCopy.OrderBy(x => modOrder.IndexOf(x.ModName)), true); - return new DefinitionOrderSort() - { - TypeAndId = priority.Definition.TypeAndId, - Order = priority.Definition.Order, - File = Path.GetFileNameWithoutExtension(priority.FileName) - }; + return new DefinitionOrderSort { TypeAndId = priority.Definition.TypeAndId, Order = priority.Definition.Order, File = Path.GetFileNameWithoutExtension(priority.FileName) }; }).GroupBy(p => p.File).OrderBy(p => p.Key, StringComparer.Ordinal).SelectMany(p => p.OrderBy(x => x.Order)).ToList(); - var fullyOrdered = ordered.Select(p => new DefinitionOrderSort() - { - TypeAndId = p.TypeAndId, - Order = ordered.IndexOf(p) - }).ToList(); + var fullyOrdered = ordered.Select(p => new DefinitionOrderSort { TypeAndId = p.TypeAndId, Order = ordered.IndexOf(p) }).ToList(); value = fullyOrdered; - overwrittenSort.Add(item.FirstOrDefault().ParentDirectoryCI, value); + overwrittenSort.Add(item.FirstOrDefault()!.ParentDirectoryCI, value); } var conflicted = await indexedConflicts.GetByTypeAndIdAsync(item.First().TypeAndId); @@ -649,35 +648,39 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition var all = item.Select(p => p); foreach (var def in all) { - var hasOverrides = all.Any(p => !p.IsCustomPatch && p.Dependencies != null && p.Dependencies.Any(p => p.Equals(def.ModName))); + var hasOverrides = all.Any(p => !p.IsCustomPatch && p.Dependencies != null && p.Dependencies.Any(s => s.Equals(def.ModName))); if (!hasOverrides || actualMode == PatchStateMode.Advanced) { valid.Add(def); } } + definitions = valid; definition = EvalDefinitionPriority(valid.OrderBy(p => modOrder.IndexOf(p.ModName))).Definition; } + if (!overwrittenDefs.ContainsKey(definition.TypeAndId)) { var newDefinition = CopyDefinition(definition); var ordered = value; - newDefinition.Order = ordered.FirstOrDefault(p => p.TypeAndId == newDefinition.TypeAndId).Order; + newDefinition.Order = ordered.FirstOrDefault(p => p.TypeAndId == newDefinition.TypeAndId)!.Order; if (!overwrittenSortExport.TryGetValue(newDefinition.ParentDirectoryCI, out var valueInner)) { - overwrittenSortExport.Add(newDefinition.ParentDirectoryCI, new List() { newDefinition }); + overwrittenSortExport.Add(newDefinition.ParentDirectoryCI, [newDefinition]); } else { valueInner.Add(newDefinition); } + overwrittenDefs.Add(definition.TypeAndId, Tuple.Create(newDefinition, definitions, definition)); } + processed++; var perc = GetProgressPercentage(total, processed, 99.99); - if (perc != previousProgress) + if (perc.IsNotNearlyEqual(previousProgress)) { - messageBus.Publish(new ModDefinitionAnalyzeEvent(perc)); + await messageBus.PublishAsync(new ModDefinitionAnalyzeEvent(perc)); previousProgress = perc; } } @@ -703,8 +706,9 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition { overwrittenFileNames.Add(file); } + newDefinition.OverwrittenFileNames = overwrittenFileNames.Distinct().ToList(); - newDefinition.DiskFile = provider.GetDiskFileName(newDefinition); + newDefinition.DiskFile = provider!.GetDiskFileName(newDefinition); var preserveOverwrittenFileName = oldFileName == newDefinition.File; newDefinition.File = provider.GetFileName(newDefinition); if (preserveOverwrittenFileName) @@ -714,14 +718,16 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition preservedOverwrittenFileName.Add(oldFileName); newDefinition.OverwrittenFileNames = preservedOverwrittenFileName; } + var mutex = await opLock.LockAsync(); processed++; var perc = GetProgressPercentage(total, processed, 99.99); - if (perc != previousProgress) + if (perc.IsNotNearlyEqual(previousProgress)) { - messageBus.Publish(new ModDefinitionAnalyzeEvent(perc)); + await messageBus.PublishAsync(new ModDefinitionAnalyzeEvent(perc)); previousProgress = perc; } + mutex.Dispose(); } }); @@ -731,9 +737,9 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition Debug.WriteLine("FindConflictsAsync Overwritten Objects Sort Parse: " + stopWatch.Elapsed.FormatElapsed()); stopWatch.Restart(); - messageBus.Publish(new ModDefinitionAnalyzeEvent(99.99)); + await messageBus.PublishAsync(new ModDefinitionAnalyzeEvent(99.99)); var groupedConflicts = conflicts.GroupBy(p => p.TypeAndId); - var filteredConclicts = new List(); + var filteredConflicts = new List(); foreach (var conflict in groupedConflicts.Where(p => p.Count() > 1)) { if (conflict.Any(p => p.IsPlaceholder)) @@ -750,7 +756,7 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition nonPlaceholders.Add(priority.Definition); if (nonPlaceholders.Count > 1) { - filteredConclicts.AddRange(nonPlaceholders); + filteredConflicts.AddRange(nonPlaceholders); } } else @@ -758,14 +764,14 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition var nonPlaceholders = conflict.Where(p => !p.IsPlaceholder); if (nonPlaceholders.Count() > 1) { - filteredConclicts.AddRange(nonPlaceholders); + filteredConflicts.AddRange(nonPlaceholders); } } } } else { - filteredConclicts.AddRange(conflict); + filteredConflicts.AddRange(conflict); } } @@ -773,9 +779,10 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition stopWatch.Restart(); var result = GetModelInstance(); + result.AllowedLanguages = allowedLanguages != null ? allowedLanguages.Select(l => l.Type).ToList() : []; result.Mode = patchStateMode; var conflictsIndexed = DIResolver.Get(); - await conflictsIndexed.InitMapAsync(filteredConclicts, true); + await conflictsIndexed.InitMapAsync(filteredConflicts, true); result.AllConflicts = indexedDefinitions; result.Conflicts = conflictsIndexed; var resolvedConflicts = DIResolver.Get(); @@ -793,7 +800,7 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition var customConflicts = DIResolver.Get(); await customConflicts.InitMapAsync(null, true); result.CustomConflicts = customConflicts; - messageBus.Publish(new ModDefinitionAnalyzeEvent(100)); + await messageBus.PublishAsync(new ModDefinitionAnalyzeEvent(100)); stopWatch.Stop(); Debug.WriteLine("FindConflictsAsync Init Result: " + stopWatch.Elapsed.FormatElapsed()); @@ -801,6 +808,36 @@ public virtual async Task FindConflictsAsync(IIndexedDefinition return result; } + /// + /// Gets an allowed languages async. + /// + /// The collection name. + /// A Task containing IReadOnlyCollection of strings. + public async Task> GetAllowedLanguagesAsync(string collectionName) + { + var game = GameService.GetSelected(); + if (game != null && !string.IsNullOrWhiteSpace(collectionName)) + { + var patchName = GenerateCollectionPatchName(collectionName); + var @params = new ModPatchExporterParameters { RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patchName) }; + var languages = await modPatchExporter.GetAllowedLanguagesAsync(@params); + if (languages != null) + { + return languages; + } + else + { + var state = await modPatchExporter.GetPatchStateAsync(@params, false); + if (state != null) + { + return state.AllowedLanguages != null ? [.. state.AllowedLanguages] : []; + } + } + } + + return null; + } + /// /// Gets the bracket count. /// @@ -823,7 +860,7 @@ public virtual IReadOnlyList GetIgnoredMods(IConflictRe if (conflictResult != null) { var ignoredPaths = conflictResult.IgnoredPaths ?? string.Empty; - var lines = ignoredPaths.SplitOnNewLine().Where(p => !p.Trim().StartsWith("#")); + var lines = ignoredPaths.SplitOnNewLine().Where(p => !p.Trim().StartsWith('#')); foreach (var line in lines) { var ignoreMod = ParseIgnoreModLine(line); @@ -833,6 +870,7 @@ public virtual IReadOnlyList GetIgnoredMods(IConflictRe } } } + return mods; } @@ -843,9 +881,11 @@ public virtual IReadOnlyList GetIgnoredMods(IConflictRe /// The mods. /// Name of the collection. /// The mode. + /// The allowed game languages. /// A Task<IIndexedDefinitions> representing the asynchronous operation. - /// Detected a mod which is potentially to large to parse. - public virtual async Task GetModObjectsAsync(IGame game, IEnumerable mods, string collectionName, PatchStateMode mode) + /// Detected a mod which is potentially too large to parse. + /// Detected a mod which is potentially too large to parse. + public virtual async Task GetModObjectsAsync(IGame game, IEnumerable mods, string collectionName, PatchStateMode mode, IReadOnlyCollection allowedGameLanguages) { if (game == null || mods == null || !mods.Any()) { @@ -853,12 +893,13 @@ public virtual async Task GetModObjectsAsync(IGame game, IE } var tooLargeMod = false; - mods.AsParallel().WithDegreeOfParallelism(MaxModsToProcessInParallel).ForAll((m) => + mods.AsParallel().WithDegreeOfParallelism(MaxModsToProcessInParallel).ForAll(m => { if (tooLargeMod) { return; } + var size = Reader.GetTotalSize(m.FullPath, Shared.Constants.TextExtensions); if (size > MaxAllowedSource) { @@ -868,7 +909,7 @@ public virtual async Task GetModObjectsAsync(IGame game, IE if (tooLargeMod) { - throw new ModTooLargeException("Detected a mod which is potentially to large to parse."); + throw new ModTooLargeException("Detected a mod which is potentially too large to parse."); } var definitions = new ConcurrentBag(); @@ -879,18 +920,21 @@ public virtual async Task GetModObjectsAsync(IGame game, IE // Don't need full implementation just BOM check var provider = DefinitionInfoProviders.FirstOrDefault(p => p.CanProcess(game.Type)); - messageBus.Publish(new ModDefinitionLoadEvent(0)); + await messageBus.PublishAsync(new ModDefinitionLoadEvent(0)); + var allowedLanguages = allowedGameLanguages; var gameFolders = game.GameFolders.ToList(); - if (mode == PatchStateMode.DefaultWithoutLocalization || mode == PatchStateMode.AdvancedWithoutLocalization || mode == PatchStateMode.ReadOnlyWithoutLocalization) + if (mode is PatchStateMode.DefaultWithoutLocalization or PatchStateMode.AdvancedWithoutLocalization or PatchStateMode.ReadOnlyWithoutLocalization) { gameFolders = gameFolders.Where(p => !p.StartsWith(Shared.Constants.LocalizationDirectory, StringComparison.OrdinalIgnoreCase)).ToList(); + + // Mode override + allowedGameLanguages = null; } - mods.AsParallel().WithDegreeOfParallelism(MaxModsToProcessInParallel).WithExecutionMode(ParallelExecutionMode.ForceParallelism).ForAll((m) => + mods.AsParallel().WithDegreeOfParallelism(MaxModsToProcessInParallel).WithExecutionMode(ParallelExecutionMode.ForceParallelism).ForAll(m => { - IEnumerable result = null; - result = ParseModFiles(game, Reader.Read(m.FullPath, gameFolders), m, provider); + var result = ParseModFiles(game, Reader.Read(m.FullPath, gameFolders), m, provider, allowedLanguages); if (result?.Count() > 0) { foreach (var item in result) @@ -898,18 +942,18 @@ public virtual async Task GetModObjectsAsync(IGame game, IE definitions.Add(item); } } + lock (serviceLock) { processed++; var perc = GetProgressPercentage(total, processed, 100); - if (perc != previousProgress) + if (perc.IsNotNearlyEqual(previousProgress)) { messageBus.Publish(new ModDefinitionLoadEvent(perc)); previousProgress = perc; } - GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized); - GC.WaitForPendingFinalizers(); - GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized); + + GCRunner.RunGC(GCCollectionMode.Optimized); } }); @@ -917,15 +961,11 @@ public virtual async Task GetModObjectsAsync(IGame game, IE processed = 0; // Stellaris only (so far) - total = provider.SupportsInlineScripts ? definitions.Count * 2 : definitions.Count; + total = provider!.SupportsInlineScripts ? definitions.Count * 2 : definitions.Count; previousProgress = 0; List prunedDefinitions; var patchName = GenerateCollectionPatchName(collectionName); - var state = await modPatchExporter.GetPatchStateAsync(new ModPatchExporterParameters() - { - RootPath = GetModDirectoryRootPath(game), - PatchPath = EvaluatePatchNamePath(game, patchName) - }); + var state = await modPatchExporter.GetPatchStateAsync(new ModPatchExporterParameters { RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patchName) }); // Process so far Giga related stuff for now. Scared what else might be valid for inline_scripts. List prunedInlineDefinitions; @@ -933,7 +973,7 @@ public virtual async Task GetModObjectsAsync(IGame game, IE { var tempIndex = DIResolver.Get(); await tempIndex.InitMapAsync(definitions); - prunedInlineDefinitions = new List(); + prunedInlineDefinitions = []; var reportedInlineErrors = new HashSet(); foreach (var item in definitions) { @@ -943,17 +983,17 @@ public virtual async Task GetModObjectsAsync(IGame game, IE addDefault = false; var path = Path.Combine(Parser.Common.Constants.Stellaris.InlineScripts, parametrizedParser.GetScriptPath(item.Code)); var pathCI = path.ToLowerInvariant(); - var files = (await tempIndex.GetByParentDirectoryAsync(Path.GetDirectoryName(path))).Where(p => Path.Combine(Path.GetDirectoryName(p.FileCI), Path.GetFileNameWithoutExtension(p.FileCI)).Equals(pathCI)).ToList(); - if (files.Any()) + var files = (await tempIndex.GetByParentDirectoryAsync(Path.GetDirectoryName(path))).Where(p => Path.Combine(Path.GetDirectoryName(p.FileCI)!, Path.GetFileNameWithoutExtension(p.FileCI)!).Equals(pathCI)).ToList(); + if (files.Count != 0) { var modOrder = mods.Select(p => p.Name).ToList(); - var priorityDefinition = EvalDefinitionPriority(files.OrderBy(p => modOrder.IndexOf(p.ModName)).ToHashSet()); - if (priorityDefinition != null && priorityDefinition.Definition != null) + var priorityDefinition = EvalDefinitionPriority([.. files.OrderBy(p => modOrder.IndexOf(p.ModName))]); + if (priorityDefinition is { Definition: not null }) { var parametrizedCode = parametrizedParser.Process(priorityDefinition.Definition.Code, item.Code); if (!string.IsNullOrWhiteSpace(parametrizedCode)) { - ValidationType validationType = ValidationType.Full; + var validationType = ValidationType.Full; if (item.UseSimpleValidation.GetValueOrDefault() || item.UseSimpleValidation == null) { validationType = MapValidationType(item); @@ -962,7 +1002,8 @@ public virtual async Task GetModObjectsAsync(IGame game, IE { validationType = MapValidationType(priorityDefinition.Definition); } - var results = parserManager.Parse(new ParserManagerArgs() + + var results = parserManager.Parse(new ParserManagerArgs { ContentSHA = item.ContentSHA, // Want original file sha id File = item.File, // To trigger right parser @@ -978,13 +1019,14 @@ public virtual async Task GetModObjectsAsync(IGame game, IE { MergeDefinitions(results.Concat(item.Variables)); } + if (results != null && results.Any()) { prunedInlineDefinitions.AddRange(results); } else if (!reportedInlineErrors.Contains($"{item.ModName} - {pathCI}")) { - // Could happen, will need mnually investigation though + // Could happen, will need manually investigation though var copy = CopyDefinition(item); copy.ValueType = ValueType.Invalid; copy.ErrorMessage = $"Inline script {path} failed to be processed. Please report to the author of Irony."; @@ -1004,37 +1046,39 @@ public virtual async Task GetModObjectsAsync(IGame game, IE reportedInlineErrors.Add($"{item.ModName} - {pathCI}"); } } + if (addDefault) { prunedInlineDefinitions.Add(item); } + processed++; var perc = GetProgressPercentage(total, processed, 100); - if (perc != previousProgress) + if (perc.IsNotNearlyEqual(previousProgress)) { await messageBus.PublishAsync(new ModDefinitionInvalidReplaceEvent(perc)); previousProgress = perc; } } + tempIndex.Dispose(); - GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized); - GC.WaitForPendingFinalizers(); - GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized); + GCRunner.RunGC(GCCollectionMode.Optimized); } else { - prunedInlineDefinitions = definitions.ToList(); + prunedInlineDefinitions = [.. definitions]; } + definitions.Clear(); definitions = null; if (state != null && state.CustomConflicts.Any()) { - prunedDefinitions = new List(); + prunedDefinitions = []; var customIndexed = DIResolver.Get(); await customIndexed.InitMapAsync(state.CustomConflicts); foreach (var item in prunedInlineDefinitions) { - bool addDefault = true; + var addDefault = true; if (item.ValueType == ValueType.Invalid) { var fileCodes = await customIndexed.GetByFileAsync(item.File); @@ -1048,7 +1092,7 @@ public virtual async Task GetModObjectsAsync(IGame game, IE var history = value.FirstOrDefault(); if (history != null && !string.IsNullOrWhiteSpace(history.Code)) { - fileDefs.AddRange(parserManager.Parse(new ParserManagerArgs() + fileDefs.AddRange(parserManager.Parse(new ParserManagerArgs { ContentSHA = item.ContentSHA, File = item.File, @@ -1060,25 +1104,29 @@ public virtual async Task GetModObjectsAsync(IGame game, IE } } } - if (fileDefs.Any()) + + if (fileDefs.Count != 0) { foreach (var def in fileDefs) { def.IsCustomPatch = true; } + addDefault = false; MergeDefinitions(fileDefs); prunedDefinitions.AddRange(fileDefs); } } } + if (addDefault) { prunedDefinitions.Add(item); } + processed++; var perc = GetProgressPercentage(total, processed, 100); - if (perc != previousProgress) + if (perc.IsNotNearlyEqual(previousProgress)) { await messageBus.PublishAsync(new ModDefinitionInvalidReplaceEvent(perc)); previousProgress = perc; @@ -1087,8 +1135,9 @@ public virtual async Task GetModObjectsAsync(IGame game, IE } else { - prunedDefinitions = prunedInlineDefinitions.ToList(); + prunedDefinitions = [.. prunedInlineDefinitions]; } + prunedInlineDefinitions.Clear(); prunedInlineDefinitions = null; @@ -1110,11 +1159,7 @@ public virtual async Task GetPatchStateModeAsync(string collecti if (game != null && !string.IsNullOrWhiteSpace(collectionName)) { var patchName = GenerateCollectionPatchName(collectionName); - var @params = new ModPatchExporterParameters() - { - RootPath = GetModDirectoryRootPath(game), - PatchPath = EvaluatePatchNamePath(game, patchName) - }; + var @params = new ModPatchExporterParameters { RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patchName) }; var mode = await modPatchExporter.GetPatchStateModeAsync(@params); if (mode.HasValue) { @@ -1129,6 +1174,7 @@ public virtual async Task GetPatchStateModeAsync(string collecti } } } + return PatchStateMode.None; } @@ -1155,19 +1201,18 @@ public virtual async Task InitializePatchStateAsync(IConflictRe var game = GameService.GetSelected(); double previousProgress = 0; var allowCleanup = conflictResult != null && conflictResult.Mode != PatchStateMode.ReadOnly && conflictResult.Mode != PatchStateMode.ReadOnlyWithoutLocalization; + async Task cleanSingleMergeFiles(string directory, string patchName) { if (!allowCleanup) { return; } + var patchModDir = GetPatchModDirectory(game, patchName); - await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters() - { - RootDirectory = patchModDir, - Path = directory - }); + await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters { RootDirectory = patchModDir, Path = directory }); } + async Task syncPatchFiles(IConflictResult conflicts, IEnumerable patchFiles, string patchName, int total, int processed, int maxProgress) { var patchModDir = GetPatchModDirectory(game, patchName); @@ -1183,43 +1228,40 @@ async Task syncPatchFiles(IConflictResult conflicts, IEnumerable pa { continue; } + if (!await conflicts.CustomConflicts.ExistsByFileAsync(file) && !await conflicts.OverwrittenConflicts.ExistsByFileAsync(file) && !await conflicts.ResolvedConflicts.ExistsByFileAsync(file)) { - cleaned = await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters() - { - RootDirectory = patchModDir, - Path = file - }); + cleaned = await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters { RootDirectory = patchModDir, Path = file }); } + if (!cleaned) { var resolved = await conflicts.ResolvedConflicts.GetByDiskFileAsync(file); if (resolved.Any()) { - var overwritten = await conflicts.OverwrittenConflicts.GetByTypeAndIdAsync(resolved.FirstOrDefault().TypeAndId); - if (overwritten.Any() && overwritten.FirstOrDefault().DiskFileCI != resolved.FirstOrDefault().DiskFileCI) + var overwritten = await conflicts.OverwrittenConflicts.GetByTypeAndIdAsync(resolved.FirstOrDefault()!.TypeAndId); + if (overwritten.Any() && overwritten.FirstOrDefault()!.DiskFileCI != resolved.FirstOrDefault()!.DiskFileCI) { - await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters() - { - RootDirectory = patchModDir, - Path = overwritten.FirstOrDefault().DiskFile - }); + await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters { RootDirectory = patchModDir, Path = overwritten.FirstOrDefault()!.DiskFile }); } } } } + processed++; var perc = GetProgressPercentage(total, processed, maxProgress); - if (previousProgress != perc) + if (previousProgress.IsNotNearlyEqual(perc)) { await messageBus.PublishAsync(new ModDefinitionPatchLoadEvent(perc)); previousProgress = perc; } } + return processed; } + async Task<(IIndexedDefinitions, int)> initAllIndexedDefinitions(IConflictResult conflictResult, int total, int processed, int maxProgress) { async void processedSearchItemHandler(object sender, ProcessedArgs args) @@ -1227,26 +1269,29 @@ async void processedSearchItemHandler(object sender, ProcessedArgs args) using var mutex = await searchInitLock.LockAsync(); processed++; var perc = GetProgressPercentage(total, processed, maxProgress); - if (previousProgress != perc) + if (previousProgress.IsNotNearlyEqual(perc)) { await messageBus.PublishAsync(new ModDefinitionPatchLoadEvent(perc)); previousProgress = perc; } + mutex.Dispose(); } var copy = DIResolver.Get(); var options = DIResolver.Get().GetOptions(); - string diskSearchPath = string.Empty; + var diskSearchPath = string.Empty; if (options.ConflictSolver.UseDiskSearch) { diskSearchPath = StorageProvider.GetRootStoragePath(); } + copy.UseSearch(diskSearchPath, nameof(conflictResult.AllConflicts)); if (options.ConflictSolver.UseHybridMemory) { copy.UseDiskStore(StorageProvider.GetRootStoragePath()); } + copy.SetAllowedType(AddToMapAllowedType.InvalidAndSpecial); var semaphore = new AsyncSemaphore(MaxDefinitionsToAdd); var searchDefinitions = new List(); @@ -1255,19 +1300,21 @@ async void processedSearchItemHandler(object sender, ProcessedArgs args) await semaphore.WaitAsync(); try { - IDefinition defCopy = item; + var defCopy = item; if (item.ValueType == ValueType.Invalid || item.IsSpecialFolder) { defCopy = PartialDefinitionCopy(item); } + await copy.AddToMapAsync(defCopy); if (defCopy.Tags != null && defCopy.Tags.Any() && !defCopy.IsFromGame) { searchDefinitions.Add(defCopy); } + processed++; var perc = GetProgressPercentage(total, processed, maxProgress); - if (previousProgress != perc) + if (previousProgress.IsNotNearlyEqual(perc)) { await messageBus.PublishAsync(new ModDefinitionPatchLoadEvent(perc)); previousProgress = perc; @@ -1287,23 +1334,16 @@ async void processedSearchItemHandler(object sender, ProcessedArgs args) if (game != null && conflictResult != null && !string.IsNullOrWhiteSpace(collectionName)) { - double perc = 0; + double perc; var patchName = GenerateCollectionPatchName(collectionName); await messageBus.PublishAsync(new ModDefinitionPatchLoadEvent(0)); - var state = await modPatchExporter.GetPatchStateAsync(new ModPatchExporterParameters() - { - RootPath = GetModDirectoryRootPath(game), - PatchPath = EvaluatePatchNamePath(game, patchName) - }); - var patchFiles = modPatchExporter.GetPatchFiles(new ModPatchExporterParameters() - { - RootPath = GetModDirectoryRootPath(game), - PatchPath = EvaluatePatchNamePath(game, patchName) - }); + var state = await modPatchExporter.GetPatchStateAsync(new ModPatchExporterParameters { RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patchName) }); + var patchFiles = modPatchExporter.GetPatchFiles(new ModPatchExporterParameters { RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patchName) }); foreach (var item in (await conflictResult.OverwrittenConflicts.GetAllAsync()).GroupBy(p => p.ParentDirectory)) { await cleanSingleMergeFiles(item.First().ParentDirectory, patchName); } + var all = await conflictResult.AllConflicts.GetAllAsync(); var total = patchFiles.Count() + all.Count() + all.Count(p => p.Tags != null && p.Tags.Any() && !p.IsFromGame); // Other all is the trie init counter if (state != null) @@ -1311,19 +1351,20 @@ async void processedSearchItemHandler(object sender, ProcessedArgs args) var resolvedConflicts = new List(state.ResolvedConflicts); var ignoredConflicts = new List(); total += state.Conflicts.Count() + (state.OverwrittenConflicts.Count() * 2) + 1; - int processed = 0; + var processed = 0; foreach (var item in state.Conflicts.GroupBy(p => p.TypeAndId)) { var files = ProcessPatchStateFiles(state, item, ref processed); var matchedConflicts = await FindPatchStateMatchedConflictsAsync(conflictResult.Conflicts, state, ignoredConflicts, item); await SyncPatchStateAsync(game, patchName, resolvedConflicts, item, files, matchedConflicts, !allowCleanup); perc = GetProgressPercentage(total, processed); - if (previousProgress != perc) + if (previousProgress.IsNotNearlyEqual(perc)) { await messageBus.PublishAsync(new ModDefinitionPatchLoadEvent(perc)); previousProgress = perc; } } + foreach (var item in state.OverwrittenConflicts.GroupBy(p => p.TypeAndId)) { processed += item.Count(); @@ -1340,10 +1381,11 @@ async void processedSearchItemHandler(object sender, ProcessedArgs args) files.RemoveAll(p => fileNames.Any(a => a.Equals(p, StringComparison.OrdinalIgnoreCase))); } } + var matchedConflicts = await conflictResult.OverwrittenConflicts.GetByTypeAndIdAsync(item.First().TypeAndId); - await SyncPatchStatesAsync(matchedConflicts, item, patchName, game, !allowCleanup, files.ToArray()); + await SyncPatchStatesAsync(matchedConflicts, item, patchName, game, !allowCleanup, [.. files]); perc = GetProgressPercentage(total, processed); - if (previousProgress != perc) + if (previousProgress.IsNotNearlyEqual(perc)) { await messageBus.PublishAsync(new ModDefinitionPatchLoadEvent(perc)); previousProgress = perc; @@ -1364,7 +1406,7 @@ async void processedSearchItemHandler(object sender, ProcessedArgs args) if (resolved.Any()) { definition = resolved.FirstOrDefault(); - definition.Order = item.Order; + definition!.Order = item.Order; definition.DiskFile = item.DiskFile; definition.File = item.File; definition.OverwrittenFileNames = item.OverwrittenFileNames; @@ -1377,6 +1419,7 @@ async void processedSearchItemHandler(object sender, ProcessedArgs args) } } } + var canExport = true; if (definition.ValueType == ValueType.OverwrittenObjectSingleFile) { @@ -1386,7 +1429,7 @@ async void processedSearchItemHandler(object sender, ProcessedArgs args) if (merged != null) { definition = PopulateModPath(merged, GetCollectionMods()).FirstOrDefault(); - alreadyMergedTypes.Add(definition.Type); + alreadyMergedTypes.Add(definition!.Type); } } else @@ -1394,19 +1437,18 @@ async void processedSearchItemHandler(object sender, ProcessedArgs args) canExport = false; } } + if (canExport && allowCleanup) { - await modPatchExporter.ExportDefinitionAsync(new ModPatchExporterParameters() + await modPatchExporter.ExportDefinitionAsync(new ModPatchExporterParameters { - Game = game.Type, - OverwrittenConflicts = new List() { definition }, - RootPath = GetModDirectoryRootPath(game), - PatchPath = EvaluatePatchNamePath(game, patchName) + Game = game.Type, OverwrittenConflicts = [definition], RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patchName) }); } + processed++; perc = GetProgressPercentage(total, processed); - if (previousProgress != perc) + if (previousProgress.IsNotNearlyEqual(perc)) { await messageBus.PublishAsync(new ModDefinitionPatchLoadEvent(perc)); previousProgress = perc; @@ -1429,6 +1471,7 @@ await modPatchExporter.ExportDefinitionAsync(new ModPatchExporterParameters() await customConflicts.InitMapAsync(state.CustomConflicts, true); conflicts.CustomConflicts = customConflicts; conflicts.Mode = conflictResult.Mode; + conflicts.AllowedLanguages = conflictResult.AllowedLanguages; var indexResult = await initAllIndexedDefinitions(conflictResult, total, processed, 100); conflictResult.AllConflicts.Dispose(); @@ -1440,7 +1483,7 @@ await modPatchExporter.ExportDefinitionAsync(new ModPatchExporterParameters() if (allowCleanup) { - await modPatchExporter.SaveStateAsync(new ModPatchExporterParameters() + await modPatchExporter.SaveStateAsync(new ModPatchExporterParameters { LoadOrder = GetCollectionMods(collectionName: collectionName).Select(p => p.DescriptorFile), Mode = MapPatchStateMode(conflicts.Mode), @@ -1452,9 +1495,11 @@ await modPatchExporter.SaveStateAsync(new ModPatchExporterParameters() CustomConflicts = await GetDefinitionOrDefaultAsync(conflicts.CustomConflicts), RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patchName), - HasGameDefinitions = await conflicts.AllConflicts.HasGameDefinitionsAsync() + HasGameDefinitions = await conflicts.AllConflicts.HasGameDefinitionsAsync(), + AllowedLanguages = conflicts.AllowedLanguages }); } + await messageBus.PublishAsync(new ModDefinitionPatchLoadEvent(100)); return conflicts; @@ -1476,11 +1521,12 @@ await modPatchExporter.SaveStateAsync(new ModPatchExporterParameters() if (resolved.Any()) { definition = resolved.FirstOrDefault(); - definition.Order = item.Order; + definition!.Order = item.Order; definition.DiskFile = item.DiskFile; definition.File = item.File; definition.OverwrittenFileNames = item.OverwrittenFileNames; } + var canExport = true; if (definition.ValueType == ValueType.OverwrittenObjectSingleFile) { @@ -1490,7 +1536,7 @@ await modPatchExporter.SaveStateAsync(new ModPatchExporterParameters() if (merged != null) { definition = PopulateModPath(merged, GetCollectionMods()).FirstOrDefault(); - alreadyMergedTypes.Add(definition.Type); + alreadyMergedTypes.Add(definition!.Type); } } else @@ -1498,22 +1544,21 @@ await modPatchExporter.SaveStateAsync(new ModPatchExporterParameters() canExport = false; } } + if (canExport && allowCleanup) { - if (await modPatchExporter.ExportDefinitionAsync(new ModPatchExporterParameters() - { - Game = game.Type, - OverwrittenConflicts = new List() { definition }, - RootPath = GetModDirectoryRootPath(game), - PatchPath = EvaluatePatchNamePath(game, patchName) - })) + if (await modPatchExporter.ExportDefinitionAsync(new ModPatchExporterParameters + { + Game = game.Type, OverwrittenConflicts = [definition], RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patchName) + })) { exportedConflicts = true; } } + processed++; perc = GetProgressPercentage(total, processed); - if (previousProgress != perc) + if (previousProgress.IsNotNearlyEqual(perc)) { await messageBus.PublishAsync(new ModDefinitionPatchLoadEvent(perc)); previousProgress = perc; @@ -1525,7 +1570,7 @@ await modPatchExporter.SaveStateAsync(new ModPatchExporterParameters() if (exportedConflicts && allowCleanup) { - await modPatchExporter.SaveStateAsync(new ModPatchExporterParameters() + await modPatchExporter.SaveStateAsync(new ModPatchExporterParameters { LoadOrder = GetCollectionMods(collectionName: collectionName).Select(p => p.DescriptorFile), Mode = MapPatchStateMode(conflictResult.Mode), @@ -1537,7 +1582,8 @@ await modPatchExporter.SaveStateAsync(new ModPatchExporterParameters() CustomConflicts = await GetDefinitionOrDefaultAsync(conflictResult.CustomConflicts), RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patchName), - HasGameDefinitions = await conflictResult.AllConflicts.HasGameDefinitionsAsync() + HasGameDefinitions = await conflictResult.AllConflicts.HasGameDefinitionsAsync(), + AllowedLanguages = conflictResult.AllowedLanguages }); } @@ -1550,7 +1596,8 @@ await modPatchExporter.SaveStateAsync(new ModPatchExporterParameters() return conflictResult; } - }; + } + return null; } @@ -1558,16 +1605,17 @@ await modPatchExporter.SaveStateAsync(new ModPatchExporterParameters() /// Invalidates the state of the patch mod. /// /// Name of the collection. - /// true if XXXX, false otherwise. + /// true if invalid, false otherwise. public virtual bool InvalidatePatchModState(string collectionName) { var game = GameService.GetSelected(); if (game != null) { var patchName = GenerateCollectionPatchName(collectionName); - Cache.Invalidate(new CacheInvalidateParameters() { Region = CacheRegion, Prefix = game.Type, Keys = new List() { patchName } }); + Cache.Invalidate(new CacheInvalidateParameters { Region = CacheRegion, Prefix = game.Type, Keys = [patchName] }); return true; } + return false; } @@ -1604,12 +1652,9 @@ public virtual Task LoadDefinitionContentsAsync(IDefinition definition, { return Task.FromResult(string.Empty); } + var patchName = GenerateCollectionPatchName(collectionName); - return modPatchExporter.LoadDefinitionContentsAsync(new ModPatchExporterParameters() - { - RootPath = GetModDirectoryRootPath(game), - PatchPath = EvaluatePatchNamePath(game, patchName) - }, definition.File); + return modPatchExporter.LoadDefinitionContentsAsync(new ModPatchExporterParameters { RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patchName) }, definition.File); } /// @@ -1623,16 +1668,13 @@ public virtual async Task PatchHasGameDefinitionsAsync(string collectionNa if (game != null && !string.IsNullOrWhiteSpace(collectionName)) { var patchName = GenerateCollectionPatchName(collectionName); - var state = await modPatchExporter.GetPatchStateAsync(new ModPatchExporterParameters() - { - RootPath = GetModDirectoryRootPath(game), - PatchPath = EvaluatePatchNamePath(game, patchName) - }, false); + var state = await modPatchExporter.GetPatchStateAsync(new ModPatchExporterParameters { RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patchName) }, false); if (state != null) { return state.HasGameDefinitions; } } + return false; } @@ -1644,44 +1686,38 @@ public virtual async Task PatchHasGameDefinitionsAsync(string collectionNa /// Task<System.Boolean>. public virtual async Task PatchModNeedsUpdateAsync(string collectionName, IReadOnlyCollection loadOrder) { - loadOrder ??= new List(); + loadOrder ??= []; + List mapEvalState(IEnumerable definitions) { var result = new List(); if ((definitions?.Any()).GetValueOrDefault()) { - result.AddRange(definitions.Where(p => !p.IsFromGame).Select(m => new EvalState() - { - ContentSha = m.ContentSHA, - FileName = m.OriginalFileName, - FallBackFileName = m.File, - ModName = m.ModName - })); + result.AddRange(definitions!.Where(p => !p.IsFromGame).Select(m => new EvalState { ContentSha = m.ContentSHA, FileName = m.OriginalFileName, FallBackFileName = m.File, ModName = m.ModName })); } + return result; } + async Task evalState(IGame game, string patchName) { - Cache.Set(new CacheAddParameters() { Region = CacheRegion, Prefix = game.Type, Key = patchName, Value = new PatchCollectionState() { CheckInProgress = true } }); + Cache.Set(new CacheAddParameters { Region = CacheRegion, Prefix = game.Type, Key = patchName, Value = new PatchCollectionState { CheckInProgress = true } }); var allMods = GetInstalledModsInternal(game, false); var mods = allMods.Where(p => loadOrder.Any(x => x.Equals(p.DescriptorFile))).ToList(); - var state = await modPatchExporter.GetPatchStateAsync(new ModPatchExporterParameters() - { - RootPath = GetModDirectoryRootPath(game), - PatchPath = EvaluatePatchNamePath(game, patchName) - }, false); + var state = await modPatchExporter.GetPatchStateAsync(new ModPatchExporterParameters { RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patchName) }, false); if (state == null) { - Cache.Set(new CacheAddParameters() { Region = CacheRegion, Prefix = game.Type, Key = patchName, Value = new PatchCollectionState() { NeedsUpdate = false, CheckInProgress = false } }); + Cache.Set(new CacheAddParameters { Region = CacheRegion, Prefix = game.Type, Key = patchName, Value = new PatchCollectionState { NeedsUpdate = false, CheckInProgress = false } }); return false; } // Check load order first if (!state.LoadOrder.SequenceEqual(loadOrder)) { - Cache.Set(new CacheAddParameters() { Region = CacheRegion, Prefix = game.Type, Key = patchName, Value = new PatchCollectionState() { NeedsUpdate = true, CheckInProgress = false } }); + Cache.Set(new CacheAddParameters { Region = CacheRegion, Prefix = game.Type, Key = patchName, Value = new PatchCollectionState { NeedsUpdate = true, CheckInProgress = false } }); return true; } + var conflicts = new List(); conflicts.AddRange(mapEvalState(state.Conflicts)); conflicts.AddRange(mapEvalState(state.OverwrittenConflicts)); @@ -1697,25 +1733,26 @@ async Task evalState(IGame game, string patchName) foreach (var item in groupedMods.GroupBy(p => p.FileName)) { var definition = item.FirstOrDefault(); - var mod = mods.FirstOrDefault(p => p.Name.Equals(definition.ModName)); + var mod = mods.FirstOrDefault(p => p.Name.Equals(definition!.ModName)); if (mod == null) { // Mod no longer in collection, needs refresh break further checks... - Cache.Set(new CacheAddParameters() { Region = CacheRegion, Prefix = game.Type, Key = patchName, Value = new PatchCollectionState() { NeedsUpdate = true, CheckInProgress = false } }); + Cache.Set(new CacheAddParameters { Region = CacheRegion, Prefix = game.Type, Key = patchName, Value = new PatchCollectionState { NeedsUpdate = true, CheckInProgress = false } }); return true; } else { - var info = Reader.GetFileInfo(mod.FullPath, definition.FileName); + var info = Reader.GetFileInfo(mod.FullPath, definition!.FileName); info ??= Reader.GetFileInfo(mod.FullPath, definition.FallBackFileName); if (info == null || !info.ContentSHA.Equals(definition.ContentSha)) { // File no longer in collection or content does not match, break further checks - Cache.Set(new CacheAddParameters() { Region = CacheRegion, Prefix = game.Type, Key = patchName, Value = new PatchCollectionState() { NeedsUpdate = true, CheckInProgress = false } }); + Cache.Set(new CacheAddParameters { Region = CacheRegion, Prefix = game.Type, Key = patchName, Value = new PatchCollectionState { NeedsUpdate = true, CheckInProgress = false } }); return true; } } } + return false; }, cancellationToken.Token); } @@ -1724,20 +1761,22 @@ async Task evalState(IGame game, string patchName) semaphore.Release(); } }).ToList(); - while (tasks.Any()) + while (tasks.Count != 0) { var task = await Task.WhenAny(tasks); tasks.Remove(task); var result = await task; if (result) { - cancellationToken.Cancel(); + await cancellationToken.CancelAsync(); return true; } } - Cache.Set(new CacheAddParameters() { Region = CacheRegion, Prefix = game.Type, Key = patchName, Value = new PatchCollectionState() { NeedsUpdate = false, CheckInProgress = false } }); + + Cache.Set(new CacheAddParameters { Region = CacheRegion, Prefix = game.Type, Key = patchName, Value = new PatchCollectionState { NeedsUpdate = false, CheckInProgress = false } }); return false; } + var game = GameService.GetSelected(); if (game != null && !string.IsNullOrWhiteSpace(collectionName)) { @@ -1746,21 +1785,23 @@ async Task evalState(IGame game, string patchName) { return false; } + var patchName = GenerateCollectionPatchName(collectionName); - var result = Cache.Get(new CacheGetParameters() { Region = CacheRegion, Prefix = game.Type, Key = patchName }); + var result = Cache.Get(new CacheGetParameters { Region = CacheRegion, Prefix = game.Type, Key = patchName }); if (result != null) { while (result.CheckInProgress) { // Since another check is queued, wait and periodically check if the task is done... await Task.Delay(10); - result = Cache.Get(new CacheGetParameters() { Region = CacheRegion, Prefix = game.Type, Key = patchName }); + result = Cache.Get(new CacheGetParameters { Region = CacheRegion, Prefix = game.Type, Key = patchName }); if (result == null) { await evalState(game, patchName); - result = Cache.Get(new CacheGetParameters() { Region = CacheRegion, Prefix = game.Type, Key = patchName }); + result = Cache.Get(new CacheGetParameters { Region = CacheRegion, Prefix = game.Type, Key = patchName }); } } + return result.NeedsUpdate; } else @@ -1768,6 +1809,7 @@ async Task evalState(IGame game, string patchName) return await evalState(game, patchName); } } + return false; } @@ -1784,14 +1826,15 @@ public Task RenamePatchCollectionAsync(string collectionName, string newCo { return Task.FromResult(false); } + var oldPatchName = GenerateCollectionPatchName(collectionName); var newPatchName = GenerateCollectionPatchName(newCollectionName); - return modPatchExporter.RenamePatchModAsync(new ModPatchExporterParameters() + return modPatchExporter.RenamePatchModAsync(new ModPatchExporterParameters { RootPath = GetModDirectoryRootPath(game), ModPath = EvaluatePatchNamePath(game, oldPatchName), PatchPath = EvaluatePatchNamePath(game, newPatchName), - RenamePairs = new List>() { new(oldPatchName, newPatchName) } + RenamePairs = [new KeyValuePair(oldPatchName, newPatchName)] }); } @@ -1822,10 +1865,10 @@ public virtual Task ResetIgnoredConflictAsync(IConflictResult conflictResu /// /// Resets the patch state cache. /// - /// true if XXXX, false otherwise. + /// true if reset, false otherwise. public virtual bool ResetPatchStateCache() { - Cache.Invalidate(new CacheInvalidateParameters() { Region = ModsExportedRegion, Keys = new List() { ModExportedKey } }); + Cache.Invalidate(new CacheInvalidateParameters { Region = ModsExportedRegion, Keys = [ModExportedKey] }); modPatchExporter.ResetCache(); return true; } @@ -1854,9 +1897,10 @@ public virtual string ResolveFullDefinitionPath(IDefinition definition) { return string.Empty; } + if (definition.IsFromGame) { - return Path.Combine(pathResolver.GetPath(game), definition.File); + return Path.Combine(PathResolver.GetPath(game), definition.File); } else { @@ -1870,10 +1914,12 @@ public virtual string ResolveFullDefinitionPath(IDefinition definition) { return mod.FullPath; } + return Path.Combine(mod.FullPath, definition.File); } } } + return string.Empty; } @@ -1890,11 +1936,12 @@ public virtual async Task SaveIgnoredPathsAsync(IConflictResult conflictRe { return false; } + await EvalModIgnoreDefinitionsAsync(conflictResult); if (conflictResult.Mode != PatchStateMode.ReadOnly && conflictResult.Mode != PatchStateMode.ReadOnlyWithoutLocalization) { var patchName = GenerateCollectionPatchName(collectionName); - return await modPatchExporter.SaveStateAsync(new ModPatchExporterParameters() + return await modPatchExporter.SaveStateAsync(new ModPatchExporterParameters { LoadOrder = GetCollectionMods(collectionName: collectionName).Select(p => p.DescriptorFile), Mode = MapPatchStateMode(conflictResult.Mode), @@ -1906,17 +1953,19 @@ public virtual async Task SaveIgnoredPathsAsync(IConflictResult conflictRe CustomConflicts = await GetDefinitionOrDefaultAsync(conflictResult.CustomConflicts), RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patchName), - HasGameDefinitions = await conflictResult.AllConflicts.HasGameDefinitionsAsync() + HasGameDefinitions = await conflictResult.AllConflicts.HasGameDefinitionsAsync(), + AllowedLanguages = conflictResult.AllowedLanguages }); } + return true; } /// - /// Shoulds the ignore game mods. + /// Checks whether it should ignore game mods. /// /// The conflict result. - /// true if XXXX, false otherwise. + /// true if it should ignore, false otherwise. public virtual bool? ShouldIgnoreGameMods(IConflictResult conflictResult) { if (conflictResult != null) @@ -1925,14 +1974,15 @@ public virtual async Task SaveIgnoredPathsAsync(IConflictResult conflictRe var lines = ignoredPaths.SplitOnNewLine(); return !lines.Any(p => p.Equals(ShowGameModsId)); } + return null; } /// - /// Shoulds the show reset conflicts. + /// Checks whether it should show the reset conflicts. /// /// The conflict result. - /// true if XXXX, false otherwise. + /// true if it should show, false otherwise. public virtual bool? ShouldShowResetConflicts(IConflictResult conflictResult) { if (conflictResult != null) @@ -1941,14 +1991,15 @@ public virtual async Task SaveIgnoredPathsAsync(IConflictResult conflictRe var lines = ignoredPaths.SplitOnNewLine(); return lines.Any(p => p.Equals(ShowResetConflicts)); } + return null; } /// - /// Shoulds the show self conflicts. + /// Checks whether if it should show self conflicts. /// /// The conflict result. - /// true if XXXX, false otherwise. + /// true if it should show, false otherwise. public virtual bool? ShouldShowSelfConflicts(IConflictResult conflictResult) { if (conflictResult != null) @@ -1957,6 +2008,7 @@ public virtual async Task SaveIgnoredPathsAsync(IConflictResult conflictRe var lines = ignoredPaths.SplitOnNewLine(); return lines.Any(p => p.Equals(ShowSelfConflictsId)); } + return null; } @@ -1964,7 +2016,7 @@ public virtual async Task SaveIgnoredPathsAsync(IConflictResult conflictRe /// Toggles the ignore game mods. /// /// The conflict result. - /// true if XXXX, false otherwise. + /// true if toggled, false otherwise. public virtual bool? ToggleIgnoreGameMods(IConflictResult conflictResult) { if (conflictResult != null) @@ -1980,9 +2032,11 @@ public virtual async Task SaveIgnoredPathsAsync(IConflictResult conflictRe { lines.Remove(ShowGameModsId); } + conflictResult.IgnoredPaths = string.Join(Environment.NewLine, lines).Trim(Environment.NewLine.ToCharArray()); return !shouldIgnore; } + return null; } @@ -1990,7 +2044,7 @@ public virtual async Task SaveIgnoredPathsAsync(IConflictResult conflictRe /// Toggles self mod conflicts. /// /// The conflict result. - /// true if XXXX, false otherwise. + /// true if toggled, false otherwise. public virtual bool? ToggleSelfModConflicts(IConflictResult conflictResult) { if (conflictResult != null) @@ -2006,9 +2060,11 @@ public virtual async Task SaveIgnoredPathsAsync(IConflictResult conflictRe { lines.Remove(ShowSelfConflictsId); } + conflictResult.IgnoredPaths = string.Join(Environment.NewLine, lines).Trim(Environment.NewLine.ToCharArray()); return !shouldShow; } + return null; } @@ -2016,7 +2072,7 @@ public virtual async Task SaveIgnoredPathsAsync(IConflictResult conflictRe /// Toggles the show reset conflicts. /// /// The conflict result. - /// true if XXXX, false otherwise. + /// true if toggled, false otherwise. public virtual bool? ToggleShowResetConflicts(IConflictResult conflictResult) { if (conflictResult != null) @@ -2032,9 +2088,11 @@ public virtual async Task SaveIgnoredPathsAsync(IConflictResult conflictRe { lines.Remove(ShowResetConflicts); } + conflictResult.IgnoredPaths = string.Join(Environment.NewLine, lines).Trim(Environment.NewLine.ToCharArray()); return !shouldReset; } + return null; } @@ -2050,26 +2108,22 @@ public virtual IValidateResult Validate(IDefinition definition) if (definition != null) { var lines = definition.Code.SplitOnNewLine(); - var args = new ParserArgs - { - Lines = lines, - File = definition.File, - ValidationType = MapValidationType(definition) - }; - IEnumerable validation = definition.ValueType != ValueType.Binary ? validateParser.Validate(args) : null; + var args = new ParserArgs { Lines = lines, File = definition.File, ValidationType = MapValidationType(definition) }; + var validation = definition.ValueType != ValueType.Binary ? validateParser.Validate(args) : null; if (validation != null && validation.Any()) { - result.ErrorMessage = validation.FirstOrDefault().ErrorMessage; - result.ErrorLine = validation.FirstOrDefault().ErrorLine; - result.ErrorColumn = validation.FirstOrDefault().ErrorColumn; + result.ErrorMessage = validation.FirstOrDefault()!.ErrorMessage; + result.ErrorLine = validation.FirstOrDefault()!.ErrorLine; + result.ErrorColumn = validation.FirstOrDefault()!.ErrorColumn; result.IsValid = false; } } + return result; } /// - /// Evals the definitions. + /// Evaluates the definitions. /// /// The indexed definitions. /// The conflicts. @@ -2079,25 +2133,29 @@ public virtual IValidateResult Validate(IDefinition definition) /// The file conflict cache. /// The mod sha conflict cache. /// A Task representing the asynchronous operation. - protected virtual async Task EvalDefinitionsAsync(IIndexedDefinitions indexedDefinitions, HashSet conflicts, IEnumerable definitions, IList modOrder, PatchStateMode patchStateMode, Dictionary fileConflictCache, Dictionary> modShaConflictCache) + protected virtual async Task EvalDefinitionsAsync(IIndexedDefinitions indexedDefinitions, HashSet conflicts, IEnumerable definitions, IList modOrder, PatchStateMode patchStateMode, + Dictionary fileConflictCache, Dictionary> modShaConflictCache) { async Task existsInLastFile(IDefinition definition) { var result = true; var fileDefs = await indexedDefinitions.GetByFileAsync(definition.FileCI); - var lastMod = fileDefs.GroupBy(p => p.ModName).Select(p => p.First()).OrderByDescending(p => modOrder.IndexOf(p.ModName)).FirstOrDefault(); + var lastMod = fileDefs.GroupBy(p => p.ModName).Select(p => p.First()).MaxBy(p => modOrder.IndexOf(p.ModName)); if (lastMod != null) { result = fileDefs.Any(p => p.ModName.Equals(lastMod.ModName) && p.TypeAndId.Equals(definition.TypeAndId)); } + return result; } - bool anyWholeTextFile = definitions.Any(p => p.ValueType == ValueType.WholeTextFile); + + var anyWholeTextFile = definitions.Any(p => p.ValueType == ValueType.WholeTextFile); var validDefinitions = new HashSet(); foreach (var item in definitions.Where(p => IsValidDefinitionType(p) || (anyWholeTextFile && p.ValueType == ValueType.EmptyFile))) { validDefinitions.Add(item); } + var processed = new HashSet(); foreach (var def in validDefinitions) { @@ -2105,110 +2163,152 @@ async Task existsInLastFile(IDefinition definition) { continue; } + var allConflicts = (await indexedDefinitions.GetByTypeAndIdAsync(def.Type, def.Id)).Where(p => IsValidDefinitionType(p) || (anyWholeTextFile && p.ValueType == ValueType.EmptyFile)); foreach (var conflict in allConflicts) { processed.Add(conflict); } - if (allConflicts.Count() > 1) + + switch (allConflicts.Count()) { - if (!allConflicts.All(p => p.DefinitionSHA.Equals(def.DefinitionSHA))) + case > 1: { - var validConflicts = new HashSet(); - foreach (var conflict in allConflicts) + if (!allConflicts.All(p => p.DefinitionSHA.Equals(def.DefinitionSHA))) { - if (conflicts.Contains(conflict) || validConflicts.Contains(conflict)) + var validConflicts = new HashSet(); + foreach (var conflict in allConflicts) { - continue; - } - var hasOverrides = allConflicts.Any(p => !p.IsCustomPatch && p.Dependencies != null && p.Dependencies.Any(p => p.Equals(conflict.ModName))); - if (hasOverrides && (patchStateMode == PatchStateMode.Default || patchStateMode == PatchStateMode.DefaultWithoutLocalization)) - { - var existing = allConflicts.Where(p => !p.IsCustomPatch && p.Dependencies != null && p.Dependencies.Any(p => p.Equals(conflict.ModName))); - if (existing.Any()) + if (conflicts.Contains(conflict) || validConflicts.Contains(conflict)) + { + continue; + } + + var hasOverrides = allConflicts.Any(p => !p.IsCustomPatch && p.Dependencies != null && p.Dependencies.Any(p => p.Equals(conflict.ModName))); + if (hasOverrides && patchStateMode is PatchStateMode.Default or PatchStateMode.DefaultWithoutLocalization) { - var fileNames = conflict.AdditionalFileNames; - foreach (var item in existing.Where(p => p != null)) + var existing = allConflicts.Where(p => !p.IsCustomPatch && p.Dependencies != null && p.Dependencies.Any(p => p.Equals(conflict.ModName))); + if (existing.Any()) { - foreach (var fileName in item.AdditionalFileNames) + var fileNames = conflict.AdditionalFileNames; + foreach (var item in existing.Where(p => p != null)) { - fileNames.Add(fileName); + foreach (var fileName in item.AdditionalFileNames) + { + fileNames.Add(fileName); + } } + + conflict.AdditionalFileNames = fileNames; } - conflict.AdditionalFileNames = fileNames; + + continue; } - continue; + + validConflicts.Add(conflict); } - validConflicts.Add(conflict); - } - var validConflictsGroup = validConflicts.GroupBy(p => p.DefinitionSHA); - if (validConflictsGroup.Count() > 1) - { - var filteredConflicts = validConflictsGroup.Select(p => EvalDefinitionPriority(p.OrderBy(m => modOrder.IndexOf(m.ModName))).Definition); - foreach (var item in filteredConflicts) + var validConflictsGroup = validConflicts.GroupBy(p => p.DefinitionSHA); + if (validConflictsGroup.Count() > 1) { - if (!conflicts.Contains(item) && (IsValidDefinitionType(item) || (anyWholeTextFile && item.ValueType == ValueType.EmptyFile))) + var filteredConflicts = validConflictsGroup.Select(p => EvalDefinitionPriority(p.OrderBy(m => modOrder.IndexOf(m.ModName))).Definition); + foreach (var item in filteredConflicts) { - if (!item.IsFromGame) + if (!conflicts.Contains(item) && (IsValidDefinitionType(item) || (anyWholeTextFile && item.ValueType == ValueType.EmptyFile))) { - if (modShaConflictCache.TryGetValue(item.TypeAndId, out var value) && value.Contains(item.DefinitionSHA)) + if (!item.IsFromGame) { - continue; + if (modShaConflictCache.TryGetValue(item.TypeAndId, out var value) && value.Contains(item.DefinitionSHA)) + { + continue; + } } - } - var shaMatches = validConflictsGroup.FirstOrDefault(p => p.Key == item.DefinitionSHA); - if (shaMatches.Count() > 1) - { - var fileNames = item.AdditionalFileNames; - foreach (var shaMatch in shaMatches.Where(p => p != item)) + + var shaMatches = validConflictsGroup.FirstOrDefault(p => p.Key == item.DefinitionSHA); + if (shaMatches.Count() > 1) { - foreach (var fileName in shaMatch.AdditionalFileNames) + var fileNames = item.AdditionalFileNames; + foreach (var shaMatch in shaMatches.Where(p => p != item)) { - fileNames.Add(fileName); + foreach (var fileName in shaMatch.AdditionalFileNames) + { + fileNames.Add(fileName); + } + } + + item.AdditionalFileNames = fileNames; + if (item.IsPlaceholder && shaMatches.Any(p => !p.IsPlaceholder)) + { + // Uncheck placeholder mark as there's a duplicate which is not marked as placeholder + item.IsPlaceholder = false; } } - item.AdditionalFileNames = fileNames; - if (item.IsPlaceholder && shaMatches.Any(p => !p.IsPlaceholder)) + + item.ExistsInLastFile = await existsInLastFile(item); + conflicts.Add(item); + if (!item.IsFromGame) { - // Uncheck placeholder mark as there's a duplicate which is not marked as placeholder - item.IsPlaceholder = false; + if (modShaConflictCache.TryGetValue(item.TypeAndId, out var value)) + { + value.Add(item.DefinitionSHA); + } + else + { + var sha = new List { item.DefinitionSHA }; + modShaConflictCache[item.TypeAndId] = sha; + } } } - item.ExistsInLastFile = await existsInLastFile(item); - conflicts.Add(item); - if (!item.IsFromGame) + } + } + } + + break; + } + case 1 when allConflicts.FirstOrDefault().ValueType == ValueType.Binary: + fileConflictCache.TryAdd(def.FileCI, false); + break; + case 1 when fileConflictCache.TryGetValue(def.FileCI, out var result): + { + if (result) + { + if (!conflicts.Contains(def) && (IsValidDefinitionType(def) || (anyWholeTextFile && def.ValueType == ValueType.EmptyFile))) + { + def.ExistsInLastFile = await existsInLastFile(def); + if (!def.ExistsInLastFile) + { + conflicts.Add(def); + if (!def.IsFromGame) { - if (modShaConflictCache.TryGetValue(item.TypeAndId, out var value)) + if (modShaConflictCache.TryGetValue(def.TypeAndId, out var value)) { - value.Add(item.DefinitionSHA); + value.Add(def.DefinitionSHA); } else { - var sha = new List - { - item.DefinitionSHA - }; - modShaConflictCache[item.TypeAndId] = sha; + var sha = new List { def.DefinitionSHA }; + modShaConflictCache[def.TypeAndId] = sha; } } } } } + + break; } - } - else if (allConflicts.Count() == 1) - { - if (allConflicts.FirstOrDefault().ValueType == ValueType.Binary) - { - fileConflictCache.TryAdd(def.FileCI, false); - } - else + case 1: { - if (fileConflictCache.TryGetValue(def.FileCI, out var result)) + var fileDefs = await indexedDefinitions.GetByFileAsync(def.FileCI); + if (fileDefs.GroupBy(p => p.ModName).Count() > 1) { - if (result) + var hasOverrides = !def.IsCustomPatch && def.Dependencies != null && def.Dependencies.Any(p => fileDefs.Any(s => s.ModName.Equals(p))); + if (hasOverrides && patchStateMode is PatchStateMode.Default or PatchStateMode.DefaultWithoutLocalization) + { + fileConflictCache.TryAdd(def.FileCI, false); + } + else { + fileConflictCache.TryAdd(def.FileCI, true); if (!conflicts.Contains(def) && (IsValidDefinitionType(def) || (anyWholeTextFile && def.ValueType == ValueType.EmptyFile))) { def.ExistsInLastFile = await existsInLastFile(def); @@ -2223,10 +2323,7 @@ async Task existsInLastFile(IDefinition definition) } else { - var sha = new List - { - def.DefinitionSHA - }; + var sha = new List { def.DefinitionSHA }; modShaConflictCache[def.TypeAndId] = sha; } } @@ -2236,54 +2333,17 @@ async Task existsInLastFile(IDefinition definition) } else { - var fileDefs = await indexedDefinitions.GetByFileAsync(def.FileCI); - if (fileDefs.GroupBy(p => p.ModName).Count() > 1) - { - var hasOverrides = !def.IsCustomPatch && def.Dependencies != null && def.Dependencies.Any(p => fileDefs.Any(s => s.ModName.Equals(p))); - if (hasOverrides && (patchStateMode == PatchStateMode.Default || patchStateMode == PatchStateMode.DefaultWithoutLocalization)) - { - fileConflictCache.TryAdd(def.FileCI, false); - } - else - { - fileConflictCache.TryAdd(def.FileCI, true); - if (!conflicts.Contains(def) && (IsValidDefinitionType(def) || (anyWholeTextFile && def.ValueType == ValueType.EmptyFile))) - { - def.ExistsInLastFile = await existsInLastFile(def); - if (!def.ExistsInLastFile) - { - conflicts.Add(def); - if (!def.IsFromGame) - { - if (modShaConflictCache.TryGetValue(def.TypeAndId, out var value)) - { - value.Add(def.DefinitionSHA); - } - else - { - var sha = new List - { - def.DefinitionSHA - }; - modShaConflictCache[def.TypeAndId] = sha; - } - } - } - } - } - } - else - { - fileConflictCache.TryAdd(def.FileCI, false); - } + fileConflictCache.TryAdd(def.FileCI, false); } + + break; } } } } /// - /// Evals the mod ignore definitions. + /// Evaluates the mod ignore definitions. /// /// The conflict result. /// A Task representing the asynchronous operation. @@ -2299,6 +2359,7 @@ bool canAllowForbiddenMod(IHierarchicalDefinitions hierarchicalDefinition, IRead return hierarchicalDefinition.Mods.Count > item.Count; } } + return true; } @@ -2314,7 +2375,7 @@ bool canAllowForbiddenMod(IHierarchicalDefinitions hierarchicalDefinition, IRead var forbiddenMods = new List(); var ignoreRules = new List(); var includeRules = new List(); - var lines = conflictResult.IgnoredPaths.SplitOnNewLine().Where(p => !p.Trim().StartsWith("#")); + var lines = conflictResult.IgnoredPaths.SplitOnNewLine().Where(p => !p.Trim().StartsWith('#')); foreach (var line in lines) { var parsed = line.StandardizeDirectorySeparator().Trim().TrimStart(Path.DirectorySeparatorChar); @@ -2342,7 +2403,7 @@ bool canAllowForbiddenMod(IHierarchicalDefinitions hierarchicalDefinition, IRead } else { - if (!parsed.StartsWith("!")) + if (!parsed.StartsWith('!')) { ignoreRules.Add(parsed); } @@ -2352,6 +2413,7 @@ bool canAllowForbiddenMod(IHierarchicalDefinitions hierarchicalDefinition, IRead } } } + if (!showResetConflicts) { foreach (var topConflict in conflictResult.Conflicts.GetHierarchicalDefinitions()) @@ -2362,24 +2424,24 @@ bool canAllowForbiddenMod(IHierarchicalDefinitions hierarchicalDefinition, IRead { if (!item.Mods.Any(allowedMods.Contains) || !canAllowForbiddenMod(item, forbiddenMods)) { - if (!alreadyIgnored.Contains(item.Key)) + if (alreadyIgnored.Add(item.Key)) { - alreadyIgnored.Add(item.Key); await ruleIgnoredDefinitions.AddToMapAsync((await conflictResult.Conflicts.GetByTypeAndIdAsync(item.Key)).First()); } } } + var name = topConflict.Name; if (!name.EndsWith(Path.DirectorySeparatorChar)) { name = $"{name}{Path.DirectorySeparatorChar}"; } - var invalid = topConflict.Children.Where(p => ignoreRules.Any(r => EvalWildcard(r, p.FileNames.ToArray()))).Where(p => !includeRules.Any(r => EvalWildcard(r, p.FileNames.ToArray()))); + + var invalid = topConflict.Children.Where(p => ignoreRules.Any(r => EvalWildcard(r, [.. p.FileNames]))).Where(p => !includeRules.Any(r => EvalWildcard(r, [.. p.FileNames]))); foreach (var item in invalid) { - if (!alreadyIgnored.Contains(item.Key)) + if (alreadyIgnored.Add(item.Key)) { - alreadyIgnored.Add(item.Key); await ruleIgnoredDefinitions.AddToMapAsync((await conflictResult.Conflicts.GetByTypeAndIdAsync(item.Key)).First()); } } @@ -2388,9 +2450,8 @@ bool canAllowForbiddenMod(IHierarchicalDefinitions hierarchicalDefinition, IRead { foreach (var item in topConflict.Children) { - if (!alreadyIgnored.Contains(item.Key)) + if (alreadyIgnored.Add(item.Key)) { - alreadyIgnored.Add(item.Key); await ruleIgnoredDefinitions.AddToMapAsync((await conflictResult.Conflicts.GetByTypeAndIdAsync(item.Key)).First()); } } @@ -2398,15 +2459,15 @@ bool canAllowForbiddenMod(IHierarchicalDefinitions hierarchicalDefinition, IRead } } } + if (showResetConflicts) { foreach (var topConflict in conflictResult.Conflicts.GetHierarchicalDefinitions()) { foreach (var item in topConflict.Children) { - if (item.ResetType == ResetType.None && !alreadyIgnored.Contains(item.Key)) + if (item.ResetType == ResetType.None && alreadyIgnored.Add(item.Key)) { - alreadyIgnored.Add(item.Key); await ruleIgnoredDefinitions.AddToMapAsync((await conflictResult.Conflicts.GetByTypeAndIdAsync(item.Key)).First()); } } @@ -2420,29 +2481,29 @@ bool canAllowForbiddenMod(IHierarchicalDefinitions hierarchicalDefinition, IRead { foreach (var item in topConflict.Children.Where(p => p.Mods.Count <= 1)) { - if (ignoreGameMods && item.NonGameDefinitions <= 1 && !alreadyIgnored.Contains(item.Key)) + if (ignoreGameMods && item.NonGameDefinitions <= 1 && alreadyIgnored.Add(item.Key)) { - alreadyIgnored.Add(item.Key); await ruleIgnoredDefinitions.AddToMapAsync((await conflictResult.Conflicts.GetByTypeAndIdAsync(item.Key)).First()); } - if (ignoreSelfConflicts && item.NonGameDefinitions > 1 && !alreadyIgnored.Contains(item.Key)) + + if (ignoreSelfConflicts && item.NonGameDefinitions > 1 && alreadyIgnored.Add(item.Key)) { - alreadyIgnored.Add(item.Key); await ruleIgnoredDefinitions.AddToMapAsync((await conflictResult.Conflicts.GetByTypeAndIdAsync(item.Key)).First()); } } } } } + conflictResult.RuleIgnoredConflicts = ruleIgnoredDefinitions; } /// - /// Evals the wildcard. + /// Evaluates the wildcard. /// /// The pattern. /// The content. - /// true if XXXX, false otherwise. + /// true if evaluated the wildcard, false otherwise. protected virtual bool EvalWildcard(string pattern, params string[] values) { foreach (var item in values) @@ -2463,6 +2524,7 @@ protected virtual bool EvalWildcard(string pattern, params string[] values) } } } + return false; } @@ -2473,7 +2535,7 @@ protected virtual bool EvalWildcard(string pattern, params string[] values) /// The definition. /// Name of the collection. /// Type of the export. - /// true if XXXX, false otherwise. + /// true if exported, false otherwise. protected virtual async Task ExportModPatchDefinitionAsync(IConflictResult conflictResult, IDefinition definition, string collectionName, ExportType exportType) { var game = GameService.GetSelected(); @@ -2485,24 +2547,14 @@ protected virtual async Task ExportModPatchDefinitionAsync(IConflictResult if (!allMods.Any(p => p.Name.Equals(patchName))) { mod = GeneratePatchModDescriptor(allMods, game, patchName); - await ModWriter.CreateModDirectoryAsync(new ModWriterParameters() - { - RootDirectory = game.UserDirectory, - Path = Shared.Constants.ModDirectory - }); - await ModWriter.CreateModDirectoryAsync(new ModWriterParameters() - { - RootDirectory = GetPatchModDirectory(game, patchName), - }); + await ModWriter.CreateModDirectoryAsync(new ModWriterParameters { RootDirectory = game.UserDirectory, Path = Shared.Constants.ModDirectory }); + await ModWriter.CreateModDirectoryAsync(new ModWriterParameters { RootDirectory = GetPatchModDirectory(game, patchName) }); if (game.ModDescriptorType == ModDescriptorType.JsonMetadata) { - await ModWriter.CreateModDirectoryAsync(new ModWriterParameters() - { - RootDirectory = game.UserDirectory, - Path = Shared.Constants.JsonModDirectory - }); + await ModWriter.CreateModDirectoryAsync(new ModWriterParameters { RootDirectory = game.UserDirectory, Path = Shared.Constants.JsonModDirectory }); } - await ModWriter.WriteDescriptorAsync(new ModWriterParameters() + + await ModWriter.WriteDescriptorAsync(new ModWriterParameters { Mod = mod, RootDirectory = game.UserDirectory, @@ -2511,21 +2563,17 @@ await ModWriter.WriteDescriptorAsync(new ModWriterParameters() DescriptorType = MapDescriptorType(game.ModDescriptorType) }, IsPatchMod(mod)); allMods.Add(mod); - Cache.Invalidate(new CacheInvalidateParameters() { Region = ModsCacheRegion, Prefix = game.Type, Keys = new List { GetModsCacheKey(true), GetModsCacheKey(false) } }); + Cache.Invalidate(new CacheInvalidateParameters { Region = ModsCacheRegion, Prefix = game.Type, Keys = [GetModsCacheKey(true), GetModsCacheKey(false)] }); } else { mod = allMods.FirstOrDefault(p => p.Name.Equals(patchName)); } + var definitionMod = allMods.FirstOrDefault(p => p.Name.Equals(definition.ModName)); if (definitionMod != null || definition.IsFromGame) { - var args = new ModPatchExporterParameters() - { - Game = game.Type, - RootPath = GetModDirectoryRootPath(game), - PatchPath = EvaluatePatchNamePath(game, patchName) - }; + var args = new ModPatchExporterParameters { Game = game.Type, RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patchName) }; var exportPatches = new HashSet(); switch (exportType) { @@ -2546,20 +2594,15 @@ await ModWriter.WriteDescriptorAsync(new ModWriterParameters() break; } - var state = Cache.Get(new CacheGetParameters() { Region = ModsExportedRegion, Key = ModExportedKey }); + var state = Cache.Get(new CacheGetParameters { Region = ModsExportedRegion, Key = ModExportedKey }); if (state == null || state.Exported.GetValueOrDefault() == false) { await Task.Run(() => { - ModWriter.ApplyModsAsync(new ModWriterParameters() - { - AppendOnly = true, - TopPriorityMods = new List() { mod }, - RootDirectory = game.UserDirectory, - DescriptorType = MapDescriptorType(game.ModDescriptorType) - }).ConfigureAwait(false); + ModWriter.ApplyModsAsync(new ModWriterParameters { AppendOnly = true, TopPriorityMods = [mod], RootDirectory = game.UserDirectory, DescriptorType = MapDescriptorType(game.ModDescriptorType) }) + .ConfigureAwait(false); }).ConfigureAwait(false); - Cache.Set(new CacheAddParameters() { Region = ModsExportedRegion, Key = ModExportedKey, Value = new ModsExportedState() { Exported = true } }); + Cache.Set(new CacheAddParameters { Region = ModsExportedRegion, Key = ModExportedKey, Value = new ModsExportedState { Exported = true } }); } // Reset type flag since it was resolved now @@ -2567,7 +2610,7 @@ await Task.Run(() => await conflictResult.ResolvedConflicts.ChangeHierarchicalResetStateAsync(definition); var exportResult = false; - if (exportPatches.Any()) + if (exportPatches.Count != 0) { if (definition.ValueType == ValueType.OverwrittenObjectSingleFile) { @@ -2583,25 +2626,22 @@ await Task.Run(() => var overwritten = await conflictResult.OverwrittenConflicts.GetByTypeAndIdAsync(definition.TypeAndId); if (overwritten.Any()) { - definition.Order = overwritten.FirstOrDefault().Order; + definition.Order = overwritten.FirstOrDefault()!.Order; } } + exportResult = await modPatchExporter.ExportDefinitionAsync(args); if (exportResult) { var overwritten = await conflictResult.OverwrittenConflicts.GetByTypeAndIdAsync(definition.TypeAndId); - if (overwritten.Any() && overwritten.FirstOrDefault().DiskFileCI != definition.DiskFileCI) + if (overwritten.Any() && overwritten.FirstOrDefault()!.DiskFileCI != definition.DiskFileCI) { - await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters() - { - RootDirectory = GetPatchModDirectory(game, patchName), - Path = overwritten.FirstOrDefault().DiskFile - }); + await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters { RootDirectory = GetPatchModDirectory(game, patchName), Path = overwritten.FirstOrDefault()!.DiskFile }); } } } - var stateResult = await modPatchExporter.SaveStateAsync(new ModPatchExporterParameters() + var stateResult = await modPatchExporter.SaveStateAsync(new ModPatchExporterParameters { LoadOrder = GetCollectionMods(collectionName: collectionName).Select(p => p.DescriptorFile), Mode = MapPatchStateMode(conflictResult.Mode), @@ -2614,11 +2654,13 @@ await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters() CustomConflicts = await GetDefinitionOrDefaultAsync(conflictResult.CustomConflicts), RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patchName), - HasGameDefinitions = await conflictResult.AllConflicts.HasGameDefinitionsAsync() + HasGameDefinitions = await conflictResult.AllConflicts.HasGameDefinitionsAsync(), + AllowedLanguages = conflictResult.AllowedLanguages }); - return exportPatches.Any() ? exportResult && stateResult : stateResult; + return exportPatches.Count != 0 ? exportResult && stateResult : stateResult; } } + return false; } @@ -2648,6 +2690,7 @@ protected virtual async Task> FindPatchStateMatchedConf } } } + return matchedConflicts; } @@ -2663,7 +2706,8 @@ protected virtual async Task> GetDefinitionOrDefaultAsync(IIn var defs = await definitions.GetAllAsync(); return defs.ToList(); } - return new List(); + + return []; } /// @@ -2684,6 +2728,7 @@ protected virtual double GetProgressPercentage(double total, double processed, d { perc = maxPerc; } + return perc; } @@ -2699,6 +2744,7 @@ protected virtual bool IsCachedDefinitionDifferent(IEnumerable curr { return true; } + var cachedDiffs = cachedConflicts.Where(p => currentConflicts.Any(a => a.FileCI.Equals(p.FileCI) && a.DefinitionSHA.Equals(p.DefinitionSHA))); return cachedDiffs.Count() != cachedConflicts.Count(); } @@ -2726,7 +2772,7 @@ protected virtual PatchStateMode MapPatchStateMode(IO.Common.PatchStateMode mode IO.Common.PatchStateMode.Advanced => PatchStateMode.Advanced, IO.Common.PatchStateMode.AdvancedWithoutLocalization => PatchStateMode.AdvancedWithoutLocalization, IO.Common.PatchStateMode.DefaultWithoutLocalization => PatchStateMode.DefaultWithoutLocalization, - _ => PatchStateMode.None, + _ => PatchStateMode.None }; } @@ -2735,6 +2781,7 @@ protected virtual PatchStateMode MapPatchStateMode(IO.Common.PatchStateMode mode /// /// The mode. /// IO.Common.PatchStateMode. + /// Invalid readonly mode /// Invalid readonly mode protected virtual IO.Common.PatchStateMode MapPatchStateMode(PatchStateMode mode) { @@ -2742,13 +2789,14 @@ protected virtual IO.Common.PatchStateMode MapPatchStateMode(PatchStateMode mode { throw new ArgumentException("Invalid readonly mode"); } + return mode switch { PatchStateMode.Default => IO.Common.PatchStateMode.Default, PatchStateMode.Advanced => IO.Common.PatchStateMode.Advanced, PatchStateMode.AdvancedWithoutLocalization => IO.Common.PatchStateMode.AdvancedWithoutLocalization, PatchStateMode.DefaultWithoutLocalization => IO.Common.PatchStateMode.DefaultWithoutLocalization, - _ => IO.Common.PatchStateMode.None, + _ => IO.Common.PatchStateMode.None }; } @@ -2767,6 +2815,7 @@ protected virtual ValidationType MapValidationType(IDefinition definition) { return ValidationType.SkipAll; } + return ValidationType.Full; } @@ -2797,6 +2846,7 @@ static void appendLine(StringBuilder sb, IEnumerable lines, int indent = } } } + static void mergeCode(StringBuilder sb, string codeTag, string separator, IEnumerable variables, IEnumerable lines) { if (Shared.Constants.CodeSeparators.ClosingSeparators.Map.TryGetValue(separator, out var value)) @@ -2813,7 +2863,7 @@ static void mergeCode(StringBuilder sb, string codeTag, string separator, IEnume } else { - bool varsInserted = false; + var varsInserted = false; foreach (var item in lines) { var splitLines = item.SplitOnNewLine(); @@ -2832,6 +2882,7 @@ static void mergeCode(StringBuilder sb, string codeTag, string separator, IEnume } } } + sb.AppendLine(closingTag); } else @@ -2842,6 +2893,7 @@ static void mergeCode(StringBuilder sb, string codeTag, string separator, IEnume var splitLines = item.SplitOnNewLine(); appendLine(sb, splitLines, 4); } + foreach (var item in lines) { var splitLines = item.SplitOnNewLine(); @@ -2855,9 +2907,9 @@ static void mergeCode(StringBuilder sb, string codeTag, string separator, IEnume copy.ValueType = ValueType.OverwrittenObjectSingleFile; copy.IsFromGame = false; var groups = definitions.GroupBy(p => p.CodeTag, StringComparer.OrdinalIgnoreCase); - foreach (var group in groups.OrderBy(p => p.FirstOrDefault().CodeTag, StringComparer.OrdinalIgnoreCase)) + foreach (var group in groups.OrderBy(p => p.FirstOrDefault()!.CodeTag, StringComparer.OrdinalIgnoreCase)) { - bool hasCodeTag = !string.IsNullOrWhiteSpace(group.FirstOrDefault().CodeTag); + var hasCodeTag = !string.IsNullOrWhiteSpace(group.FirstOrDefault()!.CodeTag); if (!hasCodeTag) { var namespaces = group.Where(p => hasCode(p.File, p.Code) && p.ValueType == ValueType.Namespace); @@ -2873,7 +2925,7 @@ static void mergeCode(StringBuilder sb, string codeTag, string separator, IEnume var other = group.Where(p => p.ValueType != ValueType.Variable && p.ValueType != ValueType.Namespace && hasCode(p.File, p.Code)); var vars = namespaces.Select(p => p.OriginalCode).Concat(variables.Select(p => p.OriginalCode)); var code = other.Select(p => p.OriginalCode); - mergeCode(sb, group.FirstOrDefault().CodeTag, group.FirstOrDefault().CodeSeparator, vars, code); + mergeCode(sb, group.FirstOrDefault()!.CodeTag, group.FirstOrDefault()!.CodeSeparator, vars, code); } } @@ -2891,9 +2943,9 @@ protected virtual IModIgnoreConfiguration ParseIgnoreModLine(string line) var parsed = line.StandardizeDirectorySeparator().Trim().TrimStart(Path.DirectorySeparatorChar); if (parsed.StartsWith(ModNameIgnoreId)) { - string[] statements = line.Split(IgnoreRulesSeparator); + var statements = line.Split(IgnoreRulesSeparator); var ignoredModName = statements[0].Replace(ModNameIgnoreId, string.Empty).Trim(); - int counter = 2; + var counter = 2; if (statements.Length > 1) { if (int.TryParse(statements[1].Replace(ModNameIgnoreCounterId, string.Empty).Trim(), out var val)) @@ -2901,11 +2953,13 @@ protected virtual IModIgnoreConfiguration ParseIgnoreModLine(string line) counter = val; } } + var model = GetModelInstance(); model.ModName = ignoredModName; model.Count = counter; return model; } + return null; } @@ -2916,17 +2970,29 @@ protected virtual IModIgnoreConfiguration ParseIgnoreModLine(string line) /// The file infos. /// The mod object. /// The definition information provider. + /// The allowed game languages. /// IEnumerable<IDefinition>. - protected virtual IEnumerable ParseModFiles(IGame game, IEnumerable fileInfos, IModObject modObject, IDefinitionInfoProvider definitionInfoProvider) + protected virtual IEnumerable ParseModFiles(IGame game, IEnumerable fileInfos, IModObject modObject, IDefinitionInfoProvider definitionInfoProvider, IReadOnlyCollection allowedGameLanguages) { if (fileInfos == null) { return null; } + var definitions = new List(); foreach (var fileInfo in fileInfos) { - var fileDefs = parserManager.Parse(new ParserManagerArgs() + // See if we should skip maybe + var onlyFilename = Path.GetFileNameWithoutExtension(fileInfo.FileName); + if (allowedGameLanguages != null && fileInfo.FileName!.StartsWith(Shared.Constants.LocalizationDirectory, StringComparison.OrdinalIgnoreCase)) + { + if (!allowedGameLanguages.Any(p => onlyFilename!.EndsWith(p.Type, StringComparison.OrdinalIgnoreCase))) + { + continue; + } + } + + var fileDefs = parserManager.Parse(new ParserManagerArgs { ContentSHA = fileInfo.ContentSHA, File = fileInfo.FileName, @@ -2940,30 +3006,30 @@ protected virtual IEnumerable ParseModFiles(IGame game, IEnumerable if (fileDefs.Any()) { // Validate and see whether we need to check encoding - if (!fileDefs.Any(p => p.ValueType == ValueType.Invalid)) - { - if (!fileDefs.Any(p => p.ValueType == ValueType.Binary) && definitionInfoProvider != null && !definitionInfoProvider.IsValidEncoding(Path.GetDirectoryName(fileInfo.FileName), fileInfo.Encoding)) - { - var definition = DIResolver.Get(); - definition.ErrorMessage = "File has invalid encoding, please use UTF-8-BOM Encoding."; - definition.Id = Path.GetFileName(fileInfo.FileName).ToLowerInvariant(); - definition.ValueType = ValueType.Invalid; - definition.OriginalCode = definition.Code = string.Join(Environment.NewLine, fileInfo.Content ?? new List()); - definition.ContentSHA = fileInfo.ContentSHA; - definition.Dependencies = modObject.Dependencies; - definition.ModName = modObject.Name; - definition.OriginalModName = modObject.Name; - definition.OriginalFileName = fileInfo.FileName; - definition.File = fileInfo.FileName; - definition.Type = fileInfo.FileName.FormatDefinitionType(); - definitions.Add(definition); - continue; - } + if (fileDefs.All(p => p.ValueType != ValueType.Invalid && p.ValueType != ValueType.Binary) && definitionInfoProvider != null && + !definitionInfoProvider.IsValidEncoding(Path.GetDirectoryName(fileInfo.FileName), fileInfo.Encoding)) + { + var definition = DIResolver.Get(); + definition.ErrorMessage = "File has invalid encoding, please use UTF-8-BOM Encoding."; + definition.Id = Path.GetFileName(fileInfo.FileName)!.ToLowerInvariant(); + definition.ValueType = ValueType.Invalid; + definition.OriginalCode = definition.Code = string.Join(Environment.NewLine, fileInfo.Content ?? []); + definition.ContentSHA = fileInfo.ContentSHA; + definition.Dependencies = modObject.Dependencies; + definition.ModName = modObject.Name; + definition.OriginalModName = modObject.Name; + definition.OriginalFileName = fileInfo.FileName; + definition.File = fileInfo.FileName; + definition.Type = fileInfo.FileName.FormatDefinitionType(); + definitions.Add(definition); + continue; } + MergeDefinitions(fileDefs); definitions.AddRange(fileDefs); } } + return definitions; } @@ -2971,19 +3037,21 @@ protected virtual IEnumerable ParseModFiles(IGame game, IEnumerable /// Partials the definition copy. /// /// The definition. - /// if set to true [copy aditional filenames]. + /// The copy additional filenames. /// IDefinition. - protected virtual IDefinition PartialDefinitionCopy(IDefinition definition, bool copyAditionalFilenames = true) + protected virtual IDefinition PartialDefinitionCopy(IDefinition definition, bool copyAdditionalFilenames = true) { if (definition.ValueType == ValueType.Invalid || definition.AllowDuplicate) { return CopyDefinition(definition); } + var copy = DIResolver.Get(); - if (copyAditionalFilenames) + if (copyAdditionalFilenames) { copy.AdditionalFileNames = definition.AdditionalFileNames; } + copy.DiskFile = definition.DiskFile; copy.File = definition.File; copy.Id = definition.Id; @@ -3008,9 +3076,9 @@ protected virtual IDefinition PartialDefinitionCopy(IDefinition definition, bool /// The conflict result. /// Name of the patch. /// The type. - /// The state provinder. + /// The state provider. /// IDefinition. - protected virtual async Task ProcessOverwrittenSingleFileDefinitionsAsync(IConflictResult conflictResult, string patchName, string type, Tuple stateProvinder = null) + protected virtual async Task ProcessOverwrittenSingleFileDefinitionsAsync(IConflictResult conflictResult, string patchName, string type, Tuple stateProvider = null) { static string cleanString(string text) { @@ -3018,17 +3086,20 @@ static string cleanString(string text) text = text.Replace(" ", string.Empty).Replace("\t", string.Empty).Trim(); return text; } - static string getNextVariableName(List exportDefinitons, IDefinition definition) + + static string getNextVariableName(List exportDefinitions, IDefinition definition) { - var count = exportDefinitons.Where(p => p.Id.Equals(definition.Id, StringComparison.OrdinalIgnoreCase)).Count() + 1; + var count = exportDefinitions.Count(p => p.Id.Equals(definition.Id, StringComparison.OrdinalIgnoreCase)) + 1; var name = $"{definition.Id}_{count}"; - while (exportDefinitons.Any(p => p.Id.Equals(name, StringComparison.OrdinalIgnoreCase))) + while (exportDefinitions.Any(p => p.Id.Equals(name, StringComparison.OrdinalIgnoreCase))) { count++; name = $"{definition.Id}_{count}"; } + return name; } + void parseNameSpaces(List exportDefinitions, IDefinition def) { var namespaces = def.Variables?.Where(p => p.ValueType == ValueType.Namespace); @@ -3046,6 +3117,7 @@ void parseNameSpaces(List exportDefinitions, IDefinition def) } } } + void parseVariables(List exportDefinitions, IDefinition def) { var variables = def.Variables?.Where(p => p.ValueType == ValueType.Variable); @@ -3056,13 +3128,18 @@ void parseVariables(List exportDefinitions, IDefinition def) var copy = CopyDefinition(variable); var oldId = copy.Id; copy.Id = getNextVariableName(exportDefinitions, variable); - copy.Code = string.Join(" ", copy.Code.Split(" ", StringSplitOptions.None).Select(p => p.Contains(oldId) ? string.Join(Environment.NewLine, p.SplitOnNewLine(false).Select(s => s.Trim() == oldId ? s.Replace(oldId, copy.Id) : s)) : p)); - copy.OriginalCode = string.Join(" ", copy.OriginalCode.Split(" ", StringSplitOptions.None).Select(p => p.Contains(oldId) ? string.Join(Environment.NewLine, p.SplitOnNewLine(false).Select(s => s.Trim() == oldId ? s.Replace(oldId, copy.Id) : s)) : p)); + copy.Code = string.Join(" ", + copy.Code.Split(" ", StringSplitOptions.None).Select(p => p.Contains(oldId) ? string.Join(Environment.NewLine, p.SplitOnNewLine(false).Select(s => s.Trim() == oldId ? s.Replace(oldId, copy.Id) : s)) : p)); + copy.OriginalCode = string.Join(" ", + copy.OriginalCode.Split(" ", StringSplitOptions.None) + .Select(p => p.Contains(oldId) ? string.Join(Environment.NewLine, p.SplitOnNewLine(false).Select(s => s.Trim() == oldId ? s.Replace(oldId, copy.Id) : s)) : p)); copy.CodeTag = def.CodeTag; copy.CodeSeparator = def.CodeSeparator; exportDefinitions.Add(copy); - def.Code = string.Join(" ", def.Code.Split(" ", StringSplitOptions.None).Select(p => p.Contains(oldId) ? string.Join(Environment.NewLine, p.SplitOnNewLine(false).Select(s => s.Trim() == oldId ? s.Replace(oldId, copy.Id) : s)) : p)); - def.OriginalCode = string.Join(" ", def.OriginalCode.Split(" ", StringSplitOptions.None).Select(p => p.Contains(oldId) ? string.Join(Environment.NewLine, p.SplitOnNewLine(false).Select(s => s.Trim() == oldId ? s.Replace(oldId, copy.Id) : s)) : p)); + def.Code = string.Join(" ", + def.Code.Split(" ", StringSplitOptions.None).Select(p => p.Contains(oldId) ? string.Join(Environment.NewLine, p.SplitOnNewLine(false).Select(s => s.Trim() == oldId ? s.Replace(oldId, copy.Id) : s)) : p)); + def.OriginalCode = string.Join(" ", + def.OriginalCode.Split(" ", StringSplitOptions.None).Select(p => p.Contains(oldId) ? string.Join(Environment.NewLine, p.SplitOnNewLine(false).Select(s => s.Trim() == oldId ? s.Replace(oldId, copy.Id) : s)) : p)); } } } @@ -3073,20 +3150,15 @@ void parseVariables(List exportDefinitions, IDefinition def) var modOrder = GetCollectionMods().Select(p => p.Name).ToList(); var game = GameService.GetSelected(); var export = new List(); - var all = (await conflictResult.AllConflicts.GetByParentDirectoryAsync(definitions.FirstOrDefault().ParentDirectoryCI)).Where(IsValidDefinitionType); + var all = (await conflictResult.AllConflicts.GetByParentDirectoryAsync(definitions.FirstOrDefault()!.ParentDirectoryCI)).Where(IsValidDefinitionType); var ordered = all.GroupBy(p => p.TypeAndId).Select(p => { if (p.Any(v => v.AllowDuplicate)) { return p.GroupBy(p => p.File).Select(v => { - var defininition = v.FirstOrDefault(); - return new DefinitionOrderSort() - { - TypeAndId = defininition.TypeAndId, - Order = defininition.Order, - File = Path.GetFileNameWithoutExtension(defininition.File) - }; + var definition = v.FirstOrDefault(); + return new DefinitionOrderSort { TypeAndId = definition!.TypeAndId, Order = definition.Order, File = Path.GetFileNameWithoutExtension(definition.File) }; }); } else @@ -3094,34 +3166,21 @@ void parseVariables(List exportDefinitions, IDefinition def) var partialCopy = new List(); p.ToList().ForEach(x => partialCopy.Add(PartialDefinitionCopy(x, false))); var priority = EvalDefinitionPriorityInternal(partialCopy.OrderBy(x => modOrder.IndexOf(x.ModName)), true); - return new List() - { - new() - { - TypeAndId = priority.Definition.TypeAndId, - Order = priority.Definition.Order, - File = Path.GetFileNameWithoutExtension(priority.FileName) - } - }; + return [new DefinitionOrderSort { TypeAndId = priority.Definition.TypeAndId, Order = priority.Definition.Order, File = Path.GetFileNameWithoutExtension(priority.FileName) }]; } }).SelectMany(p => p).GroupBy(p => p.File).OrderBy(p => p.Key, StringComparer.Ordinal).SelectMany(p => p.OrderBy(x => x.Order)).ToList(); - var fullyOrdered = ordered.Select(p => new DefinitionOrderSort() - { - TypeAndId = p.TypeAndId, - Order = ordered.IndexOf(p), - File = p.File - }).ToList(); + var fullyOrdered = ordered.Select(p => new DefinitionOrderSort { TypeAndId = p.TypeAndId, Order = ordered.IndexOf(p), File = p.File }).ToList(); var sortExport = new List(); var infoProvider = DefinitionInfoProviders.FirstOrDefault(p => p.CanProcess(game.Type) && p.IsFullyImplemented); var overwrittenFileNames = new HashSet(); async Task handleDefinition(IDefinition item) { - IDefinition definition = item; + var definition = item; IEnumerable resolved; - if (stateProvinder != null) + if (stateProvider != null) { - resolved = await stateProvinder.Item2.GetByTypeAndIdAsync(item.TypeAndId); + resolved = await stateProvider.Item2.GetByTypeAndIdAsync(item.TypeAndId); } else { @@ -3132,15 +3191,15 @@ async Task handleDefinition(IDefinition item) if (resolved.Any()) { definition = resolved.FirstOrDefault(); - if (stateProvinder != null) + if (stateProvider != null) { - definition.Order = item.Order; + definition!.Order = item.Order; definition.DiskFile = item.DiskFile; definition.File = item.File; definition.OverwrittenFileNames = item.OverwrittenFileNames; // If state is provided assume we need to load from conflict history - if (stateProvinder.Item1 != null && stateProvinder.Item1.IndexedConflictHistory != null && stateProvinder.Item1.IndexedConflictHistory.Any() && stateProvinder.Item1.IndexedConflictHistory.TryGetValue(definition.TypeAndId, out var value)) + if (stateProvider.Item1 is { IndexedConflictHistory: not null } && stateProvider.Item1.IndexedConflictHistory.Any() && stateProvider.Item1.IndexedConflictHistory.TryGetValue(definition.TypeAndId, out var value)) { var history = value.FirstOrDefault(); if (history != null) @@ -3150,8 +3209,9 @@ async Task handleDefinition(IDefinition item) } } } + var copy = CopyDefinition(definition); - var parsed = parserManager.Parse(new ParserManagerArgs() + var parsed = parserManager.Parse(new ParserManagerArgs { ContentSHA = copy.ContentSHA, File = copy.File, @@ -3166,18 +3226,19 @@ async Task handleDefinition(IDefinition item) var variables = parsed.Where(p => p.ValueType == ValueType.Variable || p.ValueType == ValueType.Namespace); other.Variables = variables; var exportCopy = CopyDefinition(other); - var allType = (await conflictResult.AllConflicts.GetByTypeAndIdAsync(definition.TypeAndId)).ToList(); + var allType = (await conflictResult.AllConflicts.GetByTypeAndIdAsync(definition!.TypeAndId)).ToList(); allType.ForEach(p => overwrittenFileNames.Add(p.OriginalFileName)); if (other.AllowDuplicate) { var match = fullyOrdered.FirstOrDefault(p => p.TypeAndId == definition.TypeAndId && p.File == Path.GetFileNameWithoutExtension(other.File)); match ??= fullyOrdered.FirstOrDefault(p => p.TypeAndId == definition.TypeAndId); - exportCopy.Order = match.Order; + exportCopy.Order = match!.Order; } else { - exportCopy.Order = fullyOrdered.FirstOrDefault(p => p.TypeAndId == definition.TypeAndId).Order; + exportCopy.Order = fullyOrdered.FirstOrDefault(p => p.TypeAndId == definition.TypeAndId)!.Order; } + export.Add(exportCopy); sortExport.Add(exportCopy); } @@ -3190,9 +3251,8 @@ async Task handleDefinition(IDefinition item) { await handleDefinition(item); } - else if (!handledDuplicates.Contains(item.TypeAndId)) + else if (handledDuplicates.Add(item.TypeAndId)) { - handledDuplicates.Add(item.TypeAndId); var duplicates = (await conflictResult.AllConflicts.GetByTypeAndIdAsync(item.TypeAndId)).GroupBy(p => p.File); foreach (var duplicate in duplicates) { @@ -3200,6 +3260,7 @@ async Task handleDefinition(IDefinition item) } } } + var fullySortedExport = sortExport.OrderBy(p => p.Order).ToList(); sortExport.ForEach(p => p.Order = fullySortedExport.IndexOf(p) + 1); foreach (var item in sortExport.OrderBy(p => p.Order).Where(p => p.ValueType != ValueType.Variable && p.ValueType != ValueType.Namespace)) @@ -3207,11 +3268,13 @@ async Task handleDefinition(IDefinition item) parseNameSpaces(export, item); parseVariables(export, item); } + if (export.All(p => p.ValueType == ValueType.Namespace || p.ValueType == ValueType.Variable)) { export.Clear(); } - if (export.Any()) + + if (export.Count != 0) { var namespaces = export.Where(p => p.ValueType == ValueType.Namespace).OrderBy(p => p.Id); var variables = export.Where(p => p.ValueType == ValueType.Variable).OrderBy(p => p.Id); @@ -3222,7 +3285,7 @@ async Task handleDefinition(IDefinition item) merged.Id = SingleFileMerged; merged.File = overwrittenFileNames.FirstOrDefault(); merged.GeneratedFileNames = overwrittenFileNames.Distinct().ToList(); - merged.File = infoProvider.GetFileName(merged); + merged.File = infoProvider!.GetFileName(merged); merged.DiskFile = infoProvider.GetDiskFileName(merged); merged.ValueType = ValueType.OverwrittenObjectSingleFile; merged.ModName = patchName; @@ -3231,6 +3294,7 @@ async Task handleDefinition(IDefinition item) } } } + return null; } @@ -3269,9 +3333,10 @@ protected virtual IList ProcessPatchStateFiles(IPatchState state, IGroup /// The matched conflicts. /// if set to true [read only mode]. /// A Task representing the asynchronous operation. - protected virtual async Task SyncPatchStateAsync(IGame game, string patchName, List resolvedConflicts, IGrouping item, IList files, IEnumerable matchedConflicts, bool readOnlyMode) + protected virtual async Task SyncPatchStateAsync(IGame game, string patchName, List resolvedConflicts, IGrouping item, IList files, IEnumerable matchedConflicts, + bool readOnlyMode) { - var synced = await SyncPatchStatesAsync(matchedConflicts, item, patchName, game, readOnlyMode, files.ToArray()); + var synced = await SyncPatchStatesAsync(matchedConflicts, item, patchName, game, readOnlyMode, [.. files]); if (synced) { matchedConflicts.ToList().ForEach(p => @@ -3281,7 +3346,7 @@ protected virtual async Task SyncPatchStateAsync(IGame game, string patchName, L p.ResetType = ResetType.Resolved; } }); - item.ToList().ForEach((diff) => + item.ToList().ForEach(diff => { var existingConflict = resolvedConflicts.FirstOrDefault(p => p.TypeAndId.Equals(diff.TypeAndId)); if (existingConflict != null) @@ -3301,7 +3366,7 @@ protected virtual async Task SyncPatchStateAsync(IGame game, string patchName, L /// The game. /// if set to true [read only mode]. /// The files. - /// true if XXXX, false otherwise. + /// true if synced, false otherwise. protected virtual async Task SyncPatchStatesAsync(IEnumerable currentConflicts, IEnumerable cachedConflicts, string patchName, IGame game, bool readOnlyMode, params string[] files) { if (IsCachedDefinitionDifferent(currentConflicts, cachedConflicts)) @@ -3310,15 +3375,13 @@ protected virtual async Task SyncPatchStatesAsync(IEnumerable { foreach (var file in files.Distinct()) { - await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters() - { - RootDirectory = GetPatchModDirectory(game, patchName), - Path = file - }); + await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters { RootDirectory = GetPatchModDirectory(game, patchName), Path = file }); } } + return true; } + return false; } @@ -3329,14 +3392,14 @@ await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters() /// The type and identifier. /// Name of the collection. /// Type of the export. - /// true if XXXX, false otherwise. + /// true if unresolved, false otherwise. protected virtual async Task UnResolveConflictAsync(IConflictResult conflictResult, string typeAndId, string collectionName, ExportType exportType) { var game = GameService.GetSelected(); var patchName = GenerateCollectionPatchName(collectionName); if (conflictResult != null && game != null) { - bool purgeFiles = true; + var purgeFiles = true; IIndexedDefinitions indexed; switch (exportType) { @@ -3364,19 +3427,12 @@ protected virtual async Task UnResolveConflictAsync(IConflictResult confli if (purgeFiles) { var patchModDirectory = GetPatchModDirectory(game, patchName); - await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters() - { - RootDirectory = patchModDirectory, - Path = item.File - }); + await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters { RootDirectory = patchModDirectory, Path = item.File }); if (!string.IsNullOrWhiteSpace(item.DiskFile) && item.File != item.DiskFile) { - await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters() - { - RootDirectory = patchModDirectory, - Path = item.DiskFile - }); + await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters { RootDirectory = patchModDirectory, Path = item.DiskFile }); } + if (IsOverwrittenType(item.ValueType)) { collectionMods ??= GetCollectionMods(); @@ -3388,21 +3444,20 @@ await ModWriter.PurgeModDirectoryAsync(new ModWriterParameters() var merged = await ProcessOverwrittenSingleFileDefinitionsAsync(conflictResult, patchName, item.Type); if (merged != null) { - await modPatchExporter.ExportDefinitionAsync(new ModPatchExporterParameters() + await modPatchExporter.ExportDefinitionAsync(new ModPatchExporterParameters { - Game = game.Type, - OverwrittenConflicts = PopulateModPath(merged, collectionMods), - RootPath = GetModDirectoryRootPath(game), - PatchPath = EvaluatePatchNamePath(game, patchName) + Game = game.Type, OverwrittenConflicts = PopulateModPath(merged, collectionMods), RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patchName) }); } } else { - await modPatchExporter.ExportDefinitionAsync(new ModPatchExporterParameters() + await modPatchExporter.ExportDefinitionAsync(new ModPatchExporterParameters { Game = game.Type, - OverwrittenConflicts = PopulateModPath(await overwritten.ToAsyncEnumerable().WhereAwait(async p => !(await conflictResult.ResolvedConflicts.GetByTypeAndIdAsync(p.TypeAndId)).Any()).ToListAsync(), collectionMods), + OverwrittenConflicts = + PopulateModPath(await overwritten.ToAsyncEnumerable().WhereAwait(async p => !(await conflictResult.ResolvedConflicts.GetByTypeAndIdAsync(p.TypeAndId)).Any()).ToListAsync(), + collectionMods), RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patchName) }); @@ -3411,29 +3466,20 @@ await modPatchExporter.ExportDefinitionAsync(new ModPatchExporterParameters() } } } + var allMods = GetInstalledModsInternal(game, false).ToList(); IMod mod; if (!allMods.Any(p => p.Name.Equals(patchName))) { mod = GeneratePatchModDescriptor(allMods, game, patchName); - await ModWriter.CreateModDirectoryAsync(new ModWriterParameters() - { - RootDirectory = game.UserDirectory, - Path = Shared.Constants.ModDirectory - }); - await ModWriter.CreateModDirectoryAsync(new ModWriterParameters() - { - RootDirectory = GetPatchModDirectory(game, patchName) - }); + await ModWriter.CreateModDirectoryAsync(new ModWriterParameters { RootDirectory = game.UserDirectory, Path = Shared.Constants.ModDirectory }); + await ModWriter.CreateModDirectoryAsync(new ModWriterParameters { RootDirectory = GetPatchModDirectory(game, patchName) }); if (game.ModDescriptorType == ModDescriptorType.JsonMetadata) { - await ModWriter.CreateModDirectoryAsync(new ModWriterParameters() - { - RootDirectory = game.UserDirectory, - Path = Shared.Constants.JsonModDirectory - }); + await ModWriter.CreateModDirectoryAsync(new ModWriterParameters { RootDirectory = game.UserDirectory, Path = Shared.Constants.JsonModDirectory }); } - await ModWriter.WriteDescriptorAsync(new ModWriterParameters() + + await ModWriter.WriteDescriptorAsync(new ModWriterParameters { Mod = mod, RootDirectory = game.UserDirectory, @@ -3442,29 +3488,25 @@ await ModWriter.WriteDescriptorAsync(new ModWriterParameters() DescriptorType = MapDescriptorType(game.ModDescriptorType) }, IsPatchMod(mod)); allMods.Add(mod); - Cache.Invalidate(new CacheInvalidateParameters() { Region = ModsCacheRegion, Prefix = game.Type, Keys = new List { GetModsCacheKey(true), GetModsCacheKey(false) } }); + Cache.Invalidate(new CacheInvalidateParameters { Region = ModsCacheRegion, Prefix = game.Type, Keys = [GetModsCacheKey(true), GetModsCacheKey(false)] }); } else { mod = allMods.FirstOrDefault(p => p.Name.Equals(patchName)); } - var state = Cache.Get(new CacheGetParameters() { Region = ModsExportedRegion, Key = ModExportedKey }); + var state = Cache.Get(new CacheGetParameters { Region = ModsExportedRegion, Key = ModExportedKey }); if (state == null || state.Exported.GetValueOrDefault() == false) { await Task.Run(() => { - ModWriter.ApplyModsAsync(new ModWriterParameters() - { - AppendOnly = true, - TopPriorityMods = new List() { mod }, - RootDirectory = game.UserDirectory, - DescriptorType = MapDescriptorType(game.ModDescriptorType) - }).ConfigureAwait(false); + ModWriter.ApplyModsAsync(new ModWriterParameters { AppendOnly = true, TopPriorityMods = [mod], RootDirectory = game.UserDirectory, DescriptorType = MapDescriptorType(game.ModDescriptorType) }) + .ConfigureAwait(false); }).ConfigureAwait(false); - Cache.Set(new CacheAddParameters() { Region = ModsExportedRegion, Key = ModExportedKey, Value = new ModsExportedState() { Exported = true } }); + Cache.Set(new CacheAddParameters { Region = ModsExportedRegion, Key = ModExportedKey, Value = new ModsExportedState { Exported = true } }); } - await modPatchExporter.SaveStateAsync(new ModPatchExporterParameters() + + await modPatchExporter.SaveStateAsync(new ModPatchExporterParameters { LoadOrder = GetCollectionMods(collectionName: collectionName).Select(p => p.DescriptorFile), Mode = MapPatchStateMode(conflictResult.Mode), @@ -3476,11 +3518,13 @@ await modPatchExporter.SaveStateAsync(new ModPatchExporterParameters() CustomConflicts = await GetDefinitionOrDefaultAsync(conflictResult.CustomConflicts), RootPath = GetModDirectoryRootPath(game), PatchPath = EvaluatePatchNamePath(game, patchName), - HasGameDefinitions = await conflictResult.AllConflicts.HasGameDefinitionsAsync() + HasGameDefinitions = await conflictResult.AllConflicts.HasGameDefinitionsAsync(), + AllowedLanguages = conflictResult.AllowedLanguages }); return true; } } + return false; } @@ -3499,19 +3543,19 @@ private class DefinitionOrderSort /// Gets or sets the file. /// /// The file. - public string File { get; set; } + public string File { get; init; } /// /// Gets or sets the order. /// /// The order. - public int Order { get; set; } + public int Order { get; init; } /// /// Gets or sets the type and identifier. /// /// The type and identifier. - public string TypeAndId { get; set; } + public string TypeAndId { get; init; } #endregion Properties } @@ -3527,25 +3571,25 @@ private class EvalState /// Gets or sets the content sha. /// /// The content sha. - public string ContentSha { get; set; } + public string ContentSha { get; init; } /// - /// Gets or sets the name of the fall back file. + /// Gets or sets the name of the fallback file. /// - /// The name of the fall back file. - public string FallBackFileName { get; set; } + /// The name of the fallback file. + public string FallBackFileName { get; init; } /// /// Gets or sets the name of the file. /// /// The name of the file. - public string FileName { get; set; } + public string FileName { get; init; } /// /// Gets or sets the name of the mod. /// /// The name of the mod. - public string ModName { get; set; } + public string ModName { get; init; } #endregion Properties } @@ -3561,7 +3605,7 @@ private class ModsExportedState /// Gets or sets a value indicating whether this is exported. /// /// null if [exported] contains no value, true if [exported]; otherwise, false. - public bool? Exported { get; set; } + public bool? Exported { get; init; } #endregion Properties } @@ -3577,13 +3621,13 @@ private class PatchCollectionState /// Gets or sets a value indicating whether [check in progress]. /// /// true if [check in progress]; otherwise, false. - public bool CheckInProgress { get; set; } + public bool CheckInProgress { get; init; } /// /// Gets or sets a value indicating whether [needs update]. /// /// true if [needs update]; otherwise, false. - public bool NeedsUpdate { get; set; } + public bool NeedsUpdate { get; init; } #endregion Properties } diff --git a/src/IronyModManager.Services/Properties/PublishProfiles/linux-x64.pubxml b/src/IronyModManager.Services/Properties/PublishProfiles/linux-x64.pubxml index 04545ee0d..b8f7f6db0 100644 --- a/src/IronyModManager.Services/Properties/PublishProfiles/linux-x64.pubxml +++ b/src/IronyModManager.Services/Properties/PublishProfiles/linux-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\linux-x64\net7.0\publish\linux-x64 + net8.0 + bin\x64\linux-x64\net8.0\publish\linux-x64 linux-x64 true False diff --git a/src/IronyModManager.Services/Properties/PublishProfiles/osx-x64.pubxml b/src/IronyModManager.Services/Properties/PublishProfiles/osx-x64.pubxml index df6bd9f9f..ff92dad82 100644 --- a/src/IronyModManager.Services/Properties/PublishProfiles/osx-x64.pubxml +++ b/src/IronyModManager.Services/Properties/PublishProfiles/osx-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\osx-x64\net7.0\publish\osx-x64 + net8.0 + bin\x64\osx-x64\net8.0\publish\osx-x64 osx-x64 true False diff --git a/src/IronyModManager.Services/Properties/PublishProfiles/win-x64.pubxml b/src/IronyModManager.Services/Properties/PublishProfiles/win-x64.pubxml index 1cd122792..cbd6468db 100644 --- a/src/IronyModManager.Services/Properties/PublishProfiles/win-x64.pubxml +++ b/src/IronyModManager.Services/Properties/PublishProfiles/win-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\win-x64\net7.0\publish\win-x64 + net8.0 + bin\x64\win-x64\net8.0\publish\win-x64 win-x64 true False diff --git a/src/IronyModManager.Shared/Extensions.DateTime.cs b/src/IronyModManager.Shared/Extensions.DateTime.cs new file mode 100644 index 000000000..2e10e66c9 --- /dev/null +++ b/src/IronyModManager.Shared/Extensions.DateTime.cs @@ -0,0 +1,41 @@ +// *********************************************************************** +// Assembly : IronyModManager.Shared +// Author : Mario +// Created : 02-17-2024 +// +// Last Modified By : Mario +// Last Modified On : 02-17-2024 +// *********************************************************************** +// +// Mario +// +// +// *********************************************************************** + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace IronyModManager.Shared +{ + /// + /// The extensions. + /// + public partial class Extensions + { + #region Methods + + /// + /// Is date same. + /// + /// The date 1. + /// The date 2. + /// A bool. + public static bool IsDateSame(this DateTime date1, DateTime date2) + { + return date1.Date == date2.Date; + } + + #endregion Methods + } +} diff --git a/src/IronyModManager.Shared/Extensions.Double.cs b/src/IronyModManager.Shared/Extensions.Double.cs new file mode 100644 index 000000000..f7d9e6ece --- /dev/null +++ b/src/IronyModManager.Shared/Extensions.Double.cs @@ -0,0 +1,85 @@ +// *********************************************************************** +// Assembly : IronyModManager.Shared +// Author : Mario +// Created : 02-20-2024 +// +// Last Modified By : Mario +// Last Modified On : 02-23-2024 +// *********************************************************************** +// +// Mario +// +// +// *********************************************************************** + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace IronyModManager.Shared +{ + /// + /// Class Extensions. + /// + public static partial class Extensions + { + #region Fields + + /// + /// A private const double named defaultTolerance. + /// + private const double DefaultTolerance = 0.01; + + #endregion Fields + + #region Methods + + /// + /// Is nearly equal. + /// + /// The A. + /// The B. + /// The tolerance. + /// A bool. + public static bool IsNearlyEqual(this double a, double b, double tolerance) + { + return Math.Abs(a - b) < tolerance; + } + + /// + /// Is nearly equal. + /// + /// The A. + /// The B. + /// A bool. + public static bool IsNearlyEqual(this double a, double b) + { + return IsNearlyEqual(a, b, DefaultTolerance); + } + + /// + /// Is not nearly equal. + /// + /// The A. + /// The B. + /// A bool. + public static bool IsNotNearlyEqual(this double a, double b) + { + return !IsNearlyEqual(a, b); + } + + /// + /// Is not nearly equal. + /// + /// The A. + /// The B. + /// The tolerance. + /// A bool. + public static bool IsNotNearlyEqual(this double a, double b, double tolerance) + { + return !IsNearlyEqual(a, b, tolerance); + } + + #endregion Methods + } +} diff --git a/src/IronyModManager.Shared/Extensions.cs b/src/IronyModManager.Shared/Extensions.cs index 1a550a9c8..4a413cc33 100644 --- a/src/IronyModManager.Shared/Extensions.cs +++ b/src/IronyModManager.Shared/Extensions.cs @@ -1,26 +1,26 @@ - -// *********************************************************************** +// *********************************************************************** // Assembly : IronyModManager.Shared // Author : Mario // Created : 02-16-2020 // // Last Modified By : Mario -// Last Modified On : 10-31-2023 +// Last Modified On : 02-25-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using System.Text.RegularExpressions; namespace IronyModManager.Shared { - /// /// Class Extensions. /// @@ -37,21 +37,44 @@ public static partial class Extensions /// /// The empty string characters /// - private static readonly string[] emptyStringCharacters = new string[] { " " }; + private static readonly string[] emptyStringCharacters = [" "]; /// /// The tab space /// private static readonly string tabSpace = new(' ', 4); + /// /// The invalid file name characters /// - private static IEnumerable invalidFileNameCharacters = null; + private static IEnumerable invalidFileNameCharacters; #endregion Fields #region Methods + /// + /// Capitalizes an every first letter. + /// + /// The value. + /// A string. + public static string CapitalizeEveryFirstLetter(this string value) + { + if (!string.IsNullOrWhiteSpace(value)) + { + var sb = new StringBuilder(); + var split = value.Split(' '); + foreach (var s in split) + { + sb.Append($" {char.ToUpperInvariant(s.FirstOrDefault())}{s[1..]}"); + } + + return sb.ToString().Trim(); + } + + return value; + } + /// /// Generates the short file name hash identifier. /// @@ -64,11 +87,13 @@ public static string GenerateShortFileNameHashId(this string value, int maxLengt { return string.Empty; } + var hash = value.CalculateSHA().GenerateValidFileName(); if (hash.Length > maxLength) { return hash[..maxLength]; } + return hash; } @@ -83,6 +108,7 @@ public static string GenerateValidFileName(this string value) { return value; } + var fileName = GetInvalidFileNameChars().Aggregate(value, (current, character) => current.Replace(character.ToString(), string.Empty)); fileName = emptyStringCharacters.Aggregate(fileName, (a, b) => a.Replace(b, "_")); @@ -112,6 +138,7 @@ public static string ReplaceTabs(this string value) { return value; } + return value.Replace("\t", tabSpace); } @@ -137,6 +164,7 @@ public static string StandardizeDirectorySeparator(this string value) { return string.Empty; } + return value.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar); } @@ -185,7 +213,7 @@ public static string TrimStart(this string input, string value, StringComparison { while (!string.IsNullOrEmpty(input) && input.StartsWith(value, type)) { - input = input[(value.Length)..]; + input = input[value.Length..]; } } diff --git a/src/IronyModManager.Shared/GCRunner.cs b/src/IronyModManager.Shared/GCRunner.cs new file mode 100644 index 000000000..380b9762c --- /dev/null +++ b/src/IronyModManager.Shared/GCRunner.cs @@ -0,0 +1,59 @@ +// *********************************************************************** +// Assembly : IronyModManager.Shared +// Author : Mario +// Created : 02-23-2024 +// +// Last Modified By : Mario +// Last Modified On : 02-23-2024 +// *********************************************************************** +// +// Mario +// +// +// *********************************************************************** + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace IronyModManager.Shared +{ + /// + /// The gc runner. + /// + public class GCRunner + { + #region Methods + + /// + /// Run gc. + /// + /// The mode. + /// If true, blocking. + /// + public static void RunGC(GCCollectionMode mode, bool blocking = false) + { + if (blocking) + { + Task.Run(() => + { + GC.Collect(GC.MaxGeneration, mode, blocking, blocking); + GC.WaitForPendingFinalizers(); + GC.Collect(GC.MaxGeneration, mode, blocking, blocking); + }); + } + else + { + Task.Run(() => + { + GC.Collect(GC.MaxGeneration, mode); + GC.WaitForPendingFinalizers(); + GC.Collect(GC.MaxGeneration, mode); + }); + } + } + + #endregion Methods + } +} diff --git a/src/IronyModManager.Shared/IronyModManager.Shared.csproj b/src/IronyModManager.Shared/IronyModManager.Shared.csproj index 47a8e496c..33fa41301 100644 --- a/src/IronyModManager.Shared/IronyModManager.Shared.csproj +++ b/src/IronyModManager.Shared/IronyModManager.Shared.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 Irony Mod Manager Shared Component LICENSE logo.png @@ -40,7 +40,7 @@ - + @@ -49,16 +49,17 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - - + + diff --git a/src/IronyModManager.Shared/LocalizationResources.cs b/src/IronyModManager.Shared/LocalizationResources.cs index 5906ed266..6f0ffff3a 100644 --- a/src/IronyModManager.Shared/LocalizationResources.cs +++ b/src/IronyModManager.Shared/LocalizationResources.cs @@ -27,6 +27,12 @@ public static class Actions public const string DLC = Prefix + "DLC"; } } + public static class JokeError + { + public const string Prefix = "JokeError."; + public const string Title = Prefix + "Title"; + public const string Message = Prefix + "Message"; + } public static class FatalError { public const string Prefix = "FatalError."; @@ -132,6 +138,22 @@ public static class Games public const string Victoria3 = Prefix + "Victoria3"; public const string STInfinite = Prefix + "STInfinite"; } + public static class GameLanguages + { + public const string Prefix = "GameLanguages."; + public const string l_english = Prefix + "l_english"; + public const string l_braz_por = Prefix + "l_braz_por"; + public const string l_french = Prefix + "l_french"; + public const string l_german = Prefix + "l_german"; + public const string l_polish = Prefix + "l_polish"; + public const string l_russian = Prefix + "l_russian"; + public const string l_simp_chinese = Prefix + "l_simp_chinese"; + public const string l_spanish = Prefix + "l_spanish"; + public const string l_chinese = Prefix + "l_chinese"; + public const string l_traditional_chinese = Prefix + "l_traditional_chinese"; + public const string l_japanese = Prefix + "l_japanese"; + public const string l_korean = Prefix + "l_korean"; + } public static class Installed_Mods { public const string Prefix = "Installed_Mods."; @@ -433,6 +455,7 @@ public static class ContextMenu public const string Redo = Prefix + "Redo"; public const string Editor = Prefix + "Editor"; public const string ReadonlyEditor = Prefix + "ReadonlyEditor"; + public const string ToggleCompare = Prefix + "ToggleCompare"; } public static class EditorContextMenu { @@ -598,6 +621,12 @@ public static class Game public const string AutoConfigure = Prefix + "AutoConfigure"; public const string CustomModPath = Prefix + "CustomModPath"; } + public static class ConflictSolver + { + public const string Prefix = "Options.ConflictSolver."; + public const string Title = Prefix + "Title"; + public const string AllowedLanguages = Prefix + "AllowedLanguages"; + } public static class Updates { public const string Prefix = "Options.Updates."; diff --git a/src/IronyModManager.Shared/Properties/PublishProfiles/linux-x64.pubxml b/src/IronyModManager.Shared/Properties/PublishProfiles/linux-x64.pubxml index 04545ee0d..b8f7f6db0 100644 --- a/src/IronyModManager.Shared/Properties/PublishProfiles/linux-x64.pubxml +++ b/src/IronyModManager.Shared/Properties/PublishProfiles/linux-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\linux-x64\net7.0\publish\linux-x64 + net8.0 + bin\x64\linux-x64\net8.0\publish\linux-x64 linux-x64 true False diff --git a/src/IronyModManager.Shared/Properties/PublishProfiles/osx-x64.pubxml b/src/IronyModManager.Shared/Properties/PublishProfiles/osx-x64.pubxml index df6bd9f9f..ff92dad82 100644 --- a/src/IronyModManager.Shared/Properties/PublishProfiles/osx-x64.pubxml +++ b/src/IronyModManager.Shared/Properties/PublishProfiles/osx-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\osx-x64\net7.0\publish\osx-x64 + net8.0 + bin\x64\osx-x64\net8.0\publish\osx-x64 osx-x64 true False diff --git a/src/IronyModManager.Shared/Properties/PublishProfiles/win-x64.pubxml b/src/IronyModManager.Shared/Properties/PublishProfiles/win-x64.pubxml index 1cd122792..cbd6468db 100644 --- a/src/IronyModManager.Shared/Properties/PublishProfiles/win-x64.pubxml +++ b/src/IronyModManager.Shared/Properties/PublishProfiles/win-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\win-x64\net7.0\publish\win-x64 + net8.0 + bin\x64\win-x64\net8.0\publish\win-x64 win-x64 true False diff --git a/src/IronyModManager.Storage.Common/IronyModManager.Storage.Common.csproj b/src/IronyModManager.Storage.Common/IronyModManager.Storage.Common.csproj index 2a65469a8..c25c6c2b6 100644 --- a/src/IronyModManager.Storage.Common/IronyModManager.Storage.Common.csproj +++ b/src/IronyModManager.Storage.Common/IronyModManager.Storage.Common.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 Irony Mod Manager Storage Common Component LICENSE logo.png diff --git a/src/IronyModManager.Storage.Common/Properties/PublishProfiles/linux-x64.pubxml b/src/IronyModManager.Storage.Common/Properties/PublishProfiles/linux-x64.pubxml index 04545ee0d..b8f7f6db0 100644 --- a/src/IronyModManager.Storage.Common/Properties/PublishProfiles/linux-x64.pubxml +++ b/src/IronyModManager.Storage.Common/Properties/PublishProfiles/linux-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\linux-x64\net7.0\publish\linux-x64 + net8.0 + bin\x64\linux-x64\net8.0\publish\linux-x64 linux-x64 true False diff --git a/src/IronyModManager.Storage.Common/Properties/PublishProfiles/osx-x64.pubxml b/src/IronyModManager.Storage.Common/Properties/PublishProfiles/osx-x64.pubxml index df6bd9f9f..ff92dad82 100644 --- a/src/IronyModManager.Storage.Common/Properties/PublishProfiles/osx-x64.pubxml +++ b/src/IronyModManager.Storage.Common/Properties/PublishProfiles/osx-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\osx-x64\net7.0\publish\osx-x64 + net8.0 + bin\x64\osx-x64\net8.0\publish\osx-x64 osx-x64 true False diff --git a/src/IronyModManager.Storage.Common/Properties/PublishProfiles/win-x64.pubxml b/src/IronyModManager.Storage.Common/Properties/PublishProfiles/win-x64.pubxml index 1cd122792..cbd6468db 100644 --- a/src/IronyModManager.Storage.Common/Properties/PublishProfiles/win-x64.pubxml +++ b/src/IronyModManager.Storage.Common/Properties/PublishProfiles/win-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\win-x64\net7.0\publish\win-x64 + net8.0 + bin\x64\win-x64\net8.0\publish\win-x64 win-x64 true False diff --git a/src/IronyModManager.Storage.Tests/IronyModManager.Storage.Tests.csproj b/src/IronyModManager.Storage.Tests/IronyModManager.Storage.Tests.csproj index edfc50ec7..c13cb4553 100644 --- a/src/IronyModManager.Storage.Tests/IronyModManager.Storage.Tests.csproj +++ b/src/IronyModManager.Storage.Tests/IronyModManager.Storage.Tests.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 IronyModManager.Storage.Tests LICENSE logo.png @@ -43,16 +43,16 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/IronyModManager.Storage/Database.cs b/src/IronyModManager.Storage/Database.cs index d4ba8a4e3..66c7e50a5 100644 --- a/src/IronyModManager.Storage/Database.cs +++ b/src/IronyModManager.Storage/Database.cs @@ -4,15 +4,17 @@ // Created : 01-11-2020 // // Last Modified By : Mario -// Last Modified On : 03-16-2021 +// Last Modified On : 02-17-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; +using System.Linq; using IronyModManager.DI; using IronyModManager.Models.Common; using IronyModManager.Shared; @@ -28,7 +30,7 @@ namespace IronyModManager.Storage /// /// /// - public class Database : PropertyChangedModelBase, IDatabase + public sealed class Database : PropertyChangedModelBase, IDatabase { #region Constructors @@ -37,11 +39,11 @@ public class Database : PropertyChangedModelBase, IDatabase /// public Database() { - Themes = new List(); - Games = new List(); - ModCollection = new List(); - GameSettings = new List(); - NotificationPosition = new List(); + Themes = []; + Games = []; + ModCollection = []; + GameSettings = []; + NotificationPosition = []; } #endregion Constructors @@ -53,53 +55,53 @@ public Database() /// /// The state of the application. [Trackable] - public virtual IAppState AppState { get; set; } = DIResolver.Get(); + public IAppState AppState { get; set; } = DIResolver.Get(); /// /// Gets or sets the games. /// /// The games. - public virtual IList Games { get; set; } + public IList Games { get; set; } /// /// Gets or sets the game settings. /// /// The game settings. [Trackable] - public virtual IEnumerable GameSettings { get; set; } + public IEnumerable GameSettings { get; set; } /// /// Gets or sets the mod collection. /// /// The mod collection. [Trackable] - public virtual IEnumerable ModCollection { get; set; } + public IEnumerable ModCollection { get; set; } /// /// Gets or sets the notification position. /// /// The notification position. - public virtual IList NotificationPosition { get; set; } + public IList NotificationPosition { get; set; } /// /// Gets or sets the preferences. /// /// The preferences. [Trackable] - public virtual IPreferences Preferences { get; set; } = DIResolver.Get(); + public IPreferences Preferences { get; set; } = DIResolver.Get(); /// /// Gets or sets the themes. /// /// The themes. - public virtual IList Themes { get; set; } + public IList Themes { get; set; } /// /// Gets or sets the state of the window. /// /// The state of the window. [Trackable] - public virtual IWindowState WindowState { get; set; } = DIResolver.Get(); + public IWindowState WindowState { get; set; } = DIResolver.Get(); #endregion Properties } diff --git a/src/IronyModManager.Storage/IronyModManager.Storage.csproj b/src/IronyModManager.Storage/IronyModManager.Storage.csproj index 503fc0355..718083a4d 100644 --- a/src/IronyModManager.Storage/IronyModManager.Storage.csproj +++ b/src/IronyModManager.Storage/IronyModManager.Storage.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 Mario Mario Irony Mod Manager Storage Component @@ -47,8 +47,8 @@ - - + + diff --git a/src/IronyModManager.Storage/Properties/PublishProfiles/linux-x64.pubxml b/src/IronyModManager.Storage/Properties/PublishProfiles/linux-x64.pubxml index 04545ee0d..b8f7f6db0 100644 --- a/src/IronyModManager.Storage/Properties/PublishProfiles/linux-x64.pubxml +++ b/src/IronyModManager.Storage/Properties/PublishProfiles/linux-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\linux-x64\net7.0\publish\linux-x64 + net8.0 + bin\x64\linux-x64\net8.0\publish\linux-x64 linux-x64 true False diff --git a/src/IronyModManager.Storage/Properties/PublishProfiles/osx-x64.pubxml b/src/IronyModManager.Storage/Properties/PublishProfiles/osx-x64.pubxml index df6bd9f9f..ff92dad82 100644 --- a/src/IronyModManager.Storage/Properties/PublishProfiles/osx-x64.pubxml +++ b/src/IronyModManager.Storage/Properties/PublishProfiles/osx-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\osx-x64\net7.0\publish\osx-x64 + net8.0 + bin\x64\osx-x64\net8.0\publish\osx-x64 osx-x64 true False diff --git a/src/IronyModManager.Storage/Properties/PublishProfiles/win-x64.pubxml b/src/IronyModManager.Storage/Properties/PublishProfiles/win-x64.pubxml index 1cd122792..cbd6468db 100644 --- a/src/IronyModManager.Storage/Properties/PublishProfiles/win-x64.pubxml +++ b/src/IronyModManager.Storage/Properties/PublishProfiles/win-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\win-x64\net7.0\publish\win-x64 + net8.0 + bin\x64\win-x64\net8.0\publish\win-x64 win-x64 true False diff --git a/src/IronyModManager.Storage/Store/JsonStore.cs b/src/IronyModManager.Storage/Store/JsonStore.cs index 4cb018b3f..701f3310c 100644 --- a/src/IronyModManager.Storage/Store/JsonStore.cs +++ b/src/IronyModManager.Storage/Store/JsonStore.cs @@ -1,17 +1,17 @@ - -// *********************************************************************** +// *********************************************************************** // Assembly : IronyModManager.Storage // Author : Mario // Created : 01-20-2020 // // Last Modified By : Mario -// Last Modified On : 01-23-2024 +// Last Modified On : 02-25-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections; using System.Collections.Generic; @@ -27,7 +27,6 @@ namespace IronyModManager.Storage { - /// /// Class JsonStore. /// Implements the @@ -41,12 +40,12 @@ internal class JsonStore : IStore /// /// The converters /// - private static readonly JsonConverter[] converters = new JsonConverter[] { new StoreConverter(), new DIConverter() }; + private static readonly JsonConverter[] converters = [new StoreConverter(), new DIConverter()]; /// /// The root paths /// - private static string[] rootPaths = null; + private static string[] rootPaths; /// /// The storage item @@ -67,9 +66,10 @@ protected internal static string[] RootPaths { if (rootPaths == null) { - var col = new List() { InitPath(true), InitPath(false) }; + var col = new List { InitPath(true), InitPath(false) }; rootPaths = col.Distinct().ToArray(); } + return rootPaths; } } @@ -81,6 +81,7 @@ protected internal static string[] RootPaths /// /// Clears all. /// + /// /// public void ClearAll() { @@ -91,6 +92,7 @@ public void ClearAll() /// Clears the data. /// /// The identifier. + /// /// public void ClearData(string id) { @@ -117,12 +119,9 @@ static List readItems(string path) catch { } } - if (items == null) - { - items = new List(); - } - return items; + return items ??= []; } + List storeItems; var filePath = GetFilePath(id, true); @@ -134,14 +133,7 @@ static List readItems(string path) var tempDiskDateItem = tempDiskItems.FirstOrDefault(p => p.Type == Store.Constants.StoreDateId); var diskDate = diskDateItem != null ? (DateTime)diskDateItem.Value : DateTime.MinValue; var tempDiskDate = tempDiskDateItem != null ? (DateTime)tempDiskDateItem.Value : DateTime.MinValue; - if (tempDiskDate > diskDate) - { - storeItems = tempDiskItems.Where(p => p != tempDiskDateItem).ToList(); - } - else - { - storeItems = diskItems.Where(p => p != diskDateItem).ToList(); - } + storeItems = tempDiskDate > diskDate ? tempDiskItems.Where(p => p != tempDiskDateItem).ToList() : diskItems.Where(p => p != diskDateItem).ToList(); return storeItems.ToDictionary(item => item.Name, item => item.Value); } @@ -150,6 +142,7 @@ static List readItems(string path) /// Lists the ids. /// /// IEnumerable<System.String>. + /// /// public IEnumerable ListIds() { @@ -165,14 +158,14 @@ public void SetData(string id, IDictionary values) { var filePath = GetFilePath(id); var tempFilePath = GetTempPath(filePath); - var list = values.Select(kvp => new StoreItem() { Name = kvp.Key, Value = kvp.Value, Type = FormatTypeName(kvp.Value) }).ToList(); - list.Add(new StoreItem() { Name = Store.Constants.StoreDateId, Type = Store.Constants.StoreDateId, Value = DateTime.UtcNow }); - var serialized = JsonConvert.SerializeObject(list, new JsonSerializerSettings() { Formatting = Formatting.None, TypeNameHandling = TypeNameHandling.None }); + var list = values.Select(kvp => new StoreItem { Name = kvp.Key, Value = kvp.Value, Type = FormatTypeName(kvp.Value) }).ToList(); + list.Add(new StoreItem { Name = Store.Constants.StoreDateId, Type = Store.Constants.StoreDateId, Value = DateTime.UtcNow }); + var serialized = JsonConvert.SerializeObject(list, new JsonSerializerSettings { Formatting = Formatting.None, TypeNameHandling = TypeNameHandling.None }); - string directory = Path.GetDirectoryName(filePath); + var directory = Path.GetDirectoryName(filePath); if (!Directory.Exists(directory)) { - Directory.CreateDirectory(directory); + Directory.CreateDirectory(directory!); } File.WriteAllText(tempFilePath, serialized); @@ -196,18 +189,22 @@ private static string FormatTypeName(object instance) { type = instance.GetType(); } + if (typeof(IPropertyChangedModel).IsAssignableFrom(type)) { var name = type.FullName; - var names = name.Split(Store.Constants.Dot, StringSplitOptions.RemoveEmptyEntries); + var names = name!.Split(Store.Constants.Dot, StringSplitOptions.RemoveEmptyEntries); return string.Join(Store.Constants.Dot, names); } else if (typeof(IEnumerable).IsAssignableFrom(type)) { - var name = type.GetGenericArguments().SingleOrDefault().FullName; - var names = name.Split(Store.Constants.Dot, StringSplitOptions.RemoveEmptyEntries); + // Sure why not make a breaking change + var name = !type.IsArray ? type.GetGenericArguments().SingleOrDefault()!.FullName : type.GetElementType()!.FullName; + + var names = name!.Split(Store.Constants.Dot, StringSplitOptions.RemoveEmptyEntries); return $"{nameof(IEnumerable)}{Store.Constants.EnumerableOpenTag}{string.Join(Store.Constants.Dot, names)}{Store.Constants.EnumerableCloseTag}"; } + return type.FullName; } @@ -232,8 +229,8 @@ private static string InitPath(bool useProperSeparator = true) var secondSegment = string.Empty; var entryAssembly = Assembly.GetEntryAssembly(); - var companyAttribute = (AssemblyCompanyAttribute)Attribute.GetCustomAttribute(entryAssembly, typeof(AssemblyCompanyAttribute)); - if (!string.IsNullOrEmpty(companyAttribute.Company)) + var companyAttribute = (AssemblyCompanyAttribute)Attribute.GetCustomAttribute(entryAssembly!, typeof(AssemblyCompanyAttribute)); + if (!string.IsNullOrEmpty(companyAttribute!.Company)) { if (!useProperSeparator) { @@ -244,8 +241,9 @@ private static string InitPath(bool useProperSeparator = true) fistSegment = $"{companyAttribute.Company}{Path.DirectorySeparatorChar}"; } } + var titleAttribute = (AssemblyTitleAttribute)Attribute.GetCustomAttribute(entryAssembly, typeof(AssemblyTitleAttribute)); - if (!string.IsNullOrEmpty(titleAttribute.Title)) + if (!string.IsNullOrEmpty(titleAttribute!.Title)) { if (!useProperSeparator) { @@ -268,7 +266,7 @@ private static string InitPath(bool useProperSeparator = true) /// System.String. private string GetFilePath(string id, bool lookForOlderVersion = false) { - string mainPath = string.Empty; + var mainPath = string.Empty; foreach (var root in RootPaths) { var version = FileVersionInfo.GetVersionInfo(GetType().Assembly.Location); @@ -277,10 +275,12 @@ private string GetFilePath(string id, bool lookForOlderVersion = false) { mainPath = path; } + if (File.Exists(path)) { return path; } + if (lookForOlderVersion) { if (storageItem == null && Directory.Exists(root)) @@ -293,38 +293,29 @@ private string GetFilePath(string id, bool lookForOlderVersion = false) var versionData = item.Split("_", StringSplitOptions.RemoveEmptyEntries)[1].Replace(Shared.Constants.JsonExtension, string.Empty).Trim(); if (System.Version.TryParse(versionData, out var parsedVersion)) { - dbs.Add(new StorageItem() - { - FileName = item, - Version = parsedVersion - }); + dbs.Add(new StorageItem { FileName = item, Version = parsedVersion }); } else { - dbs.Add(new StorageItem() - { - FileName = item, - Version = new System.Version(0, 0, 0, 0) - }); + dbs.Add(new StorageItem { FileName = item, Version = new System.Version(0, 0, 0, 0) }); } } else { - dbs.Add(new StorageItem() - { - FileName = item, - Version = new System.Version(0, 0, 0, 0) - }); + dbs.Add(new StorageItem { FileName = item, Version = new System.Version(0, 0, 0, 0) }); } } + storageItem = dbs.OrderByDescending(p => p.Version).FirstOrDefault(); } + if (storageItem != null) { return storageItem.FileName; } } } + return mainPath; } diff --git a/src/IronyModManager.Tests.Common/IronyModManager.Tests.Common.csproj b/src/IronyModManager.Tests.Common/IronyModManager.Tests.Common.csproj index 1cfb1b8c2..e8d294121 100644 --- a/src/IronyModManager.Tests.Common/IronyModManager.Tests.Common.csproj +++ b/src/IronyModManager.Tests.Common/IronyModManager.Tests.Common.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 IronyModManager.Tests.Common LICENSE logo.png @@ -43,17 +43,17 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/IronyModManager.Tests/IronyModManager.Tests.csproj b/src/IronyModManager.Tests/IronyModManager.Tests.csproj index 30462ba59..f70383ed3 100644 --- a/src/IronyModManager.Tests/IronyModManager.Tests.csproj +++ b/src/IronyModManager.Tests/IronyModManager.Tests.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 IronyModManager.Tests LICENSE logo.png @@ -27,17 +27,17 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/IronyModManager.Updater/IronyModManager.Updater.csproj b/src/IronyModManager.Updater/IronyModManager.Updater.csproj index 25cb781d7..fa49a52ff 100644 --- a/src/IronyModManager.Updater/IronyModManager.Updater.csproj +++ b/src/IronyModManager.Updater/IronyModManager.Updater.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 ../IronyModManager/Assets/logo.ico IronyModManager Updater Component LICENSE diff --git a/src/IronyModManager.Updater/Properties/PublishProfiles/linux-x64.pubxml b/src/IronyModManager.Updater/Properties/PublishProfiles/linux-x64.pubxml index 04545ee0d..b8f7f6db0 100644 --- a/src/IronyModManager.Updater/Properties/PublishProfiles/linux-x64.pubxml +++ b/src/IronyModManager.Updater/Properties/PublishProfiles/linux-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\linux-x64\net7.0\publish\linux-x64 + net8.0 + bin\x64\linux-x64\net8.0\publish\linux-x64 linux-x64 true False diff --git a/src/IronyModManager.Updater/Properties/PublishProfiles/osx-x64.pubxml b/src/IronyModManager.Updater/Properties/PublishProfiles/osx-x64.pubxml index df6bd9f9f..ff92dad82 100644 --- a/src/IronyModManager.Updater/Properties/PublishProfiles/osx-x64.pubxml +++ b/src/IronyModManager.Updater/Properties/PublishProfiles/osx-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\osx-x64\net7.0\publish\osx-x64 + net8.0 + bin\x64\osx-x64\net8.0\publish\osx-x64 osx-x64 true False diff --git a/src/IronyModManager.Updater/Properties/PublishProfiles/win-x64.pubxml b/src/IronyModManager.Updater/Properties/PublishProfiles/win-x64.pubxml index 1cd122792..cbd6468db 100644 --- a/src/IronyModManager.Updater/Properties/PublishProfiles/win-x64.pubxml +++ b/src/IronyModManager.Updater/Properties/PublishProfiles/win-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\win-x64\net7.0\publish\win-x64 + net8.0 + bin\x64\win-x64\net8.0\publish\win-x64 win-x64 true False diff --git a/src/IronyModManager/App.xaml.cs b/src/IronyModManager/App.xaml.cs index 9e9968d26..b252cb3cb 100644 --- a/src/IronyModManager/App.xaml.cs +++ b/src/IronyModManager/App.xaml.cs @@ -1,18 +1,19 @@ - -// *********************************************************************** +// *********************************************************************** // Assembly : IronyModManager // Author : Mario // Created : 01-10-2020 // // Last Modified By : Mario -// Last Modified On : 10-29-2023 +// Last Modified On : 02-15-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; @@ -40,7 +41,6 @@ namespace IronyModManager { - /// /// Class App. /// Implements the @@ -54,7 +54,7 @@ public class App : Application /// /// The show fatal notification /// - private bool showFatalNotification = false; + private bool showFatalNotification; #endregion Fields @@ -77,7 +77,7 @@ public App() /// public override void Initialize() { - showFatalNotification = StaticResources.CommandLineOptions != null && StaticResources.CommandLineOptions.ShowFatalErrorNotification; + showFatalNotification = StaticResources.CommandLineOptions.ShowFatalErrorNotification; AvaloniaXamlLoader.Load(this); if (!Design.IsDesignMode) { @@ -118,16 +118,33 @@ public override void OnFrameworkInitializationCompleted() /// protected virtual void HandleCommandLine() { - if (StaticResources.CommandLineOptions != null && !string.IsNullOrWhiteSpace(StaticResources.CommandLineOptions.GameAbrv)) + static void setGame(bool raiseOnlyEvent = false) { - var gameService = DIResolver.Get(); - var games = gameService.Get(); - var game = games.FirstOrDefault(g => g.Abrv.Equals(StaticResources.CommandLineOptions.GameAbrv, StringComparison.OrdinalIgnoreCase)); - if (game != null) + if (!string.IsNullOrWhiteSpace(StaticResources.CommandLineOptions.GameAbrv)) { - gameService.SetSelected(games, game); + var gameService = DIResolver.Get(); + var games = gameService.Get(); + var game = games.FirstOrDefault(g => g.Abrv.Equals(StaticResources.CommandLineOptions.GameAbrv, StringComparison.OrdinalIgnoreCase)); + if (game != null) + { + if (raiseOnlyEvent) + { + var mbus = DIResolver.Get(); + mbus.Publish(new ActiveGameRequestEvent(game)); + } + else + { + gameService.SetSelected(games, game); + } + } } } + + StaticResources.CommandLineArgsChanged += () => + { + setGame(true); + }; + setGame(); } /// @@ -142,8 +159,10 @@ static double validateSize(double minValue, double value) { return minValue; } + return value; } + var stateService = DIResolver.Get(); desktop.MainWindow.Height = validateSize(desktop.MainWindow.MinHeight, desktop.MainWindow.Height) + desktop.MainWindow.ExtendClientAreaTitleBarHeightHint; desktop.MainWindow.Width = validateSize(desktop.MainWindow.MinWidth, desktop.MainWindow.Width); @@ -162,7 +181,7 @@ protected virtual void InitAppTitle(IClassicDesktopStyleApplicationLifetime desk { SetAppTitle(desktop); var listener = MessageBus.Current.Listen(); - listener.SubscribeObservable(x => + listener.SubscribeObservable(_ => { SetAppTitle(desktop); }); @@ -184,18 +203,16 @@ protected virtual void InitCulture() protected virtual void SetAppTitle(IClassicDesktopStyleApplicationLifetime desktop) { var appTitle = IronyFormatter.Format(DIResolver.Get().GetResource(LocalizationResources.App.Title), - new - { - AppVersion = FileVersionInfo.GetVersionInfo(GetType().Assembly.Location).ProductVersion.Split("+")[0] - }); + new { AppVersion = FileVersionInfo.GetVersionInfo(GetType().Assembly.Location).ProductVersion!.Split("+")[0] }); if (File.Exists(Constants.TitleSuffixFilename)) { var suffix = File.ReadAllLines(Constants.TitleSuffixFilename); - if (suffix.Any()) + if (suffix.Length != 0) { appTitle = $"{appTitle} ({suffix.FirstOrDefault()})"; } } + desktop.MainWindow.Title = appTitle; } @@ -209,12 +226,13 @@ protected virtual async Task VerifyWritePermissionsAsync() await Task.Delay(5000); var permissionService = DIResolver.Get(); var permissions = permissionService.VerifyPermissions(); - if (permissions.Any() && permissions.Any(p => !p.Valid)) + if (permissions.Count != 0 && permissions.Any(p => !p.Valid)) { var notificationAction = DIResolver.Get(); var locManager = DIResolver.Get(); var title = locManager.GetResource(LocalizationResources.UnableToWriteError.Title); - var message = IronyFormatter.Format(locManager.GetResource(LocalizationResources.UnableToWriteError.Message), new { Environment.NewLine, Paths = string.Join(Environment.NewLine, permissions.Where(p => !p.Valid).Select(p => p.Path).ToList()) }); + var message = IronyFormatter.Format(locManager.GetResource(LocalizationResources.UnableToWriteError.Message), + new { Environment.NewLine, Paths = string.Join(Environment.NewLine, permissions.Where(p => !p.Valid).Select(p => p.Path).ToList()) }); await notificationAction.ShowPromptAsync(title, title, message, NotificationType.Error, PromptType.OK); } } @@ -249,11 +267,14 @@ await Dispatcher.UIThread.SafeInvokeAsync(async () => await appAction.ExitAppAsync(); }); } + var logger = DIResolver.Get(); var lastException = logger.GetLastFatalExceptionMessage(); var locManager = DIResolver.Get(); var title = locManager.GetResource(LocalizationResources.FatalError.Title); - var message = string.IsNullOrWhiteSpace(lastException) ? locManager.GetResource(LocalizationResources.FatalError.Message) : locManager.GetResource(LocalizationResources.FatalError.MessageWithLastError).FormatIronySmart(new { Environment.NewLine, Message = string.Join(Environment.NewLine, lastException.SplitOnNewLine()) }); + var message = string.IsNullOrWhiteSpace(lastException) + ? locManager.GetResource(LocalizationResources.FatalError.Message) + : locManager.GetResource(LocalizationResources.FatalError.MessageWithLastError).FormatIronySmart(new { Environment.NewLine, Message = string.Join(Environment.NewLine, lastException.SplitOnNewLine()) }); var header = locManager.GetResource(LocalizationResources.FatalError.Header); var messageBox = MessageBoxes.GetFatalErrorWindow(title, header, message); @@ -265,6 +286,7 @@ await Dispatcher.UIThread.SafeInvokeAsync(async () => { desktop.MainWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen; } + if (string.IsNullOrWhiteSpace(lastException)) { close().ConfigureAwait(false); @@ -281,7 +303,7 @@ private void InitThemes() themeManager.ApplyTheme(currentTheme.Type); var themeListener = MessageBus.Current.Listen(); - themeListener.SubscribeObservable(x => + themeListener.SubscribeObservable(_ => { OnThemeChanged().ConfigureAwait(true); }); @@ -291,7 +313,7 @@ private void InitThemes() { var window = (MainWindow)Helpers.GetMainWindow(); var id = idGenerator.GetNextId(); - window.ViewModel.TriggerManualOverlay(id, true, string.Empty); + window.ViewModel!.TriggerManualOverlay(id, true, string.Empty); SetFontFamily(window, x.Locale); window.ViewModel.TriggerManualOverlay(id, false, string.Empty); }); @@ -336,8 +358,9 @@ private void SetFontFamily(Window mainWindow, string locale = Shared.Constants.E { language = langService.Get().FirstOrDefault(p => p.Abrv.Equals(locale)); } + var fontResolver = DIResolver.Get(); - var font = fontResolver.ResolveFontFamily(language.Font); + var font = fontResolver.ResolveFontFamily(language!.Font); mainWindow.FontFamily = font.GetFontFamily(); } diff --git a/src/IronyModManager/Controls/TextArea.cs b/src/IronyModManager/Controls/TextArea.cs new file mode 100644 index 000000000..1749a49e7 --- /dev/null +++ b/src/IronyModManager/Controls/TextArea.cs @@ -0,0 +1,61 @@ +// *********************************************************************** +// Assembly : IronyModManager +// Author : Mario +// Created : 02-24-2024 +// +// Last Modified By : Mario +// Last Modified On : 02-24-2024 +// *********************************************************************** +// +// Mario +// +// +// *********************************************************************** + +using System; +using System.Collections.Generic; +using System.Linq; +using Avalonia.Input; +using Avalonia.Styling; +using IronyModManager.Shared; + +namespace IronyModManager.Controls +{ + /// + /// The text area. + /// + /// + [ExcludeFromCoverage("Should be tested in functional testing.")] + public class TextArea : AvaloniaEdit.Editing.TextArea, IStyleable + { + /// + /// Occurs when [left pointer pressed]. + /// + public event EventHandler LeftPointerPressed; + + #region Properties + + /// + /// Gets a value representing the style key. + /// + /// The style key. + Type IStyleable.StyleKey => typeof(AvaloniaEdit.Editing.TextArea); + + #endregion Properties + + #region Methods + + /// + /// Handles the event. + /// + /// The instance containing the event data. + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + // You're asking why? Ask whoever designed avaloia which prevents it from routing events even if they are freaking handled + LeftPointerPressed?.Invoke(this, e); + base.OnPointerPressed(e); + } + + #endregion Methods + } +} diff --git a/src/IronyModManager/Controls/TextEditor.cs b/src/IronyModManager/Controls/TextEditor.cs index 60848130d..2934488a0 100644 --- a/src/IronyModManager/Controls/TextEditor.cs +++ b/src/IronyModManager/Controls/TextEditor.cs @@ -4,18 +4,25 @@ // Created : 04-15-2020 // // Last Modified By : Mario -// Last Modified On : 04-27-2020 +// Last Modified On : 03-07-2024 // *********************************************************************** // // Mario // // // *********************************************************************** -using System.Collections.Generic; + using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; using Avalonia.Styling; -using AvaloniaEdit.Editing; +using AvaloniaEdit; +using AvaloniaEdit.Document; +using AvaloniaEdit.Rendering; using IronyModManager.DI; using IronyModManager.Implementation.Actions; using IronyModManager.Shared; @@ -34,6 +41,11 @@ public class TextEditor : AvaloniaEdit.TextEditor, IStyleable { #region Fields + /// + /// A private const double named MinimumDistanceToViewBorder. + /// + private const double MinimumDistanceToViewBorder = 30; + /// /// The application action /// @@ -48,17 +60,32 @@ public class TextEditor : AvaloniaEdit.TextEditor, IStyleable /// public TextEditor() : base(new TextArea()) { - TextArea.TextCopied += (sender, args) => + TextArea.TextCopied += (_, args) => { - string text = string.Join(Environment.NewLine, args.Text.SplitOnNewLine()); + var text = string.Join(Environment.NewLine, args.Text.SplitOnNewLine()); CopyTextAsync(text).ConfigureAwait(true); }; } #endregion Constructors + #region Events + + /// + /// Occurs when [scroll initialized]. + /// + public event EventHandler ScrollInitialized; + + #endregion Events + #region Properties + /// + /// Gets or sets a value representing the scroll viewer. + /// + /// The scroll viewer. + public ScrollViewer ScrollViewer { get; private set; } + /// /// Gets the style key. /// @@ -69,6 +96,164 @@ public TextEditor() : base(new TextArea()) #region Methods + /// + /// Gets a bottom visible line. + /// + /// An int. + public int GetBottomVisibleLine() + { + var lastLine = TextArea.TextView.GetDocumentLineByVisualTop(ScrollViewer.Offset.Y + ScrollViewer.Viewport.Height).LineNumber; + return lastLine; + } + + /// + /// Gets a last column by line. + /// + /// The line. + /// An int. + public int GetLastColumnByLine(int line) + { + var docLine = Document.GetLineByNumber(line); + if (docLine != null && docLine.Length != 0) + { + return docLine.Length + 1; + } + + return 0; + } + + /// + /// Gets a middle visible line. + /// + /// An int. + public int GetMiddleVisibleLine() + { + var firstLine = GetTopVisibleLine(); + var lastLine = GetBottomVisibleLine(); + return firstLine + ((lastLine - firstLine) / 2); + } + + /// + /// Gets a top visible line. + /// + /// An int. + public int GetTopVisibleLine() + { + var firstLine = TextArea.TextView.GetDocumentLineByVisualTop(ScrollViewer.Offset.Y).LineNumber; + return firstLine; + } + + /// + /// Saves a set text. + /// + /// The value. + public void SafeSetText(string value) + { + var document = GetDocument(); + document.Text = value ?? string.Empty; + CaretOffset = 0; + } + + /// + /// Scroll to. + /// + /// The line. + /// The column. + public new void ScrollTo(int line, int column) + { + const double minimumScrollFraction = 0.3; + ScrollTo(line, column, VisualYPosition.LineMiddle, null != ScrollViewer ? ScrollViewer.Viewport.Height / 2 : 0.0, minimumScrollFraction); + } + + /// + /// Scroll to. + /// + /// The line. + /// The column. + /// The y position mode. + /// The referenced vertical view port offset. + /// The minimum scroll fraction. + public new void ScrollTo(int line, int column, VisualYPosition yPositionMode, double referencedVerticalViewPortOffset, double minimumScrollFraction) + { + // Backported from 0.11.x version + var textView = TextArea.TextView; + var document = textView.Document; + if (ScrollViewer != null && document != null) + { + if (line < 1) + { + line = 1; + } + + if (line > document.LineCount) + { + line = document.LineCount; + } + + ILogicalScrollable scrollInfo = textView; + if (!scrollInfo.CanHorizontallyScroll) + { + var vl = textView.GetOrConstructVisualLine(document.GetLineByNumber(line)); + var remainingHeight = referencedVerticalViewPortOffset; + + while (remainingHeight > 0) + { + var prevLine = vl.FirstDocumentLine.PreviousLine; + if (prevLine == null) + { + break; + } + + vl = textView.GetOrConstructVisualLine(prevLine); + remainingHeight -= vl.Height; + } + } + + var p = TextArea.TextView.GetVisualPosition( + new TextViewPosition(line, Math.Max(1, column)), + yPositionMode); + + var targetX = ScrollViewer.Offset.X; + var targetY = ScrollViewer.Offset.Y; + + var verticalPos = p.Y - referencedVerticalViewPortOffset; + if (Math.Abs(verticalPos - ScrollViewer.Offset.Y) > minimumScrollFraction * ScrollViewer.Viewport.Height) + { + targetY = Math.Max(0, verticalPos); + } + + if (column > 0) + { + if (p.X > ScrollViewer.Viewport.Width - (MinimumDistanceToViewBorder * 2)) + { + var horizontalPos = Math.Max(0, p.X - (ScrollViewer.Viewport.Width / 2)); + if (Math.Abs(horizontalPos - ScrollViewer.Offset.X) > minimumScrollFraction * ScrollViewer.Viewport.Width) + { + targetX = 0; + } + } + else + { + targetX = 0; + } + } + + if (!targetX.IsNearlyEqual(ScrollViewer.Offset.X) || !targetY.IsNearlyEqual(ScrollViewer.Offset.Y)) + { + ScrollViewer.Offset = new Vector(targetX, targetY); + } + } + } + + /// + /// Scrolls to a line. + /// + /// The line. + public new void ScrollToLine(int line) + { + ScrollTo(line, -1); + } + /// /// Copies the text asynchronous. /// @@ -76,11 +261,35 @@ public TextEditor() : base(new TextArea()) /// Task. protected virtual Task CopyTextAsync(string text) { - if (appAction == null) + appAction ??= DIResolver.Get(); + return appAction.CopyAsync(text); + } + + /// + /// Handles the event. + /// + /// The instance containing the event data. + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + + // Yes, only to expose scroll viewer + ScrollViewer = (ScrollViewer)e.NameScope.Find("PART_ScrollViewer"); + if (ScrollViewer != null) { - appAction = DIResolver.Get(); + ScrollInitialized?.Invoke(this, EventArgs.Empty); } - return appAction.CopyAsync(text); + } + + /// + /// Get document. + /// + /// A TextDocument. + /// No document + private TextDocument GetDocument() + { + var document = Document; + return document ?? throw new NullReferenceException("No document"); } #endregion Methods diff --git a/src/IronyModManager/Implementation/AvaloniaEdit/Constants.cs b/src/IronyModManager/Implementation/AvaloniaEdit/Constants.cs new file mode 100644 index 000000000..e53d4eb14 --- /dev/null +++ b/src/IronyModManager/Implementation/AvaloniaEdit/Constants.cs @@ -0,0 +1,116 @@ +// *********************************************************************** +// Assembly : IronyModManager +// Author : Mario +// Created : 02-19-2024 +// +// Last Modified By : Mario +// Last Modified On : 02-28-2024 +// *********************************************************************** +// +// Mario +// +// +// *********************************************************************** + +using System; +using System.Collections.Generic; +using System.Linq; +using Avalonia.Media; + +namespace IronyModManager.Implementation.AvaloniaEdit +{ + /// + /// Class Constants. + /// + public class Constants + { + #region Fields + + /// + /// A public static readonly SolidColorBrush named DarkDiffDeletedLine. + /// + public static readonly SolidColorBrush DarkDiffDeletedLine = SolidColorBrush.Parse("#923E3E"); + + /// + /// A public static readonly SolidColorBrush named DarkDiffDeletedPieces. + /// + public static readonly SolidColorBrush DarkDiffDeletedPieces = SolidColorBrush.Parse("#923E3E"); + + /// + /// A public static readonly SolidColorBrush named DarkDiffImaginaryLine. + /// + public static readonly SolidColorBrush DarkDiffImaginaryLine = SolidColorBrush.Parse("#5A5A5A"); + + /// + /// A public static readonly SolidColorBrush named DarkDiffInsertedLine. + /// + public static readonly SolidColorBrush DarkDiffInsertedLine = SolidColorBrush.Parse("#1B4F35"); + + /// + /// A public static readonly SolidColorBrush named DarkDiffInsertedPieces. + /// + public static readonly SolidColorBrush DarkDiffInsertedPieces = SolidColorBrush.Parse("#1B4F35"); + + /// + /// A public static readonly SolidColorBrush named DarkDiffModifiedLine. + /// + public static readonly SolidColorBrush DarkDiffModifiedLine = SolidColorBrush.Parse("#C49400"); + + /// + /// A public static readonly SolidColorBrush named DarkDiffModifiedPieces. + /// + public static readonly SolidColorBrush DarkDiffModifiedPieces = SolidColorBrush.Parse("#C49400"); + + /// + /// A public static readonly SolidColorBrush named DarkDiffUnchangedPieces. + /// + public static readonly SolidColorBrush DarkDiffUnchangedPieces = SolidColorBrush.Parse("#C49400"); + + /// + /// A public static readonly SolidColorBrush named LightDiffDeletedLine. + /// + public static readonly SolidColorBrush LightDiffDeletedLine = SolidColorBrush.Parse("#FF9999"); + + /// + /// A public static readonly SolidColorBrush named LightDiffDeletedPieces. + /// + public static readonly SolidColorBrush LightDiffDeletedPieces = SolidColorBrush.Parse("#FF9999"); + + /// + /// A public static readonly SolidColorBrush named LightDiffImaginaryLine. + /// + public static readonly SolidColorBrush LightDiffImaginaryLine = SolidColorBrush.Parse("#FF808080"); + + /// + /// A public static readonly SolidColorBrush named LightDiffInsertedLine. + /// + public static readonly SolidColorBrush LightDiffInsertedLine = SolidColorBrush.Parse("#66cc99"); + + /// + /// A public static readonly SolidColorBrush named LightDiffInsertedPieces. + /// + public static readonly SolidColorBrush LightDiffInsertedPieces = SolidColorBrush.Parse("#66cc99"); + + /// + /// A public static readonly SolidColorBrush named LightDiffModifiedLine. + /// + public static readonly SolidColorBrush LightDiffModifiedLine = SolidColorBrush.Parse("#ffe28b"); + + /// + /// A public static readonly SolidColorBrush named LightDiffModifiedPieces. + /// + public static readonly SolidColorBrush LightDiffModifiedPieces = SolidColorBrush.Parse("#ffe28b"); + + /// + /// A public static readonly SolidColorBrush named LightDiffUnchangedPieces. + /// + public static readonly SolidColorBrush LightDiffUnchangedPieces = SolidColorBrush.Parse("#ffe28b"); + + /// + /// A public static readonly Pen named TransparentPen. + /// + public static readonly Pen TransparentPen = new(new SolidColorBrush(Brushes.Transparent.Color)); + + #endregion Fields + } +} diff --git a/src/IronyModManager/Implementation/AvaloniaEdit/DiffBackgroundRenderer.cs b/src/IronyModManager/Implementation/AvaloniaEdit/DiffBackgroundRenderer.cs new file mode 100644 index 000000000..2106820a2 --- /dev/null +++ b/src/IronyModManager/Implementation/AvaloniaEdit/DiffBackgroundRenderer.cs @@ -0,0 +1,200 @@ +// *********************************************************************** +// Assembly : IronyModManager +// Author : Mario +// Created : 02-19-2024 +// +// Last Modified By : Mario +// Last Modified On : 02-28-2024 +// *********************************************************************** +// +// Mario +// +// +// *********************************************************************** + +using System; +using System.Collections.Generic; +using System.Linq; +using Avalonia; +using Avalonia.Media; +using AvaloniaEdit.Document; +using AvaloniaEdit.Rendering; +using IronyModManager.DI; +using IronyModManager.Platform.Themes; +using IronyModManager.Services.Common; +using IronyModManager.ViewModels.Controls; + +namespace IronyModManager.Implementation.AvaloniaEdit +{ + /// + /// The diff background renderer. + /// + /// + public class DiffBackgroundRenderer : IBackgroundRenderer + { + #region Fields + + /// + /// A private bool? named isLightTheme. + /// + private bool? isLightTheme; + + /// + /// A private IThemeManager named themeManager. + /// + private IThemeManager themeManager; + + /// + /// A private IThemeService named themeService. + /// + private IThemeService themeService; + + #endregion Fields + + #region Properties + + /// + /// Gets a value representing the layer. + /// + /// The layer. + public KnownLayer Layer => KnownLayer.Background; + + /// + /// Gets or sets a value representing the lines. + /// + /// The lines. + public IList Lines { get; set; } + + #endregion Properties + + #region Methods + + /// + /// Draws the specified text view. + /// + /// The text view. + /// The drawing context. + /// + public void Draw(TextView textView, DrawingContext drawingContext) + { + if (Lines == null || Lines.Count == 0) + { + return; + } + + foreach (var line in textView.VisualLines) + { + var num = line.FirstDocumentLine.LineNumber - 1; + if (num >= Lines.Count) + { + continue; + } + + var diff = Lines[num]; + + Brush brush = diff.Type switch + { + DiffPlex.DiffBuilder.Model.ChangeType.Deleted => IsLightTheme() ? Constants.LightDiffDeletedLine : Constants.DarkDiffDeletedLine, + DiffPlex.DiffBuilder.Model.ChangeType.Inserted => IsLightTheme() ? Constants.LightDiffInsertedLine : Constants.DarkDiffInsertedLine, + DiffPlex.DiffBuilder.Model.ChangeType.Imaginary => IsLightTheme() ? Constants.LightDiffImaginaryLine : Constants.DarkDiffImaginaryLine, + DiffPlex.DiffBuilder.Model.ChangeType.Modified => IsLightTheme() ? Constants.LightDiffModifiedLine : Constants.DarkDiffModifiedLine, + _ => default + }; + + if (brush != default(Brush)) + { + var rect = BackgroundGeometryBuilder.GetRectsFromVisualSegment(textView, line, 0, 1000); + foreach (var r in rect) + { + drawingContext.DrawRectangle(brush, Constants.TransparentPen, new Rect(0, r.Top, textView.Bounds.Width, r.Height)); + } + } + + var offset = 0; + var endOffset = 0; + foreach (var piece in diff.SubPieces) + { + var subPieceBrush = piece.Type switch + { + DiffPlex.DiffBuilder.Model.ChangeType.Deleted => IsLightTheme() ? Constants.LightDiffDeletedPieces : Constants.DarkDiffDeletedPieces, + DiffPlex.DiffBuilder.Model.ChangeType.Inserted => IsLightTheme() ? Constants.LightDiffInsertedPieces : Constants.DarkDiffInsertedPieces, + DiffPlex.DiffBuilder.Model.ChangeType.Modified => IsLightTheme() ? Constants.LightDiffModifiedPieces : Constants.DarkDiffModifiedPieces, + DiffPlex.DiffBuilder.Model.ChangeType.Unchanged => IsLightTheme() ? Constants.LightDiffUnchangedPieces : Constants.DarkDiffUnchangedPieces, + _ => default(Brush) + }; + var text = piece.Text ?? string.Empty; + endOffset += text.Length; + if (subPieceBrush != default(Brush)) + { + var builder = new BackgroundGeometryBuilder { AlignToWholePixels = true }; + var diffSegment = new DiffSegment(line.StartOffset + offset, text.Length, line.StartOffset + endOffset); + builder.AddSegment(textView, diffSegment); + + var geo = builder.CreateGeometry(); + if (geo != null) + { + drawingContext.DrawGeometry(subPieceBrush, null, geo); + } + } + + offset += text.Length; + } + } + } + + /// + /// Is light theme. + /// + /// A bool. + private bool IsLightTheme() + { + if (!isLightTheme.HasValue) + { + themeManager ??= DIResolver.Get(); + themeService ??= DIResolver.Get(); + isLightTheme = themeManager.IsLightTheme(themeService.GetSelected().Type); + } + + return isLightTheme.GetValueOrDefault(); + } + + #endregion Methods + + #region Classes + + /// + /// The diff segment. + /// + /// + /// The offset. + /// The length. + /// The end offset. + /// Initializes a new instance of the class. + private class DiffSegment(int offset, int length, int endOffset) : ISegment + { + #region Properties + + /// + /// Gets a value representing the end offset. + /// + /// The end offset. + public int EndOffset { get; } = endOffset; + + /// + /// Gets a value representing the length. + /// + /// The length. + public int Length { get; } = length; + + /// + /// Gets a value representing the offset. + /// + /// The offset. + public int Offset { get; } = offset; + + #endregion Properties + } + + #endregion Classes + } +} diff --git a/src/IronyModManager/Implementation/AvaloniaEdit/DiffMargin.cs b/src/IronyModManager/Implementation/AvaloniaEdit/DiffMargin.cs new file mode 100644 index 000000000..fd9f6eb81 --- /dev/null +++ b/src/IronyModManager/Implementation/AvaloniaEdit/DiffMargin.cs @@ -0,0 +1,410 @@ +// *********************************************************************** +// Assembly : IronyModManager +// Author : Mario +// Created : 02-19-2024 +// +// Last Modified By : Mario +// Last Modified On : 02-23-2024 +// *********************************************************************** +// +// Mario +// +// Portions based on LineNumberingMargin +// *********************************************************************** + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Input; +using Avalonia.Media; +using AvaloniaEdit.Document; +using AvaloniaEdit.Editing; +using AvaloniaEdit.Rendering; +using AvaloniaEdit.Utils; +using IronyModManager.DI; +using IronyModManager.Platform.Themes; +using IronyModManager.Services.Common; +using IronyModManager.ViewModels.Controls; + +namespace IronyModManager.Implementation.AvaloniaEdit +{ + /// + /// The diff margin. + /// + /// + public class DiffMargin : AbstractMargin + { + #region Fields + + /// + /// A private const double named LineMargin. + /// + private const double LineMargin = 4d; + + /// + /// The font family + /// + private FontFamily fontFamily; + + /// + /// The font size + /// + private double fontSize; + + /// + /// A private bool? named isLightTheme. + /// + private bool? isLightTheme; + + /// + /// The maximum line number length + /// + private int maxLineNumberLength = 1; + + /// + /// The selecting + /// + private bool selecting; + + /// + /// The selection start + /// + private AnchorSegment selectionStart; + + /// + /// A private IThemeManager named themeManager. + /// + private IThemeManager themeManager; + + /// + /// A private IThemeService named themeService. + /// + private IThemeService themeService; + + #endregion Fields + + #region Properties + + /// + /// Gets or sets a value representing the lines. + /// + /// The lines. + public IList Lines { get; set; } + + #endregion Properties + + #region Methods + + /// + /// Render. + /// + /// The context. + public override void Render(DrawingContext context) + { + base.Render(context); + if (Lines == null || Lines.Count == 0) + { + return; + } + + var typeFace = CreateTypeface(); + + var visualLines = TextView.VisualLinesValid ? TextView.VisualLines : Enumerable.Empty(); + foreach (var line in visualLines) + { + var rect = BackgroundGeometryBuilder.GetRectsFromVisualSegment(TextView, line, 0, 1000); + var ln = line.FirstDocumentLine.LineNumber - 1; + if (ln >= Lines.Count) + { + continue; + } + + var diff = Lines[ln]; + + Brush brush = diff.Type switch + { + DiffPlex.DiffBuilder.Model.ChangeType.Deleted => IsLightTheme() ? Constants.LightDiffDeletedLine : Constants.DarkDiffDeletedLine, + DiffPlex.DiffBuilder.Model.ChangeType.Inserted => IsLightTheme() ? Constants.LightDiffInsertedLine : Constants.DarkDiffInsertedLine, + DiffPlex.DiffBuilder.Model.ChangeType.Imaginary => IsLightTheme() ? Constants.LightDiffImaginaryLine : Constants.DarkDiffImaginaryLine, + DiffPlex.DiffBuilder.Model.ChangeType.Modified => IsLightTheme() ? Constants.LightDiffModifiedPieces : Constants.DarkDiffModifiedPieces, + _ => default + }; + + foreach (var r in rect) + { + context.DrawRectangle(brush, Constants.TransparentPen, new Rect(0, r.Top, Bounds.Width, r.Height)); + } + + var text = new FormattedText(diff.Index.ToString(), typeFace, TextView.GetValue(TextBlock.FontSizeProperty), TextAlignment.Left, TextWrapping.NoWrap, Size.Empty); + context.DrawText(TextView.GetValue(TextBlock.ForegroundProperty), new Point(LineMargin, rect.FirstOrDefault().Top), text); + } + } + + /// + /// Measure override. + /// + /// The available size. + /// A Size. + protected override Size MeasureOverride(Size availableSize) + { + if (Lines == null || Lines.Count == 0) + { + fontFamily = GetValue(TextBlock.FontFamilyProperty); + fontSize = GetValue(TextBlock.FontSizeProperty); + + var txt = TextFormatterFactory.CreateFormattedText(this, new string('9', 2), fontFamily, fontSize, GetValue(TemplatedControl.ForegroundProperty)); + return new Size(txt.Bounds.Width + (LineMargin * 2), 0); + } + + var text = Lines.LastOrDefault()!.Index.ToString(); + var typeFace = CreateTypeface(); + var lineText = new FormattedText(text, typeFace, TextView.GetValue(TextBlock.FontSizeProperty), TextAlignment.Left, TextWrapping.NoWrap, Size.Empty); + return new Size(lineText.Bounds.Width + (LineMargin * 2), 0); + } + + /// + /// Called when [document changed]. + /// + /// The old document. + /// The new document. + protected override void OnDocumentChanged(TextDocument oldDocument, TextDocument newDocument) + { + if (oldDocument != null) + { + TextDocumentWeakEventManager.LineCountChanged.RemoveHandler(oldDocument, OnDocumentLineCountChanged); + } + + base.OnDocumentChanged(oldDocument, newDocument); + if (newDocument != null) + { + TextDocumentWeakEventManager.LineCountChanged.AddHandler(newDocument, OnDocumentLineCountChanged); + } + + OnDocumentLineCountChanged(); + } + + /// + /// Called before the event occurs. + /// + /// The event args. + protected override void OnPointerMoved(PointerEventArgs e) + { + if (selecting && TextArea != null && TextView != null) + { + e.Handled = true; + var currentSeg = GetTextLineSegment(e); + if (currentSeg == SimpleSegment.Invalid) + { + return; + } + + ExtendSelection(currentSeg); + TextArea.Caret.BringCaretToView(5.0); + } + + base.OnPointerMoved(e); + } + + /// + /// Called before the event occurs. + /// + /// The event args. + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + base.OnPointerPressed(e); + + if (!e.Handled && TextView != null && TextArea != null) + { + e.Handled = true; + TextArea.Focus(); + + var currentSeg = GetTextLineSegment(e); + if (currentSeg == SimpleSegment.Invalid) + { + return; + } + + TextArea.Caret.Offset = currentSeg.Offset + currentSeg.Length; + e.Pointer.Capture(this); + if (Equals(e.Pointer.Captured, this)) + { + selecting = true; + selectionStart = new AnchorSegment(Document, currentSeg.Offset, currentSeg.Length); + if (e.KeyModifiers.HasFlag(KeyModifiers.Shift)) + { + if (TextArea.Selection is SimpleSelection simpleSelection) + { + selectionStart = new AnchorSegment(Document, simpleSelection.SurroundingSegment); + } + } + + TextArea.Selection = Selection.Create(TextArea, selectionStart); + if (e.KeyModifiers.HasFlag(KeyModifiers.Shift)) + { + ExtendSelection(currentSeg); + } + + TextArea.Caret.BringCaretToView(5.0); + } + } + } + + /// + /// Handles the event. + /// + /// The instance containing the event data. + protected override void OnPointerReleased(PointerReleasedEventArgs e) + { + if (selecting) + { + selecting = false; + selectionStart = null; + e.Pointer.Capture(null); + e.Handled = true; + } + + base.OnPointerReleased(e); + } + + /// + /// Called when [text view changed]. + /// + /// The old text view. + /// The new text view. + protected override void OnTextViewChanged(TextView oldTextView, TextView newTextView) + { + if (oldTextView != null) + { + oldTextView.VisualLinesChanged -= TextViewVisualLinesChanged; + } + + base.OnTextViewChanged(oldTextView, newTextView); + if (newTextView != null) + { + newTextView.VisualLinesChanged += TextViewVisualLinesChanged; + } + + InvalidateVisual(); + } + + /// + /// Create typeface. + /// + /// A Typeface. + private Typeface CreateTypeface() + { + return new Typeface(TextView.GetValue(TextBlock.FontFamilyProperty), + TextView.GetValue(TextBlock.FontStyleProperty), + TextView.GetValue(TextBlock.FontWeightProperty)); + } + + /// + /// Extends the selection. + /// + /// The current seg. + private void ExtendSelection(SimpleSegment currentSeg) + { + if (currentSeg.Offset < selectionStart.Offset) + { + TextArea.Caret.Offset = currentSeg.Offset; + TextArea.Selection = Selection.Create(TextArea, currentSeg.Offset, selectionStart.Offset + selectionStart.Length); + } + else + { + TextArea.Caret.Offset = currentSeg.Offset + currentSeg.Length; + TextArea.Selection = Selection.Create(TextArea, selectionStart.Offset, currentSeg.Offset + currentSeg.Length); + } + } + + /// + /// Gets the text line segment. + /// + /// The instance containing the event data. + /// SimpleSegment. + private SimpleSegment GetTextLineSegment(PointerEventArgs e) + { + var pos = e.GetPosition(TextView); + pos = new Point(0, pos.Y.CoerceValue(0, TextView.Bounds.Height) + TextView.VerticalOffset); + var vl = TextView.GetVisualLineFromVisualTop(pos.Y); + if (vl == null) + { + return SimpleSegment.Invalid; + } + + var tl = vl.GetTextLineByVisualYPosition(pos.Y); + var visualStartColumn = vl.GetTextLineVisualStartColumn(tl); + var visualEndColumn = visualStartColumn + tl.Length; + var relStart = vl.FirstDocumentLine.Offset; + var startOffset = vl.GetRelativeOffset(visualStartColumn) + relStart; + var endOffset = vl.GetRelativeOffset(visualEndColumn) + relStart; + if (endOffset == vl.LastDocumentLine.Offset + vl.LastDocumentLine.Length) + { + endOffset += vl.LastDocumentLine.DelimiterLength; + } + + return new SimpleSegment(startOffset, endOffset - startOffset); + } + + /// + /// Is light theme. + /// + /// A bool. + private bool IsLightTheme() + { + if (!isLightTheme.HasValue) + { + themeManager ??= DIResolver.Get(); + themeService ??= DIResolver.Get(); + isLightTheme = themeManager.IsLightTheme(themeService.GetSelected().Type); + } + + return isLightTheme.GetValueOrDefault(); + } + + /// + /// Handles the event. + /// + /// The sender. + /// The instance containing the event data. + private void OnDocumentLineCountChanged(object sender, EventArgs e) + { + OnDocumentLineCountChanged(); + } + + /// + /// Called when [document line count changed]. + /// + private void OnDocumentLineCountChanged() + { + var documentLineCount = Document?.LineCount ?? 1; + var newLength = documentLineCount.ToString(CultureInfo.CurrentCulture).Length; + + if (newLength < 2) + { + newLength = 2; + } + + if (newLength != maxLineNumberLength) + { + maxLineNumberLength = newLength; + InvalidateMeasure(); + } + } + + /// + /// Texts the view visual lines changed. + /// + /// The sender. + /// The instance containing the event data. + private void TextViewVisualLinesChanged(object sender, EventArgs e) + { + InvalidateMeasure(); + } + + #endregion Methods + } +} diff --git a/src/IronyModManager/Implementation/AvaloniaEdit/ResourceLoader.cs b/src/IronyModManager/Implementation/AvaloniaEdit/ResourceLoader.cs index 416620c2e..bd2527595 100644 --- a/src/IronyModManager/Implementation/AvaloniaEdit/ResourceLoader.cs +++ b/src/IronyModManager/Implementation/AvaloniaEdit/ResourceLoader.cs @@ -4,13 +4,14 @@ // Created : 06-14-2021 // // Last Modified By : Mario -// Last Modified On : 06-14-2021 +// Last Modified On : 02-19-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; using System.IO; @@ -30,7 +31,12 @@ namespace IronyModManager.Implementation.AvaloniaEdit /// Implements the /// /// - public class ResourceLoader : IResourceLoader + /// + /// Initializes a new instance of the class. + /// + /// The theme service. + /// The theme manager. + public class ResourceLoader(IThemeService themeService, IThemeManager themeManager) : IResourceLoader { #region Fields @@ -47,30 +53,15 @@ public class ResourceLoader : IResourceLoader /// /// The theme manager /// - private readonly IThemeManager themeManager; + private readonly IThemeManager themeManager = themeManager; /// /// The theme service /// - private readonly IThemeService themeService; + private readonly IThemeService themeService = themeService; #endregion Fields - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The theme service. - /// The theme manager. - public ResourceLoader(IThemeService themeService, IThemeManager themeManager) - { - this.themeManager = themeManager; - this.themeService = themeService; - } - - #endregion Constructors - #region Methods /// @@ -82,9 +73,10 @@ public IHighlightingDefinition GetPDXScriptDefinition() { if (pdxScriptHighlightingDefinition == null) { - var resourcePath = themeManager.IsLightTheme(themeService.GetSelected().Type) ? Constants.Resources.PDXScriptLight : Constants.Resources.PDXScriptDark; + var resourcePath = themeManager.IsLightTheme(themeService.GetSelected().Type) ? IronyModManager.Constants.Resources.PDXScriptLight : IronyModManager.Constants.Resources.PDXScriptDark; pdxScriptHighlightingDefinition = GetHighlightingDefinition(resourcePath); } + return pdxScriptHighlightingDefinition; } @@ -96,9 +88,10 @@ public IHighlightingDefinition GetYAMLDefinition() { if (yamlHighlightingDefinition == null) { - var resourcePath = themeManager.IsLightTheme(themeService.GetSelected().Type) ? Constants.Resources.YAMLLight : Constants.Resources.YAMLDark; + var resourcePath = themeManager.IsLightTheme(themeService.GetSelected().Type) ? IronyModManager.Constants.Resources.YAMLLight : IronyModManager.Constants.Resources.YAMLDark; yamlHighlightingDefinition = GetHighlightingDefinition(resourcePath); } + return yamlHighlightingDefinition; } diff --git a/src/IronyModManager/Implementation/AvaloniaEdit/Resources/Light/PDXScript.xshd b/src/IronyModManager/Implementation/AvaloniaEdit/Resources/Light/PDXScript.xshd index bb689a993..93d8ef4c8 100644 --- a/src/IronyModManager/Implementation/AvaloniaEdit/Resources/Light/PDXScript.xshd +++ b/src/IronyModManager/Implementation/AvaloniaEdit/Resources/Light/PDXScript.xshd @@ -2,8 +2,8 @@ - - + + diff --git a/src/IronyModManager/Implementation/Config/PlatformConfiguration.cs b/src/IronyModManager/Implementation/Config/PlatformConfiguration.cs index ec34fcaed..f48df6a6c 100644 --- a/src/IronyModManager/Implementation/Config/PlatformConfiguration.cs +++ b/src/IronyModManager/Implementation/Config/PlatformConfiguration.cs @@ -5,7 +5,7 @@ // Created : 04-16-2021 // // Last Modified By : Mario -// Last Modified On : 11-26-2023 +// Last Modified On : 02-11-2024 // *********************************************************************** // // Mario @@ -13,13 +13,11 @@ // // *********************************************************************** using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using Microsoft.Extensions.Configuration; using IronyModManager.Platform; using IronyModManager.Platform.Configuration; using IronyModManager.Shared.Configuration; +using Microsoft.Extensions.Configuration; namespace IronyModManager.Implementation.Config { @@ -136,8 +134,10 @@ private PlatformConfigurationOptions GetPlatformOptions() platformConfiguration.Tooltips.Disable = configuration.GetSection("Tooltips").GetSection("Disable").Get(); platformConfiguration.Fonts.UseInbuiltFontsOnly = configuration.GetSection("Fonts").GetSection("UseInbuiltFontsOnly").Get(); platformConfiguration.Updates.Disable = configuration.GetSection("Updates").GetSection("Disable").Get(); + platformConfiguration.Updates.DisableInstallOnly = configuration.GetSection("Updates").GetSection("DisableInstallOnly").Get(); platformConfiguration.TitleBar.Native = configuration.GetSection("TitleBar").GetSection("Native").Get(); platformConfiguration.ConflictSolver.UseSubMenus = configuration.GetSection("ConflictSolver").GetSection("UseSubMenus").Get(); + platformConfiguration.App.SingleInstance = configuration.GetSection("App").GetSection("SingleInstance").Get(); } return platformConfiguration; } diff --git a/src/IronyModManager/Implementation/SingleInstance/Args.cs b/src/IronyModManager/Implementation/SingleInstance/Args.cs new file mode 100644 index 000000000..2043cbc40 --- /dev/null +++ b/src/IronyModManager/Implementation/SingleInstance/Args.cs @@ -0,0 +1,41 @@ + +// *********************************************************************** +// Assembly : IronyModManager +// Author : Mario +// Created : 02-10-2024 +// +// Last Modified By : Mario +// Last Modified On : 02-10-2024 +// *********************************************************************** +// +// Mario +// +// +// *********************************************************************** +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IronyModManager.Implementation.SingleInstance +{ + + /// + /// Class Args. + /// Implements the + /// + /// + public class Args + { + #region Properties + + /// + /// Gets or sets the command line arguments. + /// + /// The command line arguments. + public string[] CommandLineArgs { get; set; } + + #endregion Properties + } +} diff --git a/src/IronyModManager/Implementation/SingleInstance/SingleInstance.cs b/src/IronyModManager/Implementation/SingleInstance/SingleInstance.cs new file mode 100644 index 000000000..ad711a212 --- /dev/null +++ b/src/IronyModManager/Implementation/SingleInstance/SingleInstance.cs @@ -0,0 +1,200 @@ +// *********************************************************************** +// Assembly : IronyModManager +// Author : Mario +// Created : 02-10-2024 +// +// Last Modified By : Mario +// Last Modified On : 03-11-2024 +// *********************************************************************** +// +// Mario +// +// +// *********************************************************************** + +using System; +using System.Collections.Generic; +using System.IO.Pipes; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using Newtonsoft.Json; + +namespace IronyModManager.Implementation.SingleInstance +{ + /// + /// Class SingleInstance. + /// + internal static class SingleInstance + { + #region Fields + + /// + /// The delay + /// + private const int Delay = 100; + + /// + /// The object lock + /// + private static readonly object objLock = new(); + + /// + /// The mutex + /// + private static Mutex mutex; + + /// + /// The mutex name + /// + private static string mutexName; + + /// + /// The thread + /// + private static Thread thread; + + #endregion Fields + + #region Delegates + + /// + /// Delegate ArgsDelegate + /// + /// The arguments. + public delegate void ArgsDelegate(Args args); + + #endregion Delegates + + #region Events + + /// + /// Occurs when [instance launched]. + /// + public static event ArgsDelegate InstanceLaunched; + + #endregion Events + + #region Methods + + /// + /// Initializes this instance. + /// + /// true if XXXX, false otherwise. + public static bool Initialize() + { + lock (objLock) + { + var initial = false; + try + { + mutex = new Mutex(true, $"Global\\{GetMutexName()}", out initial); + if (initial) + { + thread = new Thread(Monitor) { Name = typeof(SingleInstance).FullName, IsBackground = true }; + thread.Start(); + } + else + { + var data = new Args { CommandLineArgs = Environment.GetCommandLineArgs() }; + var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data)); + using var pipe = new NamedPipeClientStream(".", GetMutexName(), PipeDirection.Out, PipeOptions.CurrentUserOnly | PipeOptions.WriteThrough); + pipe.Connect(); + pipe.Write(bytes, 0, bytes.Length); + } + } + catch + { + } + + if (!initial) + { + Environment.Exit(0); + return false; + } + + return true; + } + } + + /// + /// Monitors this instance. + /// + public static void Monitor() + { + using var pipe = new NamedPipeServerStream(GetMutexName(), PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.CurrentUserOnly | PipeOptions.WriteThrough); + while (mutex != null) + { + try + { + if (!pipe.IsConnected) + { + pipe.WaitForConnection(); + } + + var buffer = new byte[1024]; + var sb = new StringBuilder(); + while (true) + { + var readBytes = pipe.Read(buffer, 0, buffer.Length); + if (readBytes == 0) + { + break; + } + + sb.Append(Encoding.UTF8.GetString(buffer)); + } + + var args = JsonConvert.DeserializeObject(sb.ToString()); + pipe.Disconnect(); + if (args != null) + { + InstanceLaunched?.Invoke(args); + } + } + catch + { + Thread.Sleep(Delay); + } + } + } + + /// + /// Gets the name of the mutex. + /// + /// System.String. + private static string GetMutexName() + { + lock (objLock) + { + if (string.IsNullOrWhiteSpace(mutexName)) + { + var name = nameof(IronyModManager); + var sb = new StringBuilder(); + sb.Append(name, 0, Math.Min(name.Length, 31)); + sb.Append('.'); + var hash = new StringBuilder(); + hash.AppendLine(Environment.MachineName); + hash.AppendLine(Environment.UserName); + var data = SHA256.HashData(Encoding.UTF8.GetBytes(hash.ToString())); + foreach (var item in data) + { + if (sb.Length >= 64) + { + break; + } + + sb.Append($"{item:X2}"); + } + + mutexName = sb.ToString(); + } + + return mutexName; + } + } + + #endregion Methods + } +} diff --git a/src/IronyModManager/IronyModManager.csproj b/src/IronyModManager/IronyModManager.csproj index 3d320e06a..c9b29ea0a 100644 --- a/src/IronyModManager/IronyModManager.csproj +++ b/src/IronyModManager/IronyModManager.csproj @@ -1,7 +1,7 @@  WinExe - net7.0 + net8.0 Assets\logo.ico IronyModManager.Program Mario @@ -129,12 +129,12 @@ - + - + - + @@ -147,15 +147,15 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + - + - - - + + + diff --git a/src/IronyModManager/Localization/de.json b/src/IronyModManager/Localization/de.json index 74a329359..5a1bb566f 100644 --- a/src/IronyModManager/Localization/de.json +++ b/src/IronyModManager/Localization/de.json @@ -14,6 +14,10 @@ "DLC": "DLC" } }, + "JokeError": { + "Title": "Unbehandelte Ei-Container-Speicherverletzung", + "Message": "Drücken Sie OK und tun Sie so, als wäre nichts passiert :)" + }, "FatalError": { "Message": "Ein nicht behandelter Fehler ist aufgetreten. Anwendung wird automatisch geschlossen.", "MessageWithLastError": "Ein nicht behandelter Fehler ist aufgetreten. Bitte verwenden Sie diese Informationen, um einen Fehlerbericht einzulegen:{NewLine}{NewLine}{Message}", @@ -95,6 +99,20 @@ "Victoria3": "Victoria 3", "STInfinite": "Star Trek: Infinite" }, + "GameLanguages": { + "l_english": "Englisch", + "l_braz_por": "Portugiesisch", + "l_french": "Französisch", + "l_german": "Deutsch", + "l_polish": "Polnisch", + "l_russian": "Russisch", + "l_simp_chinese": "Einfaches Chinesisch", + "l_spanish": "Spanisch", + "l_chinese": "Chinesisch", + "l_traditional_chinese": "Traditionelles Chinesisch", + "l_japanese": "Japanisch", + "l_korean": "Koreanisch" + }, "Installed_Mods": { "Name": "Installierte Mods", "Filter": "Mods Filtern", @@ -328,7 +346,8 @@ "Undo": "Rückgängig Machen", "Redo": "Wiederholen", "Editor": "Externe Zusammenführung", - "ReadonlyEditor": "Externer Vergleich" + "ReadonlyEditor": "Externer Vergleich", + "ToggleCompare": "Umschalten des Diff-Vergleichsmodus" }, "EditorContextMenu": { "Copy": "Kopieren", @@ -458,6 +477,10 @@ "AutoConfigure": "Alle konfigurieren...", "CustomModPath": "Benutzerdefiniertes Mod-Verzeichnis" }, + "ConflictSolver": { + "Title": "Konfliktlöser", + "AllowedLanguages": "Erlaubte Sprachen" + }, "Updates": { "Title": "Aktualisierungsoptionen", "UpdateInfoTitle": "Versionsinformationen aktualisieren", diff --git a/src/IronyModManager/Localization/en.json b/src/IronyModManager/Localization/en.json index 173e51043..328a0eed9 100644 --- a/src/IronyModManager/Localization/en.json +++ b/src/IronyModManager/Localization/en.json @@ -14,6 +14,10 @@ "DLC": "DLC" } }, + "JokeError": { + "Title": "Unhandled egg-container memory violation", + "Message": "Press OK to pretend like nothing happened :)" + }, "FatalError": { "Message": "Unhandled error occurred. App will close automatically.", "MessageWithLastError": "Unhandled error occurred. Please use this information to file a bug report:{NewLine}{NewLine}{Message}", @@ -95,6 +99,20 @@ "Victoria3": "Victoria 3", "STInfinite": "Star Trek: Infinite" }, + "GameLanguages": { + "l_english": "English", + "l_braz_por": "Portugese", + "l_french": "French", + "l_german": "German", + "l_polish": "Polish", + "l_russian": "Russian", + "l_simp_chinese": "Simp Chinese", + "l_spanish": "Spanish", + "l_chinese": "Chinese", + "l_traditional_chinese": "Traditional Chinese", + "l_japanese": "Japanese", + "l_korean": "Korean" + }, "Installed_Mods": { "Name": "Installed Mods", "Filter": "Filter Mods", @@ -328,7 +346,8 @@ "Undo": "Undo", "Redo": "Redo", "Editor": "External Merge", - "ReadonlyEditor": "External Compare" + "ReadonlyEditor": "External Compare", + "ToggleCompare": "Toggle Diff Compare Mode" }, "EditorContextMenu": { "Copy": "Copy", @@ -458,6 +477,10 @@ "AutoConfigure": "Configure All...", "CustomModPath": "Custom Mod Directory" }, + "ConflictSolver": { + "Title": "Conflict Solver", + "AllowedLanguages": "Allowed Languages" + }, "Updates": { "Title": "Update Options", "UpdateInfoTitle": "Update Release Information", diff --git a/src/IronyModManager/Localization/es.json b/src/IronyModManager/Localization/es.json index a4e95dcc9..39d883246 100644 --- a/src/IronyModManager/Localization/es.json +++ b/src/IronyModManager/Localization/es.json @@ -14,6 +14,10 @@ "DLC": "DLC" } }, + "JokeError": { + "Title": "Violación de memoria del contenedor de huevos no controlada", + "Message": "Pulsa OK para fingir que no ha pasado nada :)" + }, "FatalError": { "Message": "Ocurrió un error no manejado. La aplicación se cerrará automáticamente.", "MessageWithLastError": "Ocurrió un error no manejado. Por favor, utilice esta información para presentar un informe de errores:{NewLine}{NewLine}{Message}", @@ -95,6 +99,20 @@ "Victoria3": "Victoria 3", "STInfinite": "Star Trek: Infinite" }, + "GameLanguages": { + "l_english": "English", + "l_braz_por": "Portugués", + "l_french": "Francés", + "l_german": "Alemán", + "l_polish": "Polaco", + "l_russian": "Ruso", + "l_simp_chinese": "Simp Chinese", + "l_spanish": "Español", + "l_chinese": "Chino", + "l_traditional_chinese": "Chino tradicional", + "l_japanese": "Japonés", + "l_korean": "Coreano" + }, "Installed_Mods": { "Name": "Mods Instalados", "Filter": "Filtro de Mods", @@ -328,7 +346,8 @@ "Undo": "Deshacer", "Redo": "Rehacer", "Editor": "Fusión Externa", - "ReadonlyEditor": "Comparación externa" + "ReadonlyEditor": "Comparación externa", + "ToggleCompare": "Conmutar el modo de comparación de diferencias" }, "EditorContextMenu": { "Copy": "Copiar", @@ -458,6 +477,10 @@ "AutoConfigure": "Configurar todo...", "CustomModPath": "Directorio de mod personalizado" }, + "ConflictSolver": { + "Title": "Solucionador de conflictos", + "AllowedLanguages": "Lenguas permitidas" + }, "Updates": { "Title": "Opciones de Actualización", "UpdateInfoTitle": "Actualizar Información de la versión", diff --git a/src/IronyModManager/Localization/fr.json b/src/IronyModManager/Localization/fr.json index faed67967..effcd6b88 100644 --- a/src/IronyModManager/Localization/fr.json +++ b/src/IronyModManager/Localization/fr.json @@ -14,6 +14,10 @@ "DLC": "DLC" } }, + "JokeError": { + "Title": "Violation de la mémoire d'un conteneur d'œufs non gérée", + "Message": "Appuyez sur OK pour faire comme si rien ne s'était passé :)" + }, "FatalError": { "Message": "Une erreur non gérée s'est produite. L'application se fermera automatiquement.", "MessageWithLastError": "Une erreur non gérée s'est produite. Veuillez utiliser ces informations pour déposer un rapport de bogue:{NewLine}{NewLine}{Message}", @@ -95,6 +99,20 @@ "Victoria3": "Victoria 3", "STInfinite": "Star Trek: Infinite" }, + "GameLanguages": { + "l_english": "English", + "l_braz_por": "Portugais", + "l_french": "Français", + "l_german": "Allemand", + "l_polish": "Polonais", + "l_russian": "Russe", + "l_simp_chinese": "Chinois simplifié", + "l_spanish": "Espagnol", + "l_chinese": "Chinois", + "l_traditional_chinese": "Chinois traditionnel", + "l_japanese": "japonais", + "l_korean": "Koréen" + }, "Installed_Mods": { "Name": "Mods installés", "Filter": "Filtrer les mods", @@ -328,7 +346,8 @@ "Undo": "annuler", "Redo": "Refaire", "Editor": "Fusion externe", - "ReadonlyEditor": "Comparaison externe" + "ReadonlyEditor": "Comparaison externe", + "ToggleCompare": "Basculer le mode de comparaison des différences" }, "EditorContextMenu": { "Copy": "Copier", @@ -458,6 +477,10 @@ "AutoConfigure": "Tout configurer...", "CustomModPath": "Répertoire de mod personnalisé" }, + "ConflictSolver": { + "Title": "Résoudre les conflits", + "AllowedLanguages": "Langues autorisées" + }, "Updates": { "Title": "Options de mise à jour", "UpdateInfoTitle": "Mettre à jour les informations de version", diff --git a/src/IronyModManager/Localization/hr.json b/src/IronyModManager/Localization/hr.json index b55e6a868..1bee4e930 100644 --- a/src/IronyModManager/Localization/hr.json +++ b/src/IronyModManager/Localization/hr.json @@ -14,6 +14,10 @@ "DLC": "DLC" } }, + "JokeError": { + "Title": "Neobrađeno kršenje memorije spremnika jaja", + "Message": "Pritisnite OK da se pretvarate da se ništa nije dogodilo :)" + }, "FatalError": { "Message": "Došlo je do greške. Aplikacija će se automatski zatvoriti.", "MessageWithLastError": "Došlo je do greške. Koristite ovu informaciju da biste podnijeli izvješće o greški:{NewLine}{NewLine}{Message}", @@ -95,6 +99,20 @@ "Victoria3": "Victoria 3", "STInfinite": "Star Trek: Infinite" }, + "GameLanguages": { + "l_english": "Engleski", + "l_braz_por": "Portugalski", + "l_french": "Francuski", + "l_german": "Njemački", + "l_polish": "Poljski", + "l_russian": "Ruski", + "l_simp_chinese": "Jednostavan kineski", + "l_spanish": "Španjolski", + "l_chinese": "Kineski", + "l_traditional_chinese": "Tradicionalni kineski", + "l_japanese": "Japanski", + "l_korean": "Korejski" + }, "Installed_Mods": { "Name": "Instalirani modovi", "Filter": "Filtriraj modove", @@ -328,7 +346,8 @@ "Undo": "Poništi", "Redo": "Ponovi", "Editor": "Vanjsko spajanje", - "ReadonlyEditor": "Vanjska usporedba" + "ReadonlyEditor": "Vanjska usporedba", + "ToggleCompare": "Promjeni način usporedbe razlika" }, "EditorContextMenu": { "Copy": "Kopiraj", @@ -458,6 +477,10 @@ "AutoConfigure": "Konfiguriraj sve...", "CustomModPath": "Prilagođeni direktorij modova" }, + "ConflictSolver": { + "Title": "Rješavanje sukoba", + "AllowedLanguages": "Dopušteni jezici" + }, "Updates": { "Title": "Mogućnosti ažuriranja", "UpdateInfoTitle": "Informacije o novoj verziji", diff --git a/src/IronyModManager/Localization/ru.json b/src/IronyModManager/Localization/ru.json index 57afbd518..afe8de099 100644 --- a/src/IronyModManager/Localization/ru.json +++ b/src/IronyModManager/Localization/ru.json @@ -14,6 +14,10 @@ "DLC": "DLC" } }, + "JokeError": { + "Title": "Необработанное нарушение памяти контейнера яйца", + "Message": "Нажмите OK, чтобы сделать вид, что ничего не произошло :)" + }, "FatalError": { "Message": "Возникла неизвестная ошибка, приложение будет закрыто.", "MessageWithLastError": "Возникла неизвестная ошибка, приложение будет закрыто. Пожалуйста, используйте эту информацию, чтобы подать сообщение об ошибке:{NewLine}{NewLine}{Message}", @@ -95,6 +99,20 @@ "Victoria3": "Victoria 3", "STInfinite": "Star Trek: Infinite" }, + "GameLanguages": { + "l_english": "Английский", + "l_braz_por": "Португальский", + "l_french": "Французский", + "l_german": "Немецкий", + "l_polish": "Польский", + "l_russian": "Русский", + "l_simp_chinese": "Упрощенный китайский", + "l_spanish": "Испанский", + "l_chinese": "Китайский", + "l_traditional_chinese": "Традиционный китайский", + "l_japanese": "Японский", + "l_korean": "Корейский" + }, "Installed_Mods": { "Name": "Установленные моды", "Filter": "Фильтр", @@ -328,7 +346,8 @@ "Undo": "Отменить", "Redo": "Вернуть", "Editor": "Внешнее объединение", - "ReadonlyEditor": "Внешнее сравнение" + "ReadonlyEditor": "Внешнее сравнение", + "ToggleCompare": "Переключение режима сравнения диффов" }, "EditorContextMenu": { "Copy": "Копировать", @@ -458,6 +477,10 @@ "AutoConfigure": "Настроить все...", "CustomModPath": "Своя папка с модами" }, + "ConflictSolver": { + "Title": "Разрешитель конфликтов", + "AllowedLanguages": "Разрешенные языки" + }, "Updates": { "Title": "Обновления", "UpdateInfoTitle": "Инфо о релизе", diff --git a/src/IronyModManager/Localization/zh.json b/src/IronyModManager/Localization/zh.json index 9eece08d2..cd92222c8 100644 --- a/src/IronyModManager/Localization/zh.json +++ b/src/IronyModManager/Localization/zh.json @@ -14,6 +14,10 @@ "DLC": "DLC" } }, + "JokeError": { + "Title": "未处理的彩蛋容器内存违规", + "Message": "按 \"确定\",假装什么也没发生:)" + }, "FatalError": { "Message": "发生了无法处理的异常,程序即将关闭", "MessageWithLastError": "发生了无法处理的异常,请以此信息提交错误报告:{NewLine}{NewLine}{Message}", @@ -95,6 +99,20 @@ "Victoria3": "维多利亚 Ⅲ", "STInfinite": "Star Trek: Infinite" }, + "GameLanguages": { + "l_english": "英语", + "l_braz_por": "葡萄牙语", + "l_french": "法语", + "l_german": "德语", + "l_polish": "波兰语", + "l_russian": "俄语", + "l_simp_chinese": "简体中文", + "l_spanish": "西班牙语", + "l_chinese": "中文", + "l_traditional_chinese": "繁体中文", + "l_japanese": "日语", + "l_korean": "韩语" + }, "Installed_Mods": { "Name": "已安装模组", "Filter": "过滤模组", @@ -328,7 +346,8 @@ "Undo": "撤销", "Redo": "重做", "Editor": "外部合并", - "ReadonlyEditor": "外部比较" + "ReadonlyEditor": "外部比较", + "ToggleCompare": "切换差值比较模式" }, "EditorContextMenu": { "Copy": "复制", @@ -458,6 +477,10 @@ "AutoConfigure": "设置全部……", "CustomModPath": "自定义模组目录" }, + "ConflictSolver": { + "Title": "冲突处理器", + "AllowedLanguages": "允许使用的语言" + }, "Updates": { "Title": "更新选项", "UpdateInfoTitle": "更新发布信息", diff --git a/src/IronyModManager/PrankBootstrap.cs b/src/IronyModManager/PrankBootstrap.cs new file mode 100644 index 000000000..7229c127c --- /dev/null +++ b/src/IronyModManager/PrankBootstrap.cs @@ -0,0 +1,80 @@ +// *********************************************************************** +// Assembly : IronyModManager +// Author : Mario +// Created : 02-17-2024 +// +// Last Modified By : Mario +// Last Modified On : 02-18-2024 +// *********************************************************************** +// +// Mario +// +// +// *********************************************************************** + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Avalonia.Threading; +using IronyModManager.Common; +using IronyModManager.DI; +using IronyModManager.Implementation.Actions; +using IronyModManager.Localization; +using IronyModManager.Services.Common; +using IronyModManager.Shared; + +namespace IronyModManager +{ + /// + /// Class PrankBootstrap. + /// Implements the + /// + /// + public class PrankBootstrap : PostStartup + { + #region Methods + + /// + /// On post startup callback. + /// + public override void OnPostStartup() + { + Task.Run(InitPrankAsync); + } + + /// + /// Initialize the prank async. + /// + /// A Task. + private static async Task InitPrankAsync() + { + var prankDate = new DateTime(DateTime.Today.Year, 4, 1); + var appState = DIResolver.Get(); + var state = appState.Get(); + if (DateTime.Today.IsDateSame(prankDate) && state.LastPrankCheck.GetValueOrDefault().Year != DateTime.Today.Year) + { + await Task.Delay(10000); + var main = Helpers.GetMainWindow(); + while (main == null) + { + main = Helpers.GetMainWindow(); + await Task.Delay(250); + } + + var notificationAction = DIResolver.Get(); + var locManager = DIResolver.Get(); + var title = locManager.GetResource(LocalizationResources.JokeError.Title); + var message = locManager.GetResource(LocalizationResources.JokeError.Message); + await Dispatcher.UIThread.SafeInvokeAsync(async () => + { + await notificationAction.ShowPromptAsync(title, title, message, NotificationType.Error, PromptType.OK); + }); + state.LastPrankCheck = DateTime.Now; + appState.Save(state); + } + } + + #endregion Methods + } +} diff --git a/src/IronyModManager/Program.cs b/src/IronyModManager/Program.cs index bdcd4cb4e..24d20e495 100644 --- a/src/IronyModManager/Program.cs +++ b/src/IronyModManager/Program.cs @@ -1,17 +1,17 @@ - -// *********************************************************************** +// *********************************************************************** // Assembly : IronyModManager // Author : Mario // Created : 01-10-2020 // // Last Modified By : Mario -// Last Modified On : 06-25-2023 +// Last Modified On : 03-11-2024 // *********************************************************************** // // Copyright (c) Mario. All rights reserved. // // // *********************************************************************** + using System; using System.Collections.Generic; using System.Linq; @@ -19,21 +19,24 @@ using System.Threading.Tasks; using Avalonia; using Avalonia.Controls; +using Avalonia.Threading; using CommandLine; +using IronyModManager.Common; using IronyModManager.Controls.Dialogs; using IronyModManager.DI; using IronyModManager.Implementation.Actions; using IronyModManager.Implementation.AvaloniaEdit; +using IronyModManager.Implementation.SingleInstance; using IronyModManager.Localization; using IronyModManager.Platform; using IronyModManager.Platform.Configuration; using IronyModManager.Shared; +using IronyModManager.Views; using NLog; using ILogger = IronyModManager.Shared.ILogger; namespace IronyModManager { - /// /// Class Program. /// @@ -45,7 +48,7 @@ internal class Program /// /// The external notification shown /// - private static bool ExternalNotificationShown = false; + private static bool ExternalNotificationShown; #endregion Fields @@ -57,9 +60,10 @@ internal class Program /// /// AppBuilder. public static AppBuilder BuildAvaloniaApp() - => AppBuilder.Configure() + { + return AppBuilder.Configure() .UseIronyPlatformDetect() - .UseIronyManagedDialogs().AfterSetup((s) => + .UseIronyManagedDialogs().AfterSetup(_ => { if (!Design.IsDesignMode) { @@ -72,6 +76,7 @@ public static AppBuilder BuildAvaloniaApp() DIContainer.Finish(true); } }); + } // Initialization code. Don't use any Avalonia, third-party APIs or any // SynchronizationContext-reliant code before AppMain is called: things aren't initialized @@ -93,6 +98,17 @@ public static void Main(string[] args) try { ParseArguments(args); + var canInitialize = true; + if (!StaticResources.CommandLineOptions.ShowFatalErrorNotification) + { + canInitialize = InitSingleInstance(); + } + + if (!canInitialize) + { + return; + } + var app = BuildAvaloniaApp(); InitAvaloniaOptions(app); Bootstrap.PostStartup(); @@ -141,27 +157,33 @@ void configureLinux() { waylandOpts.AppId = configuration.WaylandAppId; } + if (configuration.UseGPU.HasValue) { x11Opts.UseGpu = configuration.UseGPU.GetValueOrDefault(); waylandOpts.UseGpu = configuration.UseGPU.GetValueOrDefault(); } + if (configuration.UseEGL.HasValue) { x11Opts.UseEGL = configuration.UseEGL.GetValueOrDefault(); } + if (configuration.UseDBusMenu.HasValue) { x11Opts.UseDBusMenu = configuration.UseDBusMenu.GetValueOrDefault(); } + if (configuration.UseDeferredRendering.HasValue) { x11Opts.UseDeferredRendering = configuration.UseDeferredRendering.GetValueOrDefault(); waylandOpts.UseDeferredRendering = configuration.UseDeferredRendering.GetValueOrDefault(); } + app.With(x11Opts); app.With(waylandOpts); } + configureLinux(); } @@ -180,11 +202,7 @@ private static void InitDefaultCulture() private static void InitDI() { Bootstrap.Setup( - new DIOptions() - { - Container = new SimpleInjector.Container(), - PluginPathAndName = Shared.Constants.PluginsPathAndName - }); + new DIOptions { Container = new SimpleInjector.Container(), PluginPathAndName = Shared.Constants.PluginsPathAndName }); } /// @@ -195,6 +213,46 @@ private static void InitLogging() LogManager.Setup().SetupExtensions(s => s.RegisterTarget("IronyFile", typeof(Log.IronyFileTarget))); } + /// + /// Initializes the single instance. + /// + /// true if XXXX, false otherwise. + private static bool InitSingleInstance() + { + var configuration = DIResolver.Get().GetOptions().App; + if (configuration.SingleInstance) + { + var result = SingleInstance.Initialize(); + SingleInstance.InstanceLaunched += args => + { + if (!StaticResources.AllowCommandLineChange) + { + return; + } + + ParseArguments(args.CommandLineArgs); + Dispatcher.UIThread.SafeInvoke(() => + { + var mainWindow = (MainWindow)Helpers.GetMainWindow(); + var previousState = mainWindow.ActualState; + if (mainWindow.WindowState != WindowState.Minimized) + { + mainWindow.WindowState = WindowState.Minimized; + } + + mainWindow.WindowState = previousState; + mainWindow.Show(); + mainWindow.BringIntoView(); + mainWindow.Activate(); + mainWindow.Focus(); + }); + }; + return result; + } + + return true; + } + /// /// Logs the error. /// @@ -206,7 +264,7 @@ private static async void LogError(Exception e) var logger = DIResolver.Get(); logger.Fatal(e); - var runFatalErrorProcess = StaticResources.CommandLineOptions == null || !StaticResources.CommandLineOptions.ShowFatalErrorNotification; + var runFatalErrorProcess = !StaticResources.CommandLineOptions.ShowFatalErrorNotification; if (runFatalErrorProcess && !ExternalNotificationShown) { var path = Environment.ProcessPath; diff --git a/src/IronyModManager/Properties/PublishProfiles/linux-x64.pubxml b/src/IronyModManager/Properties/PublishProfiles/linux-x64.pubxml index 04545ee0d..b8f7f6db0 100644 --- a/src/IronyModManager/Properties/PublishProfiles/linux-x64.pubxml +++ b/src/IronyModManager/Properties/PublishProfiles/linux-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\linux-x64\net7.0\publish\linux-x64 + net8.0 + bin\x64\linux-x64\net8.0\publish\linux-x64 linux-x64 true False diff --git a/src/IronyModManager/Properties/PublishProfiles/osx-x64.pubxml b/src/IronyModManager/Properties/PublishProfiles/osx-x64.pubxml index df6bd9f9f..ff92dad82 100644 --- a/src/IronyModManager/Properties/PublishProfiles/osx-x64.pubxml +++ b/src/IronyModManager/Properties/PublishProfiles/osx-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\osx-x64\net7.0\publish\osx-x64 + net8.0 + bin\x64\osx-x64\net8.0\publish\osx-x64 osx-x64 true False diff --git a/src/IronyModManager/Properties/PublishProfiles/win-x64.pubxml b/src/IronyModManager/Properties/PublishProfiles/win-x64.pubxml index 1cd122792..cbd6468db 100644 --- a/src/IronyModManager/Properties/PublishProfiles/win-x64.pubxml +++ b/src/IronyModManager/Properties/PublishProfiles/win-x64.pubxml @@ -7,8 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem Release x64 - net7.0 - bin\x64\win-x64\net7.0\publish\win-x64 + net8.0 + bin\x64\win-x64\net8.0\publish\win-x64 win-x64 true False diff --git a/src/IronyModManager/StaticResources.cs b/src/IronyModManager/StaticResources.cs index bbaf843e6..7e444bbf0 100644 --- a/src/IronyModManager/StaticResources.cs +++ b/src/IronyModManager/StaticResources.cs @@ -4,7 +4,7 @@ // Created : 05-07-2020 // // Last Modified By : Mario -// Last Modified On : 10-28-2022 +// Last Modified On : 02-21-2024 // *********************************************************************** // // Mario @@ -31,6 +31,11 @@ public static class StaticResources { #region Fields + /// + /// The command line options + /// + private static CommandLineArgs commandLineOptions; + /// /// The application icon /// @@ -49,7 +54,7 @@ public static class StaticResources /// /// The updater path /// - private static string[] updaterPath = null; + private static string[] updaterPath; /// /// The window icon @@ -58,13 +63,49 @@ public static class StaticResources #endregion Fields + #region Delegates + + /// + /// Delegate CommandLineArgsChangedDelegate + /// + public delegate void CommandLineArgsChangedDelegate(); + + #endregion Delegates + + #region Events + + /// + /// Occurs when [command line arguments changed]. + /// + public static event CommandLineArgsChangedDelegate CommandLineArgsChanged; + + #endregion Events + #region Properties + /// + /// Gets or sets a value indicating whether the allow command line change. + /// + /// true if allow command line change; otherwise, false. + public static bool AllowCommandLineChange { get; set; } = true; + /// /// Gets or sets the command line options. /// /// The command line options. - public static CommandLineArgs CommandLineOptions { get; set; } + public static CommandLineArgs CommandLineOptions + { + get + { + commandLineOptions ??= new CommandLineArgs(); + return commandLineOptions; + } + set + { + commandLineOptions = value; + CommandLineArgsChanged?.Invoke(); + } + } /// /// Gets or sets a value indicating whether this instance is verifying container. @@ -87,6 +128,7 @@ public static WindowIcon GetAppIcon() using var ms = GetAppIconStream(); windowIcon = new WindowIcon(ms); } + return windowIcon; } @@ -101,6 +143,7 @@ public static Bitmap GetAppIconBitmap() using var ms = GetAppIconStream(); iconBitmap = new Bitmap(ms); } + return iconBitmap; } @@ -114,20 +157,23 @@ public static string GetLastKnownLocationInfo() { return lastKnownLocation; } + var firstSegment = string.Empty; var secondSegment = string.Empty; var entryAssembly = Assembly.GetEntryAssembly(); - var companyAttribute = (AssemblyCompanyAttribute)Attribute.GetCustomAttribute(entryAssembly, typeof(AssemblyCompanyAttribute)); - if (!string.IsNullOrEmpty(companyAttribute.Company)) + var companyAttribute = (AssemblyCompanyAttribute)Attribute.GetCustomAttribute(entryAssembly!, typeof(AssemblyCompanyAttribute)); + if (!string.IsNullOrEmpty(companyAttribute!.Company)) { firstSegment = $"{companyAttribute.Company}{Path.DirectorySeparatorChar}"; } + var titleAttribute = (AssemblyTitleAttribute)Attribute.GetCustomAttribute(entryAssembly, typeof(AssemblyTitleAttribute)); - if (!string.IsNullOrEmpty(titleAttribute.Title)) + if (!string.IsNullOrEmpty(titleAttribute!.Title)) { secondSegment = $"{titleAttribute.Title}-Location{Path.DirectorySeparatorChar}"; } + lastKnownLocation = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), $@"{firstSegment}{secondSegment}"); return lastKnownLocation; } @@ -176,8 +222,8 @@ static string generatePath(bool useProperSeparator) var secondSegment = string.Empty; var entryAssembly = Assembly.GetEntryAssembly(); - var companyAttribute = (AssemblyCompanyAttribute)Attribute.GetCustomAttribute(entryAssembly, typeof(AssemblyCompanyAttribute)); - if (!string.IsNullOrEmpty(companyAttribute.Company)) + var companyAttribute = (AssemblyCompanyAttribute)Attribute.GetCustomAttribute(entryAssembly!, typeof(AssemblyCompanyAttribute)); + if (!string.IsNullOrEmpty(companyAttribute!.Company)) { if (!useProperSeparator) { @@ -188,8 +234,9 @@ static string generatePath(bool useProperSeparator) firstSegment = $"{companyAttribute.Company}{Path.DirectorySeparatorChar}"; } } + var titleAttribute = (AssemblyTitleAttribute)Attribute.GetCustomAttribute(entryAssembly, typeof(AssemblyTitleAttribute)); - if (!string.IsNullOrEmpty(titleAttribute.Title)) + if (!string.IsNullOrEmpty(titleAttribute!.Title)) { if (!useProperSeparator) { @@ -200,14 +247,16 @@ static string generatePath(bool useProperSeparator) secondSegment = $"{titleAttribute.Title}-Updater{Path.DirectorySeparatorChar}"; } } + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), $@"{firstSegment}{secondSegment}"); } if (updaterPath == null) { - var col = new List() { generatePath(true), generatePath(false) }; + var col = new List { generatePath(true), generatePath(false) }; updaterPath = col.Distinct().ToArray(); } + return updaterPath; } diff --git a/src/IronyModManager/ViewModels/Controls/CollectionModsControlViewModel.cs b/src/IronyModManager/ViewModels/Controls/CollectionModsControlViewModel.cs index b0e45a04c..0c6193767 100644 --- a/src/IronyModManager/ViewModels/Controls/CollectionModsControlViewModel.cs +++ b/src/IronyModManager/ViewModels/Controls/CollectionModsControlViewModel.cs @@ -5,7 +5,7 @@ // Created : 03-03-2020 // // Last Modified By : Mario -// Last Modified On : 12-05-2023 +// Last Modified On : 02-10-2024 // *********************************************************************** // // Mario @@ -41,9 +41,9 @@ using IronyModManager.Models.Common; using IronyModManager.Services.Common; using IronyModManager.Shared; -using static IronyModManager.ViewModels.Controls.ModifyCollectionControlViewModel; using Nito.AsyncEx; using ReactiveUI; +using static IronyModManager.ViewModels.Controls.ModifyCollectionControlViewModel; namespace IronyModManager.ViewModels.Controls { @@ -284,7 +284,7 @@ public CollectionModsControlViewModel(IScrollState scrollState, ModExportProgres this.modExportProgressHandler = modExportProgressHandler; HashReportView = hashReportView; SearchMods.ShowArrows = true; - reorderQueue = new ConcurrentBag(); + reorderQueue = []; } #endregion Constructors @@ -954,7 +954,7 @@ async Task reorder() /// true if [is redo available]; otherwise, false. public virtual bool IsRedoAvailable() { - return redoStack.Any(); + return redoStack.Count != 0; } /// @@ -963,7 +963,7 @@ public virtual bool IsRedoAvailable() /// true if [is undo available]; otherwise, false. public virtual bool IsUndoAvailable() { - return undoStack.Any(); + return undoStack.Count != 0; } /// @@ -1191,8 +1191,8 @@ protected virtual void HandleModCollectionChange(bool resetStack) var missingMods = new List(); var hasModNames = existingCollection.ModNames != null && existingCollection.ModNames.Count() == existingCollection.Mods.Count(); var mods = existingCollection.Mods.ToList(); - var modPaths = existingCollection.ModPaths != null ? existingCollection.ModPaths.ToList() : new List(); - var modNames = hasModNames ? existingCollection.ModNames.ToList() : new List(); + var modPaths = existingCollection.ModPaths != null ? existingCollection.ModPaths.ToList() : []; + var modNames = hasModNames ? existingCollection.ModNames.ToList() : []; for (int i = 0; i < mods.Count; i++) { var item = mods[i]; @@ -1219,7 +1219,7 @@ protected virtual void HandleModCollectionChange(bool resetStack) } } } - if (missingMods.Any() && activeGame != null && existingCollection.Game.Equals(activeGame.Type)) + if (missingMods.Count != 0 && activeGame != null && existingCollection.Game.Equals(activeGame.Type)) { var title = localizationManager.GetResource(LocalizationResources.Collection_Mods.Prompts.ModsMissingTitle); var message = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Collection_Mods.Prompts.ModsMissingMessage), new { Environment.NewLine, Mods = string.Join(Environment.NewLine, missingMods) }); @@ -1280,7 +1280,7 @@ Task importInstance(IModCollection importData) if (importData != null) { importData.IsSelected = true; - modNames = importData.ModNames != null ? importData.ModNames.ToList() : new List(); + modNames = importData.ModNames != null ? importData.ModNames.ToList() : []; if (modCollectionService.Save(importData)) { return Task.FromResult(importData); @@ -1333,13 +1333,13 @@ Task importInstance(IModCollection importData) var game = gameService.Get().FirstOrDefault(p => p.Type.Equals(result.Game, StringComparison.OrdinalIgnoreCase)); await MessageBus.PublishAsync(new ActiveGameRequestEvent(game)); await MessageBus.PublishAsync(new ModListInstallRefreshRequestEvent(true)); - var hasMods = (modNames?.Any()).GetValueOrDefault(); - var mods = game != null ? await modService.GetAvailableModsAsync(game) ?? new List() : new List(); + var hasMods = modNames != null && modNames.Count != 0; + var mods = game != null ? await modService.GetAvailableModsAsync(game) ?? [] : []; if (hasMods && !string.IsNullOrWhiteSpace(result.MergedFolderName)) { var importedMods = new List(); var descriptors = result.Mods.ToList(); - var paths = result.ModPaths != null ? result.ModPaths.ToList() : new List(); + var paths = result.ModPaths != null ? result.ModPaths.ToList() : []; for (int i = 0; i < descriptors.Count; i++) { var descriptor = descriptors[i]; @@ -1366,8 +1366,8 @@ Task importInstance(IModCollection importData) result.Mods = importedMods; modCollectionService.Save(result); } - var modDescriptorPaths = result.Mods != null ? result.Mods.ToList() : new List(); - var modPaths = result.ModPaths != null ? result.ModPaths.ToList() : new List(); + var modDescriptorPaths = result.Mods != null ? result.Mods.ToList() : []; + var modPaths = result.ModPaths != null ? result.ModPaths.ToList() : []; restoreCollectionSelection = result.Name; LoadModCollections(); var showImportNotification = true; @@ -1379,7 +1379,7 @@ Task importInstance(IModCollection importData) if (nonExistingModPaths.Any()) { var nonExistingModNames = new List(); - var hasModNames = modNames != null && modNames.Any() && modDescriptorPaths.Count == modNames.Count; + var hasModNames = modNames != null && modNames.Count != 0 && modDescriptorPaths.Count == modNames.Count; foreach (var item in nonExistingModPaths) { var index = modDescriptorPaths.IndexOf(item); @@ -1404,7 +1404,7 @@ Task importInstance(IModCollection importData) } } } - if (nonExistingModNames.Any()) + if (nonExistingModNames.Count != 0) { var notExistingModTitle = localizationManager.GetResource(LocalizationResources.Collection_Mods.ImportNonExistingMods.Title); var nonExistingModMessage = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Collection_Mods.ImportNonExistingMods.Message), new { Environment.NewLine, Mods = string.Join(Environment.NewLine, nonExistingModNames) }); @@ -1484,7 +1484,7 @@ protected override void OnActivated(CompositeDisposable disposables) { EvaluateHighlight(); EvalGameSpecificVisibility(); - SetSelectedModsState(Mods != null ? Mods.Where(p => p.IsSelected).ToObservableCollection() : new ObservableCollection()); + SetSelectedModsState(Mods != null ? Mods.Where(p => p.IsSelected).ToObservableCollection() : []); SubscribeToMods(); skipModSelectionSave = true; @@ -1543,7 +1543,7 @@ protected override void OnActivated(CompositeDisposable disposables) mod.IsSelected = false; } } - SetSelectedModsState(Mods != null ? Mods.Where(p => p.IsSelected).ToObservableCollection() : new ObservableCollection()); + SetSelectedModsState(Mods != null ? Mods.Where(p => p.IsSelected).ToObservableCollection() : []); AllModsEnabled = SelectedMods?.Count > 0 && SelectedMods.All(p => p.IsSelected); LoadModCollections(); SaveState(); @@ -1663,7 +1663,7 @@ await Task.Run(async () => mod.IsSelected = false; } } - SetSelectedModsState(Mods != null ? Mods.Where(p => p.IsSelected).ToObservableCollection() : new ObservableCollection()); + SetSelectedModsState(Mods != null ? Mods.Where(p => p.IsSelected).ToObservableCollection() : []); AllModsEnabled = SelectedMods?.Count > 0 && SelectedMods.All(p => p.IsSelected); LoadModCollections(); SaveState(); @@ -1767,7 +1767,7 @@ await Task.Run(async () => { item.IsSelected = false; } - SetSelectedModsState(new List()); + SetSelectedModsState([]); SaveSelectedCollection(); } skipModCollectionSave = false; @@ -1919,7 +1919,7 @@ void registerReportHandlers(long id, bool useImportOverlay = false) { reports.AddRange(gameReports); } - if (reports.Any()) + if (reports.Count != 0) { await TriggerOverlayAsync(id, false); HashReportView.SetParameters(reports); @@ -2082,7 +2082,7 @@ protected virtual async Task PerformModReorderAsync(bool instant, CancellationTo if (SelectedMods != null) { using var mutex = await reorderLock.LockAsync(cancellationToken); - if (reorderQueue.Any()) + if (!reorderQueue.IsEmpty) { scrollState.SetState(false); skipModSelectionSave = true; @@ -2132,7 +2132,7 @@ protected virtual async Task PerformModReorderAsync(bool instant, CancellationTo /// protected virtual void PerformRedo() { - if (!redoStack.Any()) + if (redoStack.Count == 0) { return; } @@ -2186,7 +2186,7 @@ protected virtual void PerformRedoUndoOrdering(IEnumerable descriptors) /// protected virtual void PerformUndo() { - if (!undoStack.Any()) + if (undoStack.Count == 0) { return; } @@ -2205,7 +2205,7 @@ protected virtual void RecognizeSortOrder(IModCollection modCollection) { var mods = new List(); var colMods = modCollection.Mods.ToList(); - var colPaths = modCollection.ModPaths != null ? modCollection.ModPaths.ToList() : new List(); + var colPaths = modCollection.ModPaths != null ? modCollection.ModPaths.ToList() : []; for (int i = 0; i < colMods.Count; i++) { var item = colMods[i]; @@ -2220,7 +2220,7 @@ protected virtual void RecognizeSortOrder(IModCollection modCollection) mods.Add(mod); } } - if (mods.Any()) + if (mods.Count != 0) { var ascending = mods.OrderBy(p => p.Name, StringComparer.OrdinalIgnoreCase).Select(p => p.DescriptorFile); var descending = mods.OrderByDescending(p => p.Name, StringComparer.OrdinalIgnoreCase).Select(p => p.DescriptorFile); @@ -2311,7 +2311,7 @@ protected virtual void SaveSelectedCollection() } collection.Game = game; collection.Name = SelectedModCollection.Name; - var selectedMods = SelectedMods != null ? SelectedMods.Where(p => p.IsSelected) : new List(); + var selectedMods = SelectedMods != null ? SelectedMods.Where(p => p.IsSelected) : []; collection.Mods = selectedMods.Select(p => p.DescriptorFile).ToList(); collection.ModPaths = selectedMods.Select(p => p.FullPath).ToList(); collection.IsSelected = true; @@ -2408,7 +2408,7 @@ protected virtual void SetSelectedModsState(IList selectedMods, bool canSh previousValidatedMods.AddOrUpdate(SelectedModCollection.Name, oldMods, (k, v) => oldMods); if (!ignoreStack && !prevMods.ListsSame(selectedMods)) { - undoStack.Push((prevMods ?? new List()).Select(p => p.DescriptorFile).ToList()); + undoStack.Push((prevMods ?? []).Select(p => p.DescriptorFile).ToList()); redoStack.Clear(); } } diff --git a/src/IronyModManager/ViewModels/Controls/GameControlViewModel.cs b/src/IronyModManager/ViewModels/Controls/GameControlViewModel.cs index 92cbb2939..1f728bf03 100644 --- a/src/IronyModManager/ViewModels/Controls/GameControlViewModel.cs +++ b/src/IronyModManager/ViewModels/Controls/GameControlViewModel.cs @@ -1,10 +1,11 @@ -// *********************************************************************** + +// *********************************************************************** // Assembly : IronyModManager // Author : Mario // Created : 02-12-2020 // // Last Modified By : Mario -// Last Modified On : 02-23-2021 +// Last Modified On : 02-10-2024 // *********************************************************************** // // Mario @@ -16,6 +17,8 @@ using System.Linq; using System.Reactive.Disposables; using System.Reactive.Linq; +using Avalonia.Threading; +using IronyModManager.Common; using IronyModManager.Common.Events; using IronyModManager.Common.ViewModels; using IronyModManager.Implementation.MessageBus; @@ -27,25 +30,29 @@ namespace IronyModManager.ViewModels.Controls { + /// /// Class GameControlViewModel. /// Implements the /// /// + /// The game service. + /// The active game request handler. + /// Initializes a new instance of the class. [ExcludeFromCoverage("This should be tested via functional testing.")] - public class GameControlViewModel : BaseViewModel + public class GameControlViewModel(IGameService gameService, ActiveGameRequestHandler activeGameRequestHandler) : BaseViewModel { #region Fields /// /// The active game request handler /// - private readonly ActiveGameRequestHandler activeGameRequestHandler; + private readonly ActiveGameRequestHandler activeGameRequestHandler = activeGameRequestHandler; /// /// The game service /// - private readonly IGameService gameService; + private readonly IGameService gameService = gameService; /// /// The previous game @@ -54,21 +61,6 @@ public class GameControlViewModel : BaseViewModel #endregion Fields - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The game service. - /// The active game request handler. - public GameControlViewModel(IGameService gameService, ActiveGameRequestHandler activeGameRequestHandler) - { - this.gameService = gameService; - this.activeGameRequestHandler = activeGameRequestHandler; - } - - #endregion Constructors - #region Properties /// @@ -123,7 +115,10 @@ protected override void OnActivated(CompositeDisposable disposables) { if (m.Game != null) { - SelectedGame = Games.FirstOrDefault(p => p.Type.Equals(m.Game.Type, StringComparison.OrdinalIgnoreCase)); + Dispatcher.UIThread.SafeInvoke(() => + { + SelectedGame = Games.FirstOrDefault(p => p.Type.Equals(m.Game.Type, StringComparison.OrdinalIgnoreCase)); + }); } }).DisposeWith(disposables); diff --git a/src/IronyModManager/ViewModels/Controls/MergeViewerControlViewModel.cs b/src/IronyModManager/ViewModels/Controls/MergeViewerControlViewModel.cs index fdc54f3d1..c9329febe 100644 --- a/src/IronyModManager/ViewModels/Controls/MergeViewerControlViewModel.cs +++ b/src/IronyModManager/ViewModels/Controls/MergeViewerControlViewModel.cs @@ -4,20 +4,20 @@ // Created : 03-20-2020 // // Last Modified By : Mario -// Last Modified On : 04-27-2023 +// Last Modified On : 03-07-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reactive; using System.Reactive.Disposables; -using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia.Collections; using Avalonia.Threading; @@ -41,43 +41,56 @@ namespace IronyModManager.ViewModels.Controls { /// /// Class MergeViewerControlViewModel. - /// Implements the + /// Implements the /// - /// + /// [ExcludeFromCoverage("This should be tested via functional testing.")] - public class MergeViewerControlViewModel : BaseViewModel + public class MergeViewerControlViewModel( + IAppStateService appStateService, + IScrollState scrollState, + IModPatchCollectionService modPatchCollectionService, + ConflictSolverViewHotkeyPressedHandler hotkeyPressedHandler, + IAppAction appAction, + IExternalEditorService externalEditorService, + INotificationAction notificationAction, + ILocalizationManager localizationManager) : BaseViewModel { #region Fields /// /// The URL action /// - private readonly IAppAction appAction; + private readonly IAppAction appAction = appAction; + + /// + /// A private readonly IAppStateService named appStateService. + /// + private readonly IAppStateService appStateService = appStateService; /// /// The external editor service /// - private readonly IExternalEditorService externalEditorService; + private readonly IExternalEditorService externalEditorService = externalEditorService; /// /// The hotkey pressed handler /// - private readonly ConflictSolverViewHotkeyPressedHandler hotkeyPressedHandler; + private readonly ConflictSolverViewHotkeyPressedHandler hotkeyPressedHandler = hotkeyPressedHandler; /// /// The localization manager /// - private readonly ILocalizationManager localizationManager; + private readonly ILocalizationManager localizationManager = localizationManager; /// /// The mod patch collection service /// - private readonly IModPatchCollectionService modPatchCollectionService; + private readonly IModPatchCollectionService modPatchCollectionService = modPatchCollectionService; /// /// The notification action /// - private readonly INotificationAction notificationAction; + private readonly INotificationAction notificationAction = notificationAction; /// /// The redo stack @@ -87,7 +100,7 @@ public class MergeViewerControlViewModel : BaseViewModel /// /// The scroll state /// - private readonly IScrollState scrollState; + private readonly IScrollState scrollState = scrollState; /// /// The undo stack @@ -107,38 +120,10 @@ public class MergeViewerControlViewModel : BaseViewModel /// /// The syncing selection /// - private bool syncingSelection = false; + private bool syncingSelection; #endregion Fields - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// State of the scroll. - /// The mod patch collection service. - /// The hotkey pressed handler. - /// The application action. - /// The external editor service. - /// The notification action. - /// The localization manager. - public MergeViewerControlViewModel(IScrollState scrollState, - IModPatchCollectionService modPatchCollectionService, ConflictSolverViewHotkeyPressedHandler hotkeyPressedHandler, - IAppAction appAction, IExternalEditorService externalEditorService, - INotificationAction notificationAction, ILocalizationManager localizationManager) - { - this.appAction = appAction; - this.externalEditorService = externalEditorService; - this.notificationAction = notificationAction; - this.localizationManager = localizationManager; - this.hotkeyPressedHandler = hotkeyPressedHandler; - this.modPatchCollectionService = modPatchCollectionService; - this.scrollState = scrollState; - } - - #endregion Constructors - #region Delegates /// @@ -162,6 +147,16 @@ public MergeViewerControlViewModel(IScrollState scrollState, /// public event ConflictFoundDelegate ConflictFound; + /// + /// Occurs when [hotkey after perform]. + /// + public event EventHandler HotkeyAfterPerform; + + /// + /// Occurs when [hotkey copy before perform]. + /// + public event EventHandler HotkeyBeforePerform; + /// /// Occurs when [focus side]. /// @@ -242,9 +237,9 @@ public MergeViewerControlViewModel(IScrollState scrollState, public virtual string CopyThisAfterLine { get; protected set; } /// - /// Gets or sets the take other then this command. + /// Gets or sets the copy this after line command. /// - /// The take other then this command. + /// The copy this after line command. public virtual ReactiveCommand CopyThisAfterLineCommand { get; protected set; } /// @@ -285,6 +280,18 @@ public MergeViewerControlViewModel(IScrollState scrollState, /// The delete text command. public virtual ReactiveCommand DeleteTextCommand { get; protected set; } + /// + /// Gets or sets a value representing the diff editor bottom line. + /// + /// The diff editor bottom line. + public virtual int? DiffEditorBottomLine { get; set; } + + /// + /// Gets or sets a value representing the diff editor top line. + /// + /// The diff editor top line. + public virtual int? DiffEditorTopLine { get; set; } + /// /// Gets or sets a value indicating whether [editing left]. /// @@ -511,6 +518,18 @@ public MergeViewerControlViewModel(IScrollState scrollState, /// The previous conflict command. public virtual ReactiveCommand PrevConflictCommand { get; protected set; } + /// + /// Gets or sets a value representing the previous left diff. + /// + /// The previous left diff. + public virtual IList PreviousLeftDiff { get; set; } + + /// + /// Gets or sets a value representing the previous right diff. + /// + /// The previous right diff. + public IList PreviousRightDiff { get; set; } + /// /// Gets or sets the read only editor. /// @@ -567,6 +586,19 @@ public MergeViewerControlViewModel(IScrollState scrollState, /// The right side selected. public virtual IAvaloniaList RightSideSelected { get; set; } + /// + /// Gets or sets a value representing the toggle merge type caption. + /// + /// The toggle merge type caption. + [StaticLocalization(LocalizationResources.Conflict_Solver.ContextMenu.ToggleCompare)] + public virtual string ToggleMergeTypeCaption { get; protected set; } + + /// + /// Gets or sets a value representing the toggle merge type command. + /// + /// The toggle merge type command. + public virtual ReactiveCommand ToggleMergeTypeCommand { get; protected set; } + /// /// Gets or sets the undo. /// @@ -580,6 +612,12 @@ public MergeViewerControlViewModel(IScrollState scrollState, /// The undo command. public virtual ReactiveCommand UndoCommand { get; protected set; } + /// + /// Gets or sets a value indicating whether the using new merge type. + /// + /// true if using new merge type; otherwise, false. + public virtual bool UsingNewMergeType { get; protected set; } + #endregion Properties #region Methods @@ -606,6 +644,8 @@ public virtual void InitParameters() { EditorAvailable = true; } + + EvaluateMergeType(); } /// @@ -614,7 +654,7 @@ public virtual void InitParameters() /// true if [is redo available]; otherwise, false. public virtual bool IsRedoAvailable() { - return redoStack.Any(); + return redoStack.Count != 0; } /// @@ -623,7 +663,7 @@ public virtual bool IsRedoAvailable() /// true if [is undo available]; otherwise, false. public virtual bool IsUndoAvailable() { - return undoStack.Any(); + return undoStack.Count != 0; } /// @@ -664,15 +704,12 @@ void evalStack(string text, string prevText) prevText ??= string.Empty; if (undoStack.Count == 0 && string.IsNullOrWhiteSpace(prevText)) { - return; } - else if (undoStack.Count > 0 && undoStack.FirstOrDefault().Equals(prevText)) + else if (undoStack.Count > 0 && undoStack.FirstOrDefault()!.Equals(prevText)) { - return; } else if (prevText.Equals(text)) { - return; } else { @@ -680,6 +717,7 @@ void evalStack(string text, string prevText) redoStack.Clear(); } } + scrollState.SetState(!lockScroll); var prevLeftSide = LeftSide; var prevRightSide = RightSide; @@ -688,8 +726,8 @@ void evalStack(string text, string prevText) LeftDocument = new TextDocument(LeftSide); RightDocument = new TextDocument(RightSide); SetBracketText(); + Compare(LeftSide, RightSide); - Compare(); if (resetStack) { undoStack.Clear(); @@ -706,16 +744,21 @@ void evalStack(string text, string prevText) evalStack(RightSide, prevRightSide); } } + scrollState.SetState(true); } /// - /// Compares this instance. + /// Compare. /// - protected virtual void Compare() + /// The left. + /// The right. + protected virtual void Compare(string left, string right) { var builder = new SideBySideDiffBuilder(new Differ()); - var diff = builder.BuildDiffModel(LeftSide, RightSide, true); + var diff = builder.BuildDiffModel(left, right, true); + PreviousLeftDiff = LeftDiff; + PreviousRightDiff = RightDiff; LeftDiff = GetDiffPieceWithIndex(diff.OldText.Lines); RightDiff = GetDiffPieceWithIndex(diff.NewText.Lines); } @@ -731,7 +774,7 @@ protected virtual void Copy(IEnumerable selected, IList 0) { - int indx = 0; + var idx = 0; selected = CopyDiffPieceCollection(selected); source = CopyDiffPieceCollection(source); destination = CopyDiffPieceCollection(destination); @@ -740,8 +783,9 @@ protected virtual void Copy(IEnumerable selected, IList p.Text != null).Select(p => p.Text))); @@ -750,7 +794,8 @@ protected virtual void Copy(IEnumerable selected, IList p.Text != null).Select(p => p.Text)), RightSide); } - ConflictFound?.Invoke(indx); + + ConflictFound?.Invoke(idx); } } @@ -765,15 +810,12 @@ protected virtual void CopyAfterLines(IEnumerable selected, { if (selected?.Count() > 0) { - int indx = 0; + var idx = 0; selected = CopyDiffPieceCollection(selected); source = CopyDiffPieceCollection(source); destination = CopyDiffPieceCollection(destination); var ordered = OrderSelected(selected, source); - var grouped = ordered.Select((x, id) => - { - return Tuple.Create(x.Key, x.Value, id == 0 ? 0 : x.Key - ordered.ElementAt(id - 1).Key, id); - }).ToList(); + var grouped = ordered.Select((x, id) => Tuple.Create(x.Key, x.Value, id == 0 ? 0 : x.Key - ordered.ElementAt(id - 1).Key, id)).ToList(); Tuple initial = null; var appliedOffset = 0; foreach (var item in grouped) @@ -785,16 +827,15 @@ protected virtual void CopyAfterLines(IEnumerable selected, var groupCopy = grouped.Skip(item.Item4 + 1).TakeWhile(p => p.Item3 <= 1); if (groupCopy.Any()) { - if (groupCopy.Last() != initial) + if (groupCopy.Last() != null) { initial = groupCopy.Last(); } } - if (initial == null) - { - initial = item; - } + + initial ??= item; } + var index = initial.Item1 + item.Item4 + appliedOffset + 1; var count = destination.Count - 1; if (index < 0) @@ -805,6 +846,7 @@ protected virtual void CopyAfterLines(IEnumerable selected, { index = count; } + while (destination[index].Type == ChangeType.Imaginary) { if (index < 0) @@ -817,12 +859,15 @@ protected virtual void CopyAfterLines(IEnumerable selected, index = count; break; } + index++; appliedOffset++; } + destination.Insert(index, item.Item2); - indx = index; + idx = index; } + if (leftSide) { SetText(LeftSide, string.Join(Environment.NewLine, destination.Where(p => p.Text != null).Select(p => p.Text))); @@ -831,7 +876,8 @@ protected virtual void CopyAfterLines(IEnumerable selected, { SetText(string.Join(Environment.NewLine, destination.Where(p => p.Text != null).Select(p => p.Text)), RightSide); } - ConflictFound?.Invoke(indx); + + ConflictFound?.Invoke(idx); } } @@ -846,15 +892,12 @@ protected virtual void CopyBeforeLines(IEnumerable selected, { if (selected?.Count() > 0) { - int indx = 0; + var idx = 0; selected = CopyDiffPieceCollection(selected); source = CopyDiffPieceCollection(source); destination = CopyDiffPieceCollection(destination); var ordered = OrderSelected(selected, source); - var grouped = ordered.Select((x, id) => - { - return Tuple.Create(x.Key, x.Value, id == 0 ? 0 : x.Key - ordered.ElementAt(id - 1).Key, id); - }).ToList(); + var grouped = ordered.Select((x, id) => { return Tuple.Create(x.Key, x.Value, id == 0 ? 0 : x.Key - ordered.ElementAt(id - 1).Key, id); }).ToList(); Tuple initial = null; var appliedOffset = 0; foreach (var item in grouped) @@ -864,6 +907,7 @@ protected virtual void CopyBeforeLines(IEnumerable selected, appliedOffset = 0; initial = item; } + var index = initial.Item1 + item.Item4 + appliedOffset; var count = destination.Count - 1; if (index < 0) @@ -874,6 +918,7 @@ protected virtual void CopyBeforeLines(IEnumerable selected, { index = count; } + while (destination[index].Type == ChangeType.Imaginary) { if (index < 0) @@ -886,12 +931,15 @@ protected virtual void CopyBeforeLines(IEnumerable selected, index = count; break; } + index--; appliedOffset--; } + destination.Insert(index, item.Item2); - indx = index; + idx = index; } + if (leftSide) { SetText(LeftSide, string.Join(Environment.NewLine, destination.Where(p => p.Text != null).Select(p => p.Text))); @@ -900,7 +948,8 @@ protected virtual void CopyBeforeLines(IEnumerable selected, { SetText(string.Join(Environment.NewLine, destination.Where(p => p.Text != null).Select(p => p.Text)), RightSide); } - ConflictFound?.Invoke(indx); + + ConflictFound?.Invoke(idx); } } @@ -911,7 +960,7 @@ protected virtual void CopyBeforeLines(IEnumerable selected, /// System.Collections.Generic.List<IronyModManager.ViewModels.Controls.MergeViewerControlViewModel.DiffPieceWithIndex>. protected virtual List CopyDiffPieceCollection(IEnumerable col) { - return new List(col); + return [.. col]; } /// @@ -930,7 +979,7 @@ protected async Task CopyTextAsync(bool leftSide) /// if set to true [left side]. protected virtual void DeleteLines(bool leftSide) { - int indx = 0; + var idx = 0; var selected = leftSide ? LeftSideSelected : RightSideSelected; var source = CopyDiffPieceCollection(leftSide ? LeftDiff : RightDiff); if (selected != null && source != null && selected.Count > 0 && selected.Count <= source.Count) @@ -938,8 +987,9 @@ protected virtual void DeleteLines(bool leftSide) foreach (var item in selected) { source.Remove(item); - indx = item.Index; + idx = item.Index; } + if (leftSide) { SetText(string.Join(Environment.NewLine, source.Where(p => p.Text != null).Select(p => p.Text)), RightSide); @@ -948,10 +998,19 @@ protected virtual void DeleteLines(bool leftSide) { SetText(LeftSide, string.Join(Environment.NewLine, source.Where(p => p.Text != null).Select(p => p.Text))); } - ConflictFound?.Invoke(indx); + + ConflictFound?.Invoke(idx); } } + /// + /// Evaluates a merge type. + /// + protected virtual void EvaluateMergeType() + { + UsingNewMergeType = appStateService.Get().UseNewDiffViewer; + } + /// /// Finds the conflict. /// @@ -973,11 +1032,12 @@ protected virtual void FindConflict(bool leftSide, bool moveDown, bool skipImagi while (true) { idx++; - if (idx > (source.Count - 1)) + if (idx > source.Count - 1) { idx = source.Count - 1; break; } + var prevIdx = idx - 1; var type = source[prevIdx].Type; if (source[idx].Type == ChangeType.Unchanged || (type == ChangeType.Unchanged && source[idx].Type != ChangeType.Unchanged)) @@ -985,6 +1045,7 @@ protected virtual void FindConflict(bool leftSide, bool moveDown, bool skipImagi break; } } + var line = source.Skip(idx).FirstOrDefault(p => p.SubPieces.Count > 0 || !(p.Type == ChangeType.Unchanged || (skipImaginary && p.Type == ChangeType.Imaginary))); if (line != null) { @@ -993,6 +1054,7 @@ protected virtual void FindConflict(bool leftSide, bool moveDown, bool skipImagi { index = 0; } + line = source.Skip(index).TakeWhile(p => p.SubPieces.Count > 0 || !(p.Type == ChangeType.Unchanged || (skipImaginary && p.Type == ChangeType.Imaginary))).LastOrDefault(); if (line != null) { @@ -1007,22 +1069,25 @@ protected virtual void FindConflict(bool leftSide, bool moveDown, bool skipImagi while (true) { reverseIdx++; - if (reverseIdx > (reverseSrc.Count - 1)) + if (reverseIdx > reverseSrc.Count - 1) { reverseIdx = reverseSrc.Count - 1; break; } + var prevIdx = reverseIdx - 1; if (prevIdx < 0) { prevIdx = 0; } + var type = reverseSrc[prevIdx].Type; if (reverseSrc[reverseIdx].Type == ChangeType.Unchanged || (type == ChangeType.Unchanged && reverseSrc[reverseIdx].Type != ChangeType.Unchanged)) { break; } } + var line = reverseSrc.Skip(reverseIdx).FirstOrDefault(p => p.SubPieces.Count > 0 || !(p.Type == ChangeType.Unchanged || (skipImaginary && p.Type == ChangeType.Imaginary))); if (line != null) { @@ -1031,6 +1096,7 @@ protected virtual void FindConflict(bool leftSide, bool moveDown, bool skipImagi { index = 0; } + line = reverseSrc.Skip(index).TakeWhile(p => p.SubPieces.Count > 0 || !(p.Type == ChangeType.Unchanged || (skipImaginary && p.Type == ChangeType.Imaginary))).LastOrDefault(); if (line != null) { @@ -1038,6 +1104,7 @@ protected virtual void FindConflict(bool leftSide, bool moveDown, bool skipImagi } } } + if (matchIdx.HasValue) { ConflictFound?.Invoke(matchIdx.GetValueOrDefault()); @@ -1054,11 +1121,11 @@ protected virtual void FindConflict(bool leftSide, bool moveDown, bool skipImagi protected virtual List GetDiffPieceWithIndex(List lines) { var col = new List(); - int counter = 0; + var counter = 0; foreach (var item in lines) { counter++; - col.Add(new DiffPieceWithIndex() + col.Add(new DiffPieceWithIndex { Index = counter, Position = item.Position, @@ -1067,6 +1134,7 @@ protected virtual List GetDiffPieceWithIndex(List Type = item.Type }); } + return col; } @@ -1081,7 +1149,7 @@ protected virtual void Move(bool moveUp, IEnumerable selecte { if (selected?.Count() > 0) { - int indx = 0; + var idx = 0; selected = CopyDiffPieceCollection(selected); source = CopyDiffPieceCollection(source); var ordered = OrderSelected(selected, source); @@ -1097,6 +1165,7 @@ protected virtual void Move(bool moveUp, IEnumerable selecte { index = count; } + while (source[index].Type == ChangeType.Imaginary) { if (index < 0) @@ -1109,6 +1178,7 @@ protected virtual void Move(bool moveUp, IEnumerable selecte index = count; break; } + if (moveUp) { index--; @@ -1118,10 +1188,12 @@ protected virtual void Move(bool moveUp, IEnumerable selecte index++; } } + source.RemoveAt(item.Key); source.Insert(index, item.Value); - indx = index; + idx = index; } + if (leftSide) { SetText(string.Join(Environment.NewLine, source.Where(p => p.Text != null).Select(p => p.Text)), RightSide); @@ -1130,7 +1202,8 @@ protected virtual void Move(bool moveUp, IEnumerable selecte { SetText(LeftSide, string.Join(Environment.NewLine, source.Where(p => p.Text != null).Select(p => p.Text))); } - ConflictFound?.Invoke(indx); + + ConflictFound?.Invoke(idx); } } @@ -1143,22 +1216,24 @@ protected override void OnActivated(CompositeDisposable disposables) LeftSideSelected = new AvaloniaList(); RightSideSelected = new AvaloniaList(); - LeftSideSelected.CollectionChanged += (sender, args) => + LeftSideSelected.CollectionChanged += (_, _) => { if (syncingSelection) { return; } + syncingSelection = true; SyncSelectionsAsync(true).ConfigureAwait(true); }; - RightSideSelected.CollectionChanged += (sender, args) => + RightSideSelected.CollectionChanged += (_, _) => { if (syncingSelection) { return; } + syncingSelection = true; SyncSelectionsAsync(false).ConfigureAwait(true); }; @@ -1167,11 +1242,11 @@ protected override void OnActivated(CompositeDisposable disposables) { if (leftSide) { - Copy(LeftDiff, LeftDiff, RightDiff, leftSide); + Copy(LeftDiff, LeftDiff, RightDiff, true); } else { - Copy(RightDiff, RightDiff, LeftDiff, leftSide); + Copy(RightDiff, RightDiff, LeftDiff, false); } }).DisposeWith(disposables); @@ -1179,11 +1254,11 @@ protected override void OnActivated(CompositeDisposable disposables) { if (leftSide) { - Copy(LeftSideSelected, LeftDiff, RightDiff, leftSide); + Copy(LeftSideSelected, LeftDiff, RightDiff, true); } else { - Copy(RightSideSelected, RightDiff, LeftDiff, leftSide); + Copy(RightSideSelected, RightDiff, LeftDiff, false); } }).DisposeWith(disposables); @@ -1191,11 +1266,11 @@ protected override void OnActivated(CompositeDisposable disposables) { if (leftSide) { - CopyBeforeLines(LeftSideSelected, LeftDiff, RightDiff, leftSide); + CopyBeforeLines(LeftSideSelected, LeftDiff, RightDiff, true); } else { - CopyBeforeLines(RightSideSelected, RightDiff, LeftDiff, leftSide); + CopyBeforeLines(RightSideSelected, RightDiff, LeftDiff, false); } }).DisposeWith(disposables); @@ -1203,11 +1278,11 @@ protected override void OnActivated(CompositeDisposable disposables) { if (leftSide) { - CopyAfterLines(LeftSideSelected, LeftDiff, RightDiff, leftSide); + CopyAfterLines(LeftSideSelected, LeftDiff, RightDiff, true); } else { - CopyAfterLines(RightSideSelected, LeftDiff, RightDiff, leftSide); + CopyAfterLines(RightSideSelected, LeftDiff, RightDiff, false); } }).DisposeWith(disposables); @@ -1215,11 +1290,11 @@ protected override void OnActivated(CompositeDisposable disposables) { if (leftSide) { - Move(true, LeftSideSelected, LeftDiff, leftSide); + Move(true, LeftSideSelected, LeftDiff, true); } else { - Move(true, RightSideSelected, RightDiff, leftSide); + Move(true, RightSideSelected, RightDiff, false); } }).DisposeWith(disposables); @@ -1227,11 +1302,11 @@ protected override void OnActivated(CompositeDisposable disposables) { if (leftSide) { - Move(false, LeftSideSelected, LeftDiff, leftSide); + Move(false, LeftSideSelected, LeftDiff, true); } else { - Move(false, RightSideSelected, RightDiff, leftSide); + Move(false, RightSideSelected, RightDiff, false); } }).DisposeWith(disposables); @@ -1241,65 +1316,44 @@ protected override void OnActivated(CompositeDisposable disposables) { if (EditingLeft) { - string merged = string.Join(Environment.NewLine, LeftDocument.Text.SplitOnNewLine()); + var merged = string.Join(Environment.NewLine, LeftDocument.Text.SplitOnNewLine()); SetText(merged, RightSide); } else { - string merged = string.Join(Environment.NewLine, RightDocument.Text.SplitOnNewLine()); + var merged = string.Join(Environment.NewLine, RightDocument.Text.SplitOnNewLine()); SetText(LeftSide, merged); } + ExitEditMode(); }, okEnabled).DisposeWith(disposables); - CancelCommand = ReactiveCommand.Create(() => - { - ExitEditMode(); - }).DisposeWith(disposables); + CancelCommand = ReactiveCommand.Create(ExitEditMode).DisposeWith(disposables); - EditThisCommand = ReactiveCommand.Create((bool leftSide) => - { - SetEditThis(leftSide); - }).DisposeWith(disposables); + EditThisCommand = ReactiveCommand.Create((bool leftSide) => { SetEditThis(leftSide); }).DisposeWith(disposables); - CopyTextCommand = ReactiveCommand.Create((bool leftSide) => - { - CopyTextAsync(leftSide).ConfigureAwait(true); - }).DisposeWith(disposables); + CopyTextCommand = ReactiveCommand.Create((bool leftSide) => { CopyTextAsync(leftSide).ConfigureAwait(true); }).DisposeWith(disposables); - NextConflictCommand = ReactiveCommand.Create((bool leftSide) => - { - FindConflict(leftSide, true, false); - }).DisposeWith(disposables); + NextConflictCommand = ReactiveCommand.Create((bool leftSide) => { FindConflict(leftSide, true, false); }).DisposeWith(disposables); - PrevConflictCommand = ReactiveCommand.Create((bool leftSide) => - { - FindConflict(leftSide, false, false); - }).DisposeWith(disposables); + PrevConflictCommand = ReactiveCommand.Create((bool leftSide) => { FindConflict(leftSide, false, false); }).DisposeWith(disposables); - DeleteTextCommand = ReactiveCommand.Create((bool leftSide) => - { - DeleteLines(leftSide); - }).DisposeWith(disposables); + DeleteTextCommand = ReactiveCommand.Create((bool leftSide) => { DeleteLines(leftSide); }).DisposeWith(disposables); - UndoCommand = ReactiveCommand.Create((bool leftSide) => - { - PerformUndo(leftSide); - }).DisposeWith(disposables); + UndoCommand = ReactiveCommand.Create((bool leftSide) => { PerformUndo(leftSide); }).DisposeWith(disposables); - RedoCommand = ReactiveCommand.Create((bool leftSide) => - { - PerformRedo(leftSide); - }).DisposeWith(disposables); + RedoCommand = ReactiveCommand.Create((bool leftSide) => { PerformRedo(leftSide); }).DisposeWith(disposables); - EditorCommand = ReactiveCommand.CreateFromTask((bool leftSide) => - { - return LaunchExternalEditor(leftSide); - }).DisposeWith(disposables); + EditorCommand = ReactiveCommand.CreateFromTask((bool leftSide) => LaunchExternalEditorAsync(leftSide)).DisposeWith(disposables); - ReadOnlyEditorCommand = ReactiveCommand.CreateFromTask((bool leftSide) => + ReadOnlyEditorCommand = ReactiveCommand.CreateFromTask((bool leftSide) => LaunchExternalEditorAsync(leftSide, true)).DisposeWith(disposables); + + ToggleMergeTypeCommand = ReactiveCommand.Create(() => { - return LaunchExternalEditor(leftSide, true); + var state = appStateService.Get(); + state.UseNewDiffViewer = !state.UseNewDiffViewer; + appStateService.Save(state); + EvaluateMergeType(); }).DisposeWith(disposables); var previousEditTextState = false; @@ -1323,6 +1377,16 @@ void performAction() { LeftSideSelected.Add(LeftDiff.FirstOrDefault()); } + + if (UsingNewMergeType) + { + if (DiffEditorTopLine.HasValue && DiffEditorTopLine.GetValueOrDefault() - 1 < LeftDiff.Count) + { + LeftSideSelected.Clear(); + LeftSideSelected.Add(LeftDiff[DiffEditorTopLine.GetValueOrDefault() - 1]); + } + } + FindConflict(true, false, false); break; @@ -1331,6 +1395,16 @@ void performAction() { LeftSideSelected.Add(LeftDiff.FirstOrDefault()); } + + if (UsingNewMergeType) + { + if (DiffEditorBottomLine.HasValue && DiffEditorBottomLine.GetValueOrDefault() - 1 < LeftDiff.Count) + { + LeftSideSelected.Clear(); + LeftSideSelected.Add(LeftDiff[DiffEditorBottomLine.GetValueOrDefault() - 1]); + } + } + FindConflict(true, true, false); break; @@ -1339,6 +1413,16 @@ void performAction() { LeftSideSelected.Add(LeftDiff.FirstOrDefault()); } + + if (UsingNewMergeType) + { + if (DiffEditorTopLine.HasValue && DiffEditorTopLine.GetValueOrDefault() - 1 < LeftDiff.Count) + { + LeftSideSelected.Clear(); + LeftSideSelected.Add(LeftDiff[DiffEditorTopLine.GetValueOrDefault() - 1]); + } + } + FindConflict(true, false, true); break; @@ -1347,6 +1431,16 @@ void performAction() { LeftSideSelected.Add(LeftDiff.FirstOrDefault()); } + + if (UsingNewMergeType) + { + if (DiffEditorBottomLine.HasValue && DiffEditorBottomLine.GetValueOrDefault() - 1 < LeftDiff.Count) + { + LeftSideSelected.Clear(); + LeftSideSelected.Add(LeftDiff[DiffEditorBottomLine.GetValueOrDefault() - 1]); + } + } + FindConflict(true, true, true); break; @@ -1355,6 +1449,7 @@ void performAction() { SetEditThis(LeftSidePatchMod); } + break; case Enums.HotKeys.Ctrl_Shift_T: @@ -1368,34 +1463,49 @@ void performAction() case Enums.HotKeys.Ctrl_C: if (LeftSidePatchMod) { + HotkeyBeforePerform?.Invoke(this, EventArgs.Empty); Copy(RightSideSelected, RightDiff, LeftDiff, false); + HotkeyAfterPerform?.Invoke(this, EventArgs.Empty); } else if (RightSidePatchMod) { + HotkeyBeforePerform?.Invoke(this, EventArgs.Empty); Copy(LeftSideSelected, LeftDiff, RightDiff, true); + HotkeyAfterPerform?.Invoke(this, EventArgs.Empty); } + break; case Enums.HotKeys.Ctrl_V: if (LeftSidePatchMod) { + HotkeyBeforePerform?.Invoke(this, EventArgs.Empty); CopyBeforeLines(RightSideSelected, RightDiff, LeftDiff, false); + HotkeyAfterPerform?.Invoke(this, EventArgs.Empty); } else if (RightSidePatchMod) { + HotkeyBeforePerform?.Invoke(this, EventArgs.Empty); CopyBeforeLines(LeftSideSelected, LeftDiff, RightDiff, true); + HotkeyAfterPerform?.Invoke(this, EventArgs.Empty); } + break; case Enums.HotKeys.Ctrl_B: if (LeftSidePatchMod) { + HotkeyBeforePerform?.Invoke(this, EventArgs.Empty); CopyAfterLines(RightSideSelected, LeftDiff, RightDiff, false); + HotkeyAfterPerform?.Invoke(this, EventArgs.Empty); } else if (RightSidePatchMod) { + HotkeyBeforePerform?.Invoke(this, EventArgs.Empty); CopyAfterLines(LeftSideSelected, LeftDiff, RightDiff, true); + HotkeyAfterPerform?.Invoke(this, EventArgs.Empty); } + break; case Enums.HotKeys.Ctrl_Z: @@ -1408,6 +1518,7 @@ void performAction() PostFocusSide?.Invoke(LeftSidePatchMod); } } + break; case Enums.HotKeys.Ctrl_Y: @@ -1420,23 +1531,23 @@ void performAction() PostFocusSide?.Invoke(LeftSidePatchMod); } } + break; case Enums.HotKeys.Ctrl_X: if (LeftSidePatchMod || RightSidePatchMod) { - LaunchExternalEditor(LeftSidePatchMod).ConfigureAwait(true); + LaunchExternalEditorAsync(LeftSidePatchMod).ConfigureAwait(true); } else { - LaunchExternalEditor(LeftSidePatchMod, true).ConfigureAwait(true); + LaunchExternalEditorAsync(LeftSidePatchMod, true).ConfigureAwait(true); } - break; - default: break; } } + if (CanPerformHotKeyActions) { Dispatcher.UIThread.SafeInvoke(performAction); @@ -1460,6 +1571,7 @@ protected virtual Dictionary OrderSelected(IEnumerable< var idx = source.IndexOf(item); orderedSelected.Add(idx, item); } + return orderedSelected.OrderBy(p => p.Key).ToDictionary(p => p.Key, p => p.Value); } @@ -1473,6 +1585,7 @@ protected virtual void PerformRedo(bool leftSide) { return; } + if (leftSide) { undoStack.Push(LeftSide); @@ -1495,6 +1608,7 @@ protected virtual void PerformUndo(bool leftSide) { return; } + if (leftSide) { redoStack.Push(LeftSide); @@ -1516,14 +1630,8 @@ protected virtual void SetEditThis(bool leftSide) EditingLeft = leftSide; EditingRight = !leftSide; EditingText = true; - if (leftSide) - { - CurrentEditText = LeftSide; - } - else - { - CurrentEditText = RightSide; - } + CurrentEditText = leftSide ? LeftSide : RightSide; + LeftDocument = new TextDocument(LeftSide); RightDocument = new TextDocument(RightSide); } @@ -1540,7 +1648,7 @@ protected virtual async Task SyncSelectionsAsync(bool leftSide) var syncDiff = !leftSide ? LeftDiff : RightDiff; var sourceCol = leftSide ? LeftSideSelected : RightSideSelected; var syncCol = !leftSide ? LeftSideSelected : RightSideSelected; - bool clearCol = true; + var clearCol = true; if (sourceCol?.Count > 0) { var filtered = sourceCol.Where(p => p != null); // Must be an underlying bug? @@ -1554,10 +1662,12 @@ protected virtual async Task SyncSelectionsAsync(bool leftSide) } } } + if (clearCol) { syncCol.Clear(); } + syncingSelection = false; } @@ -1566,7 +1676,8 @@ protected virtual async Task SyncSelectionsAsync(bool leftSide) /// /// if set to true [left side]. /// if set to true [no patch mod edit]. - private async Task LaunchExternalEditor(bool leftSide, bool noPatchModEdit = false) + /// A Task<System.Threading.Tasks.Task> representing the asynchronous operation. + private async Task LaunchExternalEditorAsync(bool leftSide, bool noPatchModEdit = false) { var opts = externalEditorService.Get(); var left = leftDefinition; @@ -1582,6 +1693,7 @@ private async Task LaunchExternalEditor(bool leftSide, bool noPatchModEdit = fal // Override if analyze mode only noPatchModEdit = true; } + if (await appAction.RunAsync(opts.ExternalEditorLocation, arguments)) { string title; @@ -1596,6 +1708,7 @@ private async Task LaunchExternalEditor(bool leftSide, bool noPatchModEdit = fal message = localizationManager.GetResource(LocalizationResources.Conflict_Solver.ReadonlyEditor.Message); title = localizationManager.GetResource(LocalizationResources.Conflict_Solver.ReadonlyEditor.Title); } + if (await notificationAction.ShowPromptAsync(title, title, message, NotificationType.Info, !noPatchModEdit ? PromptType.ConfirmCancel : PromptType.OK)) { if (!noPatchModEdit) @@ -1603,19 +1716,20 @@ private async Task LaunchExternalEditor(bool leftSide, bool noPatchModEdit = fal if (leftSide) { var text = files.LeftDiff.Text ?? string.Empty; - string merged = string.Join(Environment.NewLine, text.ReplaceTabs()); + var merged = string.Join(Environment.NewLine, text.ReplaceTabs()); SetText(merged, RightSide); } else { var text = files.RightDiff.Text ?? string.Empty; - string merged = string.Join(Environment.NewLine, text.ReplaceTabs()); + var merged = string.Join(Environment.NewLine, text.ReplaceTabs()); SetText(LeftSide, merged); } } } } - files?.Dispose(); + + files.Dispose(); } } @@ -1679,10 +1793,10 @@ public override bool Equals(object obj) } /// - /// Equalses the specified other. + /// Equals. /// /// The other. - /// bool. + /// A bool. public new bool Equals(DiffPiece other) { var result = base.Equals(other); @@ -1690,6 +1804,7 @@ public override bool Equals(object obj) { return Index.Equals(diffPieceWithIndex.Index); } + return result; } @@ -1710,6 +1825,7 @@ public bool IsMatch(string term) { return false; } + term ??= string.Empty; return Text.Trim().StartsWith(term, StringComparison.OrdinalIgnoreCase); } diff --git a/src/IronyModManager/ViewModels/Controls/ModHolderControlViewModel.cs b/src/IronyModManager/ViewModels/Controls/ModHolderControlViewModel.cs index 4b28e0048..8eb6af8da 100644 --- a/src/IronyModManager/ViewModels/Controls/ModHolderControlViewModel.cs +++ b/src/IronyModManager/ViewModels/Controls/ModHolderControlViewModel.cs @@ -1,11 +1,10 @@ - -// *********************************************************************** +// *********************************************************************** // Assembly : IronyModManager // Author : Mario // Created : 02-29-2020 // // Last Modified By : Mario -// Last Modified On : 11-27-2023 +// Last Modified On : 02-25-2024 // *********************************************************************** // // Mario @@ -21,6 +20,7 @@ using System.Reactive.Disposables; using System.Reactive.Linq; using System.Threading.Tasks; +using Avalonia.Threading; using IronyModManager.Common; using IronyModManager.Common.Events; using IronyModManager.Common.ViewModels; @@ -42,7 +42,6 @@ namespace IronyModManager.ViewModels.Controls { - /// /// Class ModHolderViewModel. /// Implements the @@ -88,6 +87,11 @@ public class ModHolderControlViewModel : BaseViewModel /// private readonly IGameIndexService gameIndexService; + /// + /// The game language service + /// + private readonly IGameLanguageService gameLanguageService; + /// /// The game service /// @@ -154,49 +158,49 @@ public class ModHolderControlViewModel : BaseViewModel private readonly IPromptNotificationsService promptNotificationsService; /// - /// The shut down state + /// The shut-down state /// private readonly IShutDownState shutDownState; /// /// The definition analyze load handler /// - private IDisposable definitionAnalyzeLoadHandler = null; + private IDisposable definitionAnalyzeLoadHandler; /// /// The definition load handler /// - private IDisposable definitionLoadHandler = null; + private IDisposable definitionLoadHandler; /// /// The definition synchronize handler /// - private IDisposable definitionSyncHandler = null; + private IDisposable definitionSyncHandler; /// /// The force enable resume button /// - private bool forceEnableResumeButton = false; + private bool forceEnableResumeButton; /// /// The game definition load handler /// - private IDisposable gameDefinitionLoadHandler = null; + private IDisposable gameDefinitionLoadHandler; /// /// The game index handler /// - private IDisposable gameIndexHandler = null; + private IDisposable gameIndexHandler; /// /// The mod invalid replace handler /// - private IDisposable modInvalidReplaceHandler = null; + private IDisposable modInvalidReplaceHandler; /// /// The showing invalid notification /// - private bool showingInvalidNotification = false; + private bool showingInvalidNotification; #endregion Fields @@ -205,6 +209,7 @@ public class ModHolderControlViewModel : BaseViewModel /// /// Initializes a new instance of the class. /// + /// The game language service. /// The external process handler service. /// The game definition load progress handler. /// The game index progress handler. @@ -213,7 +218,7 @@ public class ModHolderControlViewModel : BaseViewModel /// The mod list install refresh request handler. /// The mod definition invalid replace handler. /// The identifier generator. - /// State of the shut down. + /// State of the shut-down. /// The mod service. /// The mod patch collection service. /// The game service. @@ -227,7 +232,8 @@ public class ModHolderControlViewModel : BaseViewModel /// The mod definition patch load handler. /// The game directory changed handler. /// The logger. - public ModHolderControlViewModel(IExternalProcessHandlerService externalProcessHandlerService, GameDefinitionLoadProgressHandler gameDefinitionLoadProgressHandler, GameIndexProgressHandler gameIndexProgressHandler, + public ModHolderControlViewModel(IGameLanguageService gameLanguageService, IExternalProcessHandlerService externalProcessHandlerService, GameDefinitionLoadProgressHandler gameDefinitionLoadProgressHandler, + GameIndexProgressHandler gameIndexProgressHandler, IGameIndexService gameIndexService, IPromptNotificationsService promptNotificationsService, ModListInstallRefreshRequestHandler modListInstallRefreshRequestHandler, ModDefinitionInvalidReplaceHandler modDefinitionInvalidReplaceHandler, IIDGenerator idGenerator, IShutDownState shutDownState, IModService modService, IModPatchCollectionService modPatchCollectionService, IGameService gameService, @@ -257,13 +263,23 @@ public ModHolderControlViewModel(IExternalProcessHandlerService externalProcessH this.gameIndexProgressHandler = gameIndexProgressHandler; this.gameDefinitionLoadProgressHandler = gameDefinitionLoadProgressHandler; this.externalProcessHandlerService = externalProcessHandlerService; + this.gameLanguageService = gameLanguageService; InstalledMods = installedModsControlViewModel; CollectionMods = collectionModsControlViewModel; UseSimpleLayout = !DIResolver.Get().GetOptions().ConflictSolver.UseSubMenus; - if (StaticResources.CommandLineOptions != null && StaticResources.CommandLineOptions.EnableResumeGameButton) + if (StaticResources.CommandLineOptions.EnableResumeGameButton) { forceEnableResumeButton = true; } + + StaticResources.CommandLineArgsChanged += () => + { + Dispatcher.UIThread.SafeInvoke(() => + { + forceEnableResumeButton = StaticResources.CommandLineOptions.EnableResumeGameButton; + EvalResumeAvailability(); + }); + }; } #endregion Constructors @@ -321,47 +337,47 @@ public ModHolderControlViewModel(IExternalProcessHandlerService externalProcessH public virtual bool AllowModSelection { get; protected set; } /// - /// Gets or sets the analyze. + /// Gets or sets the analysis. /// - /// The analyze. + /// The analysis. [StaticLocalization(LocalizationResources.Mod_Actions.ConflictSolver.Conflict)] public virtual string Analyze { get; protected set; } /// - /// Gets or sets the analyze class. + /// Gets or sets the analysis class. /// - /// The analyze class. + /// The analysis class. public virtual string AnalyzeClass { get; protected set; } /// - /// Gets or sets the analyze command. + /// Gets or sets the analysis command. /// - /// The analyze command. + /// The analysis command. public virtual ReactiveCommand AnalyzeCommand { get; protected set; } /// - /// Gets or sets the analyze mode. + /// Gets or sets the analysis mode. /// - /// The analyze mode. + /// The analysis mode. [StaticLocalization(LocalizationResources.Conflict_Solver.Modes.Analyze)] public virtual string AnalyzeMode { get; protected set; } /// - /// Gets or sets the analyze mode command. + /// Gets or sets the analysis mode command. /// - /// The analyze mode command. + /// The analysis mode command. public virtual ReactiveCommand AnalyzeModeCommand { get; protected set; } /// - /// Gets or sets the analyze mode without localization command. + /// Gets or sets the analysis mode without localization command. /// - /// The analyze mode without localization command. + /// The analysis mode without localization command. public virtual ReactiveCommand AnalyzeModeWithoutLocalizationCommand { get; protected set; } /// - /// Gets or sets the analyze without localization mode. + /// Gets or sets the analysis without localization mode. /// - /// The analyze without localization mode. + /// The analysis without localization mode. [StaticLocalization(LocalizationResources.Conflict_Solver.Modes.AnalyzeWithoutLocalization)] public virtual string AnalyzeWithoutLocalizationMode { get; protected set; } @@ -561,17 +577,24 @@ protected virtual async Task AnalyzeModsAsync(long id, PatchStateMode mode, IEnu SubscribeToProgressReport(id, Disposables, totalSteps); - var overlayProgress = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Progress), new - { - PercentDone = 0.ToLocalizedPercentage(), - Count = 1, - TotalCount = totalSteps - }); + var overlayProgress = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Progress), + new { PercentDone = 0.ToLocalizedPercentage(), Count = 1, TotalCount = totalSteps }); var message = localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Loading_Definitions); await TriggerOverlayAsync(id, true, message, overlayProgress); modPatchCollectionService.InvalidatePatchModState(CollectionMods.SelectedModCollection.Name); modPatchCollectionService.ResetPatchStateCache(); + IReadOnlyCollection allowedLanguages; + var usedAllowedLanguages = await modPatchCollectionService.GetAllowedLanguagesAsync(CollectionMods.SelectedModCollection.Name); + if (usedAllowedLanguages != null) + { + allowedLanguages = gameLanguageService.GetByAbrv(usedAllowedLanguages); + } + else + { + allowedLanguages = gameLanguageService.GetSelected(); + } + var tooLargeMod = false; var game = gameService.GetSelected(); var stopWatch = new Stopwatch(); @@ -581,17 +604,15 @@ protected virtual async Task AnalyzeModsAsync(long id, PatchStateMode mode, IEnu IIndexedDefinitions result = null; try { - result = await modPatchCollectionService.GetModObjectsAsync(gameService.GetSelected(), CollectionMods.SelectedMods, CollectionMods.SelectedModCollection.Name, mode).ConfigureAwait(false); + result = await modPatchCollectionService + .GetModObjectsAsync(gameService.GetSelected(), CollectionMods.SelectedMods, CollectionMods.SelectedModCollection.Name, mode, allowedLanguages).ConfigureAwait(false); } catch (ModTooLargeException) { tooLargeMod = true; } - // To stop people from whining - GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); - GC.WaitForPendingFinalizers(); - GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); + GCRunner.RunGC(GCCollectionMode.Aggressive, true); return result; }).ConfigureAwait(false); @@ -606,16 +627,13 @@ protected virtual async Task AnalyzeModsAsync(long id, PatchStateMode mode, IEnu gameIndexHandler?.Dispose(); gameDefinitionLoadHandler?.Dispose(); - // I know, I know... but I wanna force a cleanup - GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); - GC.WaitForPendingFinalizers(); - GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); - + GCRunner.RunGC(GCCollectionMode.Aggressive, true); var largeMessageTitle = localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.TooLargePrompt.Title); var largeMessageBody = localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.TooLargePrompt.Message); notificationAction.ShowNotification(largeMessageTitle, largeMessageBody, NotificationType.Error, 60); return; } + if (versions != null && versions.Any()) { stopWatch.Restart(); @@ -623,38 +641,31 @@ await Task.Run(async () => { await gameIndexService.IndexDefinitionsAsync(game, versions, definitions); - // To stop people from whining - GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); - GC.WaitForPendingFinalizers(); - GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); + GCRunner.RunGC(GCCollectionMode.Aggressive, true); }); Debug.WriteLine("Conflict Solver Stage 2: " + stopWatch.Elapsed.FormatElapsed()); stopWatch.Restart(); definitions = await Task.Run(async () => { - var result = await gameIndexService.LoadDefinitionsAsync(definitions, game, versions); + var result = await gameIndexService.LoadDefinitionsAsync(definitions, game, versions, allowedLanguages); - // To stop people from whining - GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); - GC.WaitForPendingFinalizers(); - GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); + GCRunner.RunGC(GCCollectionMode.Aggressive, true); return result; }).ConfigureAwait(false); Debug.WriteLine("Conflict Solver Stage 3: " + stopWatch.Elapsed.FormatElapsed()); } + stopWatch.Restart(); var conflicts = await Task.Run(async () => { if (definitions != null) { - // To stop people from whining - var result = await modPatchCollectionService.FindConflictsAsync(definitions, CollectionMods.SelectedMods.Select(p => p.Name).ToList(), mode); - GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); - GC.WaitForPendingFinalizers(); - GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); + var result = await modPatchCollectionService.FindConflictsAsync(definitions, CollectionMods.SelectedMods.Select(p => p.Name).ToList(), mode, allowedLanguages); + GCRunner.RunGC(GCCollectionMode.Aggressive, true); return result; } + return null; }).ConfigureAwait(false); Debug.WriteLine("Conflict Solver Stage 4: " + stopWatch.Elapsed.FormatElapsed()); @@ -664,10 +675,7 @@ await Task.Run(async () => { var result = await modPatchCollectionService.InitializePatchStateAsync(conflicts, CollectionMods.SelectedModCollection.Name).ConfigureAwait(false); - // To stop people from whining - GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); - GC.WaitForPendingFinalizers(); - GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); + GCRunner.RunGC(GCCollectionMode.Aggressive, true); return result; }).ConfigureAwait(false); if (syncedConflicts != null) @@ -676,11 +684,11 @@ await Task.Run(async () => } Debug.WriteLine("Conflict Solver Stage 5: " + stopWatch.Elapsed.FormatElapsed()); - var args = new NavigationEventArgs() + var args = new NavigationEventArgs { SelectedCollection = CollectionMods.SelectedModCollection, Results = conflicts, - State = mode == PatchStateMode.ReadOnly || mode == PatchStateMode.ReadOnlyWithoutLocalization ? NavigationState.ReadOnlyConflictSolver : NavigationState.ConflictSolver, + State = mode is PatchStateMode.ReadOnly or PatchStateMode.ReadOnlyWithoutLocalization ? NavigationState.ReadOnlyConflictSolver : NavigationState.ConflictSolver, SelectedMods = CollectionMods.SelectedMods.Select(p => p.Name).ToList() }; ReactiveUI.MessageBus.Current.SendMessage(args); @@ -692,10 +700,7 @@ await Task.Run(async () => gameIndexHandler?.Dispose(); gameDefinitionLoadHandler?.Dispose(); - // I know, I know... but I wanna force a cleanup - GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); - GC.WaitForPendingFinalizers(); - GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); + GCRunner.RunGC(GCCollectionMode.Aggressive, true); } /// @@ -711,6 +716,7 @@ protected virtual async Task ApplyCollectionAsync(long id, bool showOverlay = tr { return; } + if (validateParadoxLauncher) { if (await externalProcessHandlerService.IsParadoxLauncherRunningAsync()) @@ -721,6 +727,7 @@ protected virtual async Task ApplyCollectionAsync(long id, bool showOverlay = tr return; } } + ApplyingCollection = true; if (CollectionMods.SelectedModCollection != null) { @@ -728,10 +735,11 @@ protected virtual async Task ApplyCollectionAsync(long id, bool showOverlay = tr { await TriggerOverlayAsync(id, true, localizationManager.GetResource(LocalizationResources.Mod_Actions.Overlay_Apply_Message)); } + var notificationType = NotificationType.Success; try { - var result = await modService.ExportModsAsync(CollectionMods.SelectedMods.ToList(), InstalledMods.AllMods.ToList(), CollectionMods.SelectedModCollection); + var result = await modService.ExportModsAsync([.. CollectionMods.SelectedMods], [.. InstalledMods.AllMods], CollectionMods.SelectedModCollection); string title; string message; if (result) @@ -745,6 +753,7 @@ protected virtual async Task ApplyCollectionAsync(long id, bool showOverlay = tr message = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Notifications.CollectionNotApplied.Message), new { CollectionName = CollectionMods.SelectedModCollection.Name }); notificationType = NotificationType.Error; } + notificationAction.ShowNotification(title, message, notificationType, 5); } catch (Exception ex) @@ -754,16 +763,18 @@ protected virtual async Task ApplyCollectionAsync(long id, bool showOverlay = tr logger.Error(ex); notificationAction.ShowNotification(title, message, NotificationType.Error, 30); } + if (showOverlay) { await TriggerOverlayAsync(id, false); } } + ApplyingCollection = false; } /// - /// Evals the resume availability. + /// Evaluate the resume availability. /// /// The game. protected virtual void EvalResumeAvailability(IGame game = null) @@ -802,13 +813,14 @@ protected virtual async Task InstallModsAsync(bool skipOverlay = false) var result = await modService.InstallModsAsync(InstalledMods.Mods); if (result != null) { - if (result.Any(p => p.Installed == true)) + if (result.Any(p => p.Installed)) { if (InstalledMods.IsActivated) { - await InstalledMods.RefreshModsAsync(skipOverlay: skipOverlay); + await InstalledMods.RefreshModsAsync(skipOverlay); } } + if (result.Any(p => p.Invalid)) { await ShowInvalidModsNotificationAsync(result.Where(p => p.Invalid).ToList()); @@ -837,6 +849,7 @@ async Task runAnalysis(PatchStateMode mode) var message = localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.GameExecutableNotSetPrompt.Message); proceed = await notificationAction.ShowPromptAsync(title, title, message, NotificationType.Info, PromptType.YesNo); } + if (proceed) { await AnalyzeModsAsync(id, mode, versions); @@ -849,11 +862,11 @@ async Task runAnalysis(PatchStateMode mode) Task.Run(() => EvalResumeAvailabilityLoopAsync().ConfigureAwait(false)); - ShowAdvancedFeatures = (gameService.GetSelected()?.AdvancedFeatures) != GameAdvancedFeatures.None; + ShowAdvancedFeatures = gameService.GetSelected()?.AdvancedFeatures != GameAdvancedFeatures.None; AnalyzeClass = string.Empty; var allowModSelectionEnabled = this.WhenAnyValue(v => v.AllowModSelection); - var applyEnabled = Observable.Merge(this.WhenAnyValue(v => v.ApplyingCollection, v => !v), allowModSelectionEnabled); + var applyEnabled = this.WhenAnyValue(v => v.ApplyingCollection, v => !v).Merge(allowModSelectionEnabled); this.WhenAnyValue(v => v.CollectionMods.SelectedModCollection).Subscribe(s => { @@ -869,6 +882,7 @@ async Task runAnalysis(PatchStateMode mode) InstalledMods.AllowModSelection = false; CollectionMods.AllowModSelection = false; } + InstallModsAsync().ConfigureAwait(true); }).DisposeWith(disposables); @@ -882,12 +896,12 @@ async Task runAnalysis(PatchStateMode mode) CollectionMods.HandleModRefresh(s, InstalledMods.Mods, InstalledMods.ActiveGame); }).DisposeWith(disposables); - this.WhenAnyValue(v => v.CollectionMods.NeedsModListRefresh).Where(x => x).Subscribe(async s => + this.WhenAnyValue(v => v.CollectionMods.NeedsModListRefresh).Where(x => x).Subscribe(async _ => { await InstalledMods.RefreshModsAsync(); }).DisposeWith(disposables); - this.WhenAnyValue(v => v.InstalledMods.GameChangedRefresh).Where(x => x).Subscribe(s => + this.WhenAnyValue(v => v.InstalledMods.GameChangedRefresh).Where(x => x).Subscribe(_ => { CollectionMods.ReloadModCollection(); }).DisposeWith(disposables); @@ -916,6 +930,7 @@ async Task runAnalysis(PatchStateMode mode) messageState.ConflictSolverPromptShown = true; promptNotificationsService.Save(messageState); } + var id = idGenerator.GetNextId(); await TriggerOverlayAsync(id, true, localizationManager.GetResource(LocalizationResources.App.WaitBackgroundOperationMessage)); await shutDownState.WaitUntilFreeAsync(); @@ -923,7 +938,6 @@ async Task runAnalysis(PatchStateMode mode) if (game.AdvancedFeatures == GameAdvancedFeatures.Full) { var mode = await modPatchCollectionService.GetPatchStateModeAsync(CollectionMods.SelectedModCollection.Name); - var versions = gameService.GetVersions(game); switch (mode) { case PatchStateMode.Default: @@ -969,6 +983,7 @@ async Task runAnalysis(PatchStateMode mode) AdvancedWithoutLocalizationModeVisible = false; DefaultWithoutLocalizationModeVisible = false; } + var height = (VerticalMenuSpacing + VerticalMenuItemHeight) * 2; AdvancedParentVisible = AdvancedModeVisible || AdvancedWithoutLocalizationModeVisible; DefaultParentVisible = DefaultModeVisible || DefaultWithoutLocalizationModeVisible; @@ -976,10 +991,12 @@ async Task runAnalysis(PatchStateMode mode) { height += VerticalMenuSpacing + VerticalMenuItemHeight; } + if (DefaultParentVisible) { height += VerticalMenuSpacing + VerticalMenuItemHeight; } + VerticalMenuHeight = height; await TriggerOverlayAsync(id, false); await Task.Delay(50); @@ -987,13 +1004,12 @@ async Task runAnalysis(PatchStateMode mode) } }, allowModSelectionEnabled).DisposeWith(disposables); - async Task ensureSteamIsRunning(IGameSettings args) + async Task ensureSteamIsRunning(IGameSettings args) { if (gameService.IsSteamGame(args)) { - return await externalProcessHandlerService.LaunchSteamAsync(gameService.GetSelected()); + await externalProcessHandlerService.LaunchSteamAsync(gameService.GetSelected()); } - return true; } async Task launchGame(bool continueGame) @@ -1008,6 +1024,7 @@ async Task launchGame(bool continueGame) notificationAction.ShowNotification(title, message, NotificationType.Error, 30); return; } + var args = gameService.GetLaunchSettings(game, continueGame); if (!string.IsNullOrWhiteSpace(args.ExecutableLocation)) { @@ -1018,6 +1035,7 @@ async Task launchGame(bool continueGame) await modService.DeleteDescriptorsAsync(InstalledMods.Mods); await modService.InstallModsAsync(InstalledMods.Mods); } + await ApplyCollectionAsync(id, false); await MessageBus.PublishAsync(new LaunchingGameEvent(game.Type)); if (gameService.IsSteamLaunchPath(args)) @@ -1076,40 +1094,19 @@ async Task launchGame(bool continueGame) await launchGame(true); }, allowModSelectionEnabled).DisposeWith(disposables); - AdvancedModeCommand = ReactiveCommand.CreateFromTask(() => - { - return runAnalysis(PatchStateMode.Advanced); - }).DisposeWith(disposables); + AdvancedModeCommand = ReactiveCommand.CreateFromTask(() => runAnalysis(PatchStateMode.Advanced)).DisposeWith(disposables); - DefaultModeCommand = ReactiveCommand.CreateFromTask(() => - { - return runAnalysis(PatchStateMode.Default); - }).DisposeWith(disposables); + DefaultModeCommand = ReactiveCommand.CreateFromTask(() => runAnalysis(PatchStateMode.Default)).DisposeWith(disposables); - AnalyzeModeCommand = ReactiveCommand.CreateFromTask(() => - { - return runAnalysis(PatchStateMode.ReadOnly); - }).DisposeWith(disposables); + AnalyzeModeCommand = ReactiveCommand.CreateFromTask(() => runAnalysis(PatchStateMode.ReadOnly)).DisposeWith(disposables); - AnalyzeModeWithoutLocalizationCommand = ReactiveCommand.CreateFromTask(() => - { - return runAnalysis(PatchStateMode.ReadOnlyWithoutLocalization); - }).DisposeWith(disposables); + AnalyzeModeWithoutLocalizationCommand = ReactiveCommand.CreateFromTask(() => runAnalysis(PatchStateMode.ReadOnlyWithoutLocalization)).DisposeWith(disposables); - DefaultWithoutLocalizationModeCommand = ReactiveCommand.CreateFromTask(() => - { - return runAnalysis(PatchStateMode.DefaultWithoutLocalization); - }).DisposeWith(disposables); + DefaultWithoutLocalizationModeCommand = ReactiveCommand.CreateFromTask(() => runAnalysis(PatchStateMode.DefaultWithoutLocalization)).DisposeWith(disposables); - AdvancedWithoutLocalizationModeCommand = ReactiveCommand.CreateFromTask(() => - { - return runAnalysis(PatchStateMode.AdvancedWithoutLocalization); - }).DisposeWith(disposables); + AdvancedWithoutLocalizationModeCommand = ReactiveCommand.CreateFromTask(() => runAnalysis(PatchStateMode.AdvancedWithoutLocalization)).DisposeWith(disposables); - CloseModeCommand = ReactiveCommand.Create(() => - { - ForceClosePopups(); - }).DisposeWith(disposables); + CloseModeCommand = ReactiveCommand.Create(ForceClosePopups).DisposeWith(disposables); var previousCollectionNotification = string.Empty; CollectionMods.ConflictSolverStateChanged += (collectionName, state) => @@ -1129,6 +1126,7 @@ async Task launchGame(bool continueGame) { CollectionMods.Reset(true); } + await InstalledMods.RefreshModsAsync(); EvalResumeAvailability(s.Game); }).DisposeWith(disposables); @@ -1149,7 +1147,7 @@ protected override void OnSelectedGameChanged(IGame game) { forceEnableResumeButton = false; EvalResumeAvailability(game); - ShowAdvancedFeatures = (game?.AdvancedFeatures) != GameAdvancedFeatures.None; + ShowAdvancedFeatures = game?.AdvancedFeatures != GameAdvancedFeatures.None; base.OnSelectedGameChanged(game); } @@ -1183,12 +1181,8 @@ private void SubscribeToProgressReport(long id, CompositeDisposable disposables, definitionLoadHandler = modDefinitionLoadHandler.Subscribe(s => { var message = localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Loading_Definitions); - var overlayProgress = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Progress), new - { - PercentDone = s.Percentage.ToLocalizedPercentage(), - Count = 1, - TotalCount = totalSteps - }); + var overlayProgress = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Progress), + new { PercentDone = s.Percentage.ToLocalizedPercentage(), Count = 1, TotalCount = totalSteps }); TriggerOverlay(id, true, message, overlayProgress); }).DisposeWith(disposables); @@ -1196,12 +1190,8 @@ private void SubscribeToProgressReport(long id, CompositeDisposable disposables, modInvalidReplaceHandler = modDefinitionInvalidReplaceHandler.Subscribe(s => { var message = localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Replacing_Definitions); - var overlayProgress = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Progress), new - { - PercentDone = s.Percentage.ToLocalizedPercentage(), - Count = 2, - TotalCount = totalSteps - }); + var overlayProgress = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Progress), + new { PercentDone = s.Percentage.ToLocalizedPercentage(), Count = 2, TotalCount = totalSteps }); TriggerOverlay(id, true, message, overlayProgress); }).DisposeWith(disposables); @@ -1209,12 +1199,8 @@ private void SubscribeToProgressReport(long id, CompositeDisposable disposables, gameIndexHandler = gameIndexProgressHandler.Subscribe(s => { var message = localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Indexing_Game); - var overlayProgress = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Progress), new - { - PercentDone = s.Percentage.ToLocalizedPercentage(), - Count = 3, - TotalCount = totalSteps - }); + var overlayProgress = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Progress), + new { PercentDone = s.Percentage.ToLocalizedPercentage(), Count = 3, TotalCount = totalSteps }); TriggerOverlay(id, true, message, overlayProgress); }).DisposeWith(disposables); @@ -1222,12 +1208,8 @@ private void SubscribeToProgressReport(long id, CompositeDisposable disposables, gameDefinitionLoadHandler = gameDefinitionLoadProgressHandler.Subscribe(s => { var message = localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Loading_Game_Definitions); - var overlayProgress = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Progress), new - { - PercentDone = s.Percentage.ToLocalizedPercentage(), - Count = 4, - TotalCount = totalSteps - }); + var overlayProgress = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Progress), + new { PercentDone = s.Percentage.ToLocalizedPercentage(), Count = 4, TotalCount = totalSteps }); TriggerOverlay(id, true, message, overlayProgress); }).DisposeWith(disposables); @@ -1235,12 +1217,8 @@ private void SubscribeToProgressReport(long id, CompositeDisposable disposables, definitionAnalyzeLoadHandler = modDefinitionAnalyzeHandler.Subscribe(s => { var message = localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Analyzing_Conflicts); - var overlayProgress = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Progress), new - { - PercentDone = s.Percentage.ToLocalizedPercentage(), - Count = totalSteps == 6 ? 5 : 3, - TotalCount = totalSteps - }); + var overlayProgress = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Progress), + new { PercentDone = s.Percentage.ToLocalizedPercentage(), Count = totalSteps == 6 ? 5 : 3, TotalCount = totalSteps }); TriggerOverlay(id, true, message, overlayProgress); }).DisposeWith(disposables); @@ -1248,12 +1226,8 @@ private void SubscribeToProgressReport(long id, CompositeDisposable disposables, definitionSyncHandler = modDefinitionPatchLoadHandler.Subscribe(s => { var message = localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Analyzing_Resolved_Conflicts); - var overlayProgress = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Progress), new - { - PercentDone = s.Percentage.ToLocalizedPercentage(), - Count = totalSteps == 6 ? 6 : 4, - TotalCount = totalSteps - }); + var overlayProgress = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Mod_Actions.ConflictSolver.Overlay_Conflict_Solver_Progress), + new { PercentDone = s.Percentage.ToLocalizedPercentage(), Count = totalSteps == 6 ? 6 : 4, TotalCount = totalSteps }); TriggerOverlay(id, true, message, overlayProgress); }).DisposeWith(disposables); } diff --git a/src/IronyModManager/ViewModels/Controls/OptionsControlViewModel.cs b/src/IronyModManager/ViewModels/Controls/OptionsControlViewModel.cs index f2c3a9953..5d68aaec3 100644 --- a/src/IronyModManager/ViewModels/Controls/OptionsControlViewModel.cs +++ b/src/IronyModManager/ViewModels/Controls/OptionsControlViewModel.cs @@ -4,13 +4,14 @@ // Created : 05-30-2020 // // Last Modified By : Mario -// Last Modified On : 11-08-2022 +// Last Modified On : 02-25-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; using System.IO; @@ -20,6 +21,8 @@ using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia; +using Avalonia.Collections; +using DynamicData; using IronyModManager.Common; using IronyModManager.Common.Events; using IronyModManager.Common.ViewModels; @@ -38,74 +41,92 @@ namespace IronyModManager.ViewModels.Controls { /// - /// Class OptionsControlViewModel. - /// Implements the + /// The options control view model. /// /// [ExcludeFromCoverage("This should be tested via functional testing.")] - public class OptionsControlViewModel : BaseViewModel + public class OptionsControlViewModel( + IGameLanguageService gameLanguageService, + IAppAction appAction, + IPlatformConfiguration platformConfiguration, + IModService modService, + INotificationPositionSettingsService positionSettingsService, + IExternalEditorService externalEditorService, + IIDGenerator idGenerator, + ILogger logger, + INotificationAction notificationAction, + ILocalizationManager localizationManager, + IUpdater updater, + IUpdaterService updaterService, + IGameService gameService, + IFileDialogAction fileDialogAction) : BaseViewModel { #region Fields /// /// The application action /// - private readonly IAppAction appAction; + private readonly IAppAction appAction = appAction; /// /// The external editor service /// - private readonly IExternalEditorService externalEditorService; + private readonly IExternalEditorService externalEditorService = externalEditorService; /// /// The file dialog action /// - private readonly IFileDialogAction fileDialogAction; + private readonly IFileDialogAction fileDialogAction = fileDialogAction; + + /// + /// A private readonly IGameLanguageService named gameLanguageService. + /// + private readonly IGameLanguageService gameLanguageService = gameLanguageService; /// /// The game service /// - private readonly IGameService gameService; + private readonly IGameService gameService = gameService; /// /// The identifier generator /// - private readonly IIDGenerator idGenerator; + private readonly IIDGenerator idGenerator = idGenerator; /// /// The localization manager /// - private readonly ILocalizationManager localizationManager; + private readonly ILocalizationManager localizationManager = localizationManager; /// /// The logger /// - private readonly ILogger logger; + private readonly ILogger logger = logger; /// /// The mod service /// - private readonly IModService modService; + private readonly IModService modService = modService; /// /// The notification action /// - private readonly INotificationAction notificationAction; + private readonly INotificationAction notificationAction = notificationAction; /// /// The position settings service /// - private readonly INotificationPositionSettingsService positionSettingsService; + private readonly INotificationPositionSettingsService positionSettingsService = positionSettingsService; /// /// The updater /// - private readonly IUpdater updater; + private readonly IUpdater updater = updater; /// /// The updater service /// - private readonly IUpdaterService updaterService; + private readonly IUpdaterService updaterService = updaterService; /// /// The automatic update changed @@ -132,25 +153,30 @@ public class OptionsControlViewModel : BaseViewModel /// private IDisposable gameArgsChanged; + /// + /// A private IDisposable named gameLanguagesChanged. + /// + private IDisposable gameLanguagesChanged; + /// /// The is editor reloading /// - private bool isEditorReloading = false; + private bool isEditorReloading; /// /// The is game reloading /// - private bool isGameReloading = false; + private bool isGameReloading; /// /// The is notification position reloading /// - private bool isNotificationPositionReloading = false; + private bool isNotificationPositionReloading; /// /// The is update reloading /// - private bool isUpdateReloading = false; + private bool isUpdateReloading; /// /// The last skipped version changed @@ -174,50 +200,15 @@ public class OptionsControlViewModel : BaseViewModel #endregion Fields - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The application action. - /// The platform configuration. - /// The mod service. - /// The position settings service. - /// The external editor service. - /// The identifier generator. - /// The logger. - /// The notification action. - /// The localization manager. - /// The updater. - /// The updater service. - /// The game service. - /// The file dialog action. - public OptionsControlViewModel(IAppAction appAction, IPlatformConfiguration platformConfiguration, IModService modService, INotificationPositionSettingsService positionSettingsService, - IExternalEditorService externalEditorService, IIDGenerator idGenerator, ILogger logger, - INotificationAction notificationAction, ILocalizationManager localizationManager, IUpdater updater, - IUpdaterService updaterService, IGameService gameService, IFileDialogAction fileDialogAction) - { - this.positionSettingsService = positionSettingsService; - this.gameService = gameService; - this.fileDialogAction = fileDialogAction; - this.updaterService = updaterService; - this.updater = updater; - this.localizationManager = localizationManager; - this.notificationAction = notificationAction; - this.logger = logger; - this.idGenerator = idGenerator; - this.externalEditorService = externalEditorService; - this.modService = modService; - this.appAction = appAction; - UpdatesAllowed = !platformConfiguration.GetOptions().Updates.Disable; - LeftMargin = new Thickness(20, 0, 0, 0); - LeftChildMargin = new Thickness(20, 10, 0, 0); - } - - #endregion Constructors - #region Properties + /// + /// Gets or sets a value representing the allowed languages caption. + /// + /// The allowed languages caption. + [StaticLocalization(LocalizationResources.Options.ConflictSolver.AllowedLanguages)] + public virtual string AllowedLanguagesCaption { get; protected set; } + /// /// Gets or sets the application options title. /// @@ -297,6 +288,13 @@ public OptionsControlViewModel(IAppAction appAction, IPlatformConfiguration plat /// The close command. public virtual ReactiveCommand CloseCommand { get; protected set; } + /// + /// Gets or sets a value representing the conflict solver title. + /// + /// The conflict solver title. + [StaticLocalization(LocalizationResources.Options.ConflictSolver.Title)] + public virtual string ConflictSolverTitle { get; protected set; } + /// /// Gets or sets the custom mod path. /// @@ -358,6 +356,19 @@ public OptionsControlViewModel(IAppAction appAction, IPlatformConfiguration plat [StaticLocalization(LocalizationResources.Options.Game.GameExecutable)] public virtual string GameExecutable { get; protected set; } + /// + /// Gets or sets a value representing the game languages. + /// + /// The game languages. + [AutoRefreshLocalization] + public virtual AvaloniaList GameLanguages { get; protected set; } + + /// + /// Gets or sets a value indicating whether the game languages visible. + /// + /// true if game languages visible; otherwise, false. + public virtual bool GameLanguagesVisible { get; protected set; } + /// /// Gets or sets the game options. /// @@ -365,6 +376,12 @@ public OptionsControlViewModel(IAppAction appAction, IPlatformConfiguration plat [StaticLocalization(LocalizationResources.Options.Game.Title)] public virtual string GameOptions { get; protected set; } + /// + /// Gets or sets a value indicating whether [installing updates allowed]. + /// + /// true if [installing updates allowed]; otherwise, false. + public virtual bool InstallingUpdatesAllowed { get; protected set; } = !platformConfiguration.GetOptions().Updates.DisableInstallOnly; + /// /// Gets or sets the install updates. /// @@ -385,16 +402,16 @@ public OptionsControlViewModel(IAppAction appAction, IPlatformConfiguration plat public virtual bool IsOpen { get; protected set; } /// - /// Gets or sets the left child margin. + /// Gets or sets a value representing the left game languages margin. /// - /// The left child margin. - public virtual Thickness LeftChildMargin { get; protected set; } + /// The left game languages margin. + public virtual Thickness LeftGameLanguagesMargin { get; protected set; } = new(20, 0, 0, 0); /// /// Gets or sets the left margin. /// /// The left margin. - public virtual Thickness LeftMargin { get; protected set; } + public virtual Thickness LeftMargin { get; protected set; } = new(20, 15, 0, 15); /// /// Gets or sets the navigate. @@ -600,7 +617,7 @@ public OptionsControlViewModel(IAppAction appAction, IPlatformConfiguration plat /// Gets or sets a value indicating whether [updates allowed]. /// /// true if [updates allowed]; otherwise, false. - public virtual bool UpdatesAllowed { get; protected set; } + public virtual bool UpdatesAllowed { get; protected set; } = !platformConfiguration.GetOptions().Updates.Disable; /// /// Gets or sets the update settings. @@ -640,6 +657,27 @@ public virtual void ForceClose() IsOpen = false; } + /// + /// Binds game languages. + /// + /// The game. + protected virtual void BindGameLanguages(IGame game = null) + { + game ??= gameService.GetSelected(); + GameLanguages = gameLanguageService.Get().ToAvaloniaList(); + var languages = GameLanguages; + GameLanguagesVisible = game != null && game.AdvancedFeatures != GameAdvancedFeatures.None; + LeftGameLanguagesMargin = new Thickness(GameLanguagesVisible ? 20 : 0, 15, 0, 15); + gameLanguagesChanged?.Dispose(); + gameLanguagesChanged = null; + + var sourceList = languages.ToSourceList(); + gameLanguagesChanged = sourceList.Connect().WhenPropertyChanged(p => p.IsSelected, false).Subscribe(_ => + { + gameLanguageService.Save(languages); + }).DisposeWith(Disposables); + } + /// /// check for updates as an asynchronous operation. /// @@ -666,7 +704,7 @@ protected virtual async Task CheckForUpdatesAsync(bool autoUpdateCheck = false) { var title = localizationManager.GetResource(LocalizationResources.Options.Updates.UpdateNotification.Title); var message = localizationManager.GetResource(LocalizationResources.Options.Updates.UpdateNotification.Message); - notificationAction.ShowNotification(title, message, NotificationType.Info, 30, onClick: () => { IsOpen = true; }); + notificationAction.ShowNotification(title, message, NotificationType.Info, 30, () => { IsOpen = true; }); } } else @@ -678,6 +716,7 @@ protected virtual async Task CheckForUpdatesAsync(bool autoUpdateCheck = false) notificationAction.ShowNotification(title, message, NotificationType.Info); } } + CheckingForUpdates = false; } @@ -687,6 +726,7 @@ protected virtual async Task CheckForUpdatesAsync(bool autoUpdateCheck = false) /// The disposables. protected override void OnActivated(CompositeDisposable disposables) { + BindGameLanguages(); SetGame(gameService.GetSelected()); SetEditor(externalEditorService.Get()); SetNotificationPosition(positionSettingsService.Get()); @@ -711,6 +751,7 @@ async Task showPrompt() SaveUpdateSettings(); } } + showPrompt().ConfigureAwait(false); } else if (updateSettings.AutoUpdates.GetValueOrDefault()) @@ -718,6 +759,7 @@ async Task showPrompt() CheckForUpdatesAsync(true).ConfigureAwait(false); } } + SetUpdateSettings(updateSettings); var updateCheckAllowed = this.WhenAnyValue(p => p.CheckingForUpdates, v => !v); @@ -747,11 +789,13 @@ async Task showPrompt() { Game.LaunchArguments = defaultSettings.LaunchArguments; } + if (string.IsNullOrWhiteSpace(Game.UserDirectory)) { Game.UserDirectory = defaultSettings.UserDirectory; } } + SaveGame(); } }).DisposeWith(disposables); @@ -784,6 +828,7 @@ async Task showPrompt() { result = result.TrimEnd(Path.DirectorySeparatorChar + Shared.Constants.JsonModDirectory); } + Game.UserDirectory = result; SaveGame(); } @@ -797,10 +842,12 @@ async Task showPrompt() { Game.LaunchArguments = defaultSettings.LaunchArguments; } + if (string.IsNullOrWhiteSpace(Game.UserDirectory)) { Game.UserDirectory = defaultSettings.UserDirectory; } + SaveGame(); }).DisposeWith(disposables); @@ -881,6 +928,7 @@ async Task showPrompt() { updater.SetSkippedVersion(version); } + UpdateSettings.LastSkippedVersion = version; }); @@ -895,6 +943,7 @@ async Task showPrompt() var message = localizationManager.GetResource(LocalizationResources.Options.Prompts.CustomModDirectory.Message); save = await notificationAction.ShowPromptAsync(title, title, message, NotificationType.Warning); } + IsOpen = true; if (save) { @@ -912,6 +961,7 @@ async Task showPrompt() var message = localizationManager.GetResource(LocalizationResources.Options.Prompts.CustomModDirectory.Message); save = await notificationAction.ShowPromptAsync(title, title, message, NotificationType.Warning); } + if (save) { var defaultSettings = gameService.GetDefaultGameSettings(Game); @@ -952,6 +1002,7 @@ async Task showPrompt() { return; } + ITempFile createTempFile(string text) { var file = DIResolver.Get(); @@ -959,6 +1010,7 @@ ITempFile createTempFile(string text) file.Text = text; return file; } + var left = createTempFile(localizationManager.GetResource(LocalizationResources.Options.Editor.TestLeft)); var right = createTempFile(localizationManager.GetResource(LocalizationResources.Options.Editor.TestRight)); var arguments = externalEditorService.GetLaunchArguments(left.File, right.File); @@ -966,6 +1018,7 @@ ITempFile createTempFile(string text) { await notificationAction.ShowPromptAsync(TestExternalEditorConfiguration, TestExternalEditorConfiguration, TestExternalEditorConfiguration, NotificationType.Info, PromptType.OK); } + left.Dispose(); right.Dispose(); }).DisposeWith(disposables); @@ -979,6 +1032,7 @@ ITempFile createTempFile(string text) /// The game. protected override void OnSelectedGameChanged(IGame game) { + BindGameLanguages(game); SetGame(game); base.OnSelectedGameChanged(game); } @@ -1001,14 +1055,14 @@ protected virtual void SaveEditor() protected virtual void SaveGame() { var game = gameService.GetSelected(); - bool exeChanged = game.ExecutableLocation != Game.ExecutableLocation; + var exeChanged = game.ExecutableLocation != Game.ExecutableLocation; game.ExecutableLocation = Game.ExecutableLocation; game.LaunchArguments = Game.LaunchArguments; game.RefreshDescriptors = Game.RefreshDescriptors; game.CloseAppAfterGameLaunch = Game.CloseAppAfterGameLaunch; - bool dirChanged = game.UserDirectory != Game.UserDirectory; + var dirChanged = game.UserDirectory != Game.UserDirectory; game.UserDirectory = Game.UserDirectory; - bool customDirectoryChanged = game.CustomModDirectory != Game.CustomModDirectory; + var customDirectoryChanged = game.CustomModDirectory != Game.CustomModDirectory; game.CustomModDirectory = Game.CustomModDirectory; if (gameService.Save(game)) { @@ -1016,11 +1070,13 @@ protected virtual void SaveGame() { MessageBus.PublishAsync(new GameUserDirectoryChangedEvent(game, customDirectoryChanged)); } + if (exeChanged) { MessageBus.PublishAsync(new GameExeChangedEvent(game.ExecutableLocation)); } } + SetGame(game); } @@ -1055,7 +1111,7 @@ protected virtual void SetEditor(IExternalEditor externalEditor) isEditorReloading = true; Editor = externalEditor; editorArgsChanged?.Dispose(); - editorArgsChanged = this.WhenAnyValue(p => p.Editor.ExternalEditorParameters).Where(p => !isEditorReloading).Subscribe(s => + editorArgsChanged = this.WhenAnyValue(p => p.Editor.ExternalEditorParameters).Where(_ => !isEditorReloading).Subscribe(_ => { SaveEditor(); }).DisposeWith(Disposables); @@ -1073,21 +1129,20 @@ protected virtual void SetGame(IGame game) refreshDescriptorsChanged?.Dispose(); closeGameChanged?.Dispose(); Game = game; - gameArgsChanged = this.WhenAnyValue(p => p.Game.LaunchArguments).Where(p => !isGameReloading).Subscribe(s => + gameArgsChanged = this.WhenAnyValue(p => p.Game.LaunchArguments).Where(_ => !isGameReloading).Subscribe(_ => { SaveGame(); }).DisposeWith(Disposables); - refreshDescriptorsChanged = this.WhenAnyValue(p => p.Game.RefreshDescriptors).Where(p => !isGameReloading).Subscribe(s => + refreshDescriptorsChanged = this.WhenAnyValue(p => p.Game.RefreshDescriptors).Where(_ => !isGameReloading).Subscribe(_ => { SaveGame(); }).DisposeWith(Disposables); - closeGameChanged = this.WhenAnyValue(p => p.Game.CloseAppAfterGameLaunch).Where(p => !isGameReloading).Subscribe(s => + closeGameChanged = this.WhenAnyValue(p => p.Game.CloseAppAfterGameLaunch).Where(_ => !isGameReloading).Subscribe(_ => { SaveGame(); }).DisposeWith(Disposables); ShowGameOptions = game != null; LeftMargin = new Thickness(ShowGameOptions ? 20 : 0, 0, 0, 0); - LeftChildMargin = new Thickness(ShowGameOptions ? 20 : 0, 10, 0, 0); isGameReloading = false; } @@ -1103,19 +1158,22 @@ protected virtual void SetNotificationPosition(IEnumerable p.NotificationPosition).Where(p => !isNotificationPositionReloading).Subscribe(s => + notificationPositionChanged = this.WhenAnyValue(p => p.NotificationPosition).Where(_ => !isNotificationPositionReloading).Subscribe(s => { foreach (var item in NotificationPositions) { item.IsSelected = item == s; } + SaveNotificationOption(); }).DisposeWith(Disposables); if (!resubscribeOnly && notificationPositions != null) { NotificationPosition = NotificationPositions.FirstOrDefault(p => p.IsSelected); } + isNotificationPositionReloading = false; } @@ -1130,16 +1188,16 @@ protected virtual void SetUpdateSettings(IUpdateSettings updateSettings) checkForPrereleaseChanged?.Dispose(); lastSkippedVersionChanged?.Dispose(); UpdateSettings = updateSettings; - autoUpdateChanged = this.WhenAnyValue(p => p.UpdateSettings.AutoUpdates).Where(v => !isUpdateReloading).Subscribe(s => + autoUpdateChanged = this.WhenAnyValue(p => p.UpdateSettings.AutoUpdates).Where(_ => !isUpdateReloading).Subscribe(_ => { SaveUpdateSettings(); }).DisposeWith(Disposables); - checkForPrereleaseChanged = this.WhenAnyValue(p => p.UpdateSettings.CheckForPrerelease).Where(v => !isUpdateReloading).Subscribe(s => + checkForPrereleaseChanged = this.WhenAnyValue(p => p.UpdateSettings.CheckForPrerelease).Where(_ => !isUpdateReloading).Subscribe(_ => { UpdateInfoVisible = false; SaveUpdateSettings(); }).DisposeWith(Disposables); - lastSkippedVersionChanged = this.WhenAnyValue(p => p.UpdateSettings.LastSkippedVersion).Where(v => !isUpdateReloading).Subscribe(s => + lastSkippedVersionChanged = this.WhenAnyValue(p => p.UpdateSettings.LastSkippedVersion).Where(_ => !isUpdateReloading).Subscribe(_ => { UpdateInfoVisible = false; SaveUpdateSettings(); diff --git a/src/IronyModManager/ViewModels/MainConflictSolverViewModel.cs b/src/IronyModManager/ViewModels/MainConflictSolverViewModel.cs index 6566eca40..931554d71 100644 --- a/src/IronyModManager/ViewModels/MainConflictSolverViewModel.cs +++ b/src/IronyModManager/ViewModels/MainConflictSolverViewModel.cs @@ -1,17 +1,17 @@ - -// *********************************************************************** +// *********************************************************************** // Assembly : IronyModManager // Author : Mario // Created : 03-18-2020 // // Last Modified By : Mario -// Last Modified On : 06-26-2023 +// Last Modified On : 02-20-2024 // *********************************************************************** // // Mario // // // *********************************************************************** + using System; using System.Collections.Generic; using System.IO; @@ -42,14 +42,46 @@ namespace IronyModManager.ViewModels { - /// /// Class MainConflictSolverControlViewModel. /// Implements the /// /// + /// The external process handler service. + /// The hotkey pressed handler. + /// The identifier generator. + /// The mod patch collection service. + /// The localization manager. + /// The merge viewer. + /// The binary merge viewer. + /// The mod compare selector. + /// The ignore conflicts rules. + /// The mod filter. + /// The reset conflicts. + /// The database search. + /// The custom conflicts. + /// The logger. + /// The notification action. + /// The application action. + /// Initializes a new instance of the class. [ExcludeFromCoverage("This should be tested via functional testing.")] - public class MainConflictSolverControlViewModel : BaseViewModel + public class MainConflictSolverControlViewModel( + IExternalProcessHandlerService externalProcessHandlerService, + ConflictSolverViewHotkeyPressedHandler hotkeyPressedHandler, + IIDGenerator idGenerator, + IModPatchCollectionService modPatchCollectionService, + ILocalizationManager localizationManager, + MergeViewerControlViewModel mergeViewer, + MergeViewerBinaryControlViewModel binaryMergeViewer, + ModCompareSelectorControlViewModel modCompareSelector, + ModConflictIgnoreControlViewModel ignoreConflictsRules, + ConflictSolverModFilterControlViewModel modFilter, + ConflictSolverResetConflictsControlViewModel resetConflicts, + ConflictSolverDBSearchControlViewModel dbSearch, + ConflictSolverCustomConflictsControlViewModel customConflicts, + ILogger logger, + INotificationAction notificationAction, + IAppAction appAction) : BaseViewModel { #region Fields @@ -61,47 +93,47 @@ public class MainConflictSolverControlViewModel : BaseViewModel /// /// The localization directory /// - private static readonly string LocalizationDirectory = $"{Shared.Constants.LocalizationDirectory}{Path.DirectorySeparatorChar}"; + private static readonly string localizationDirectory = $"{Shared.Constants.LocalizationDirectory}{Path.DirectorySeparatorChar}"; /// /// The application action /// - private readonly IAppAction appAction; + private readonly IAppAction appAction = appAction; /// /// The external process handler service /// - private readonly IExternalProcessHandlerService externalProcessHandlerService; + private readonly IExternalProcessHandlerService externalProcessHandlerService = externalProcessHandlerService; /// /// The hotkey pressed handler /// - private readonly ConflictSolverViewHotkeyPressedHandler hotkeyPressedHandler; + private readonly ConflictSolverViewHotkeyPressedHandler hotkeyPressedHandler = hotkeyPressedHandler; /// /// The identifier generator /// - private readonly IIDGenerator idGenerator; + private readonly IIDGenerator idGenerator = idGenerator; /// /// The localization manager /// - private readonly ILocalizationManager localizationManager; + private readonly ILocalizationManager localizationManager = localizationManager; /// /// The logger /// - private readonly ILogger logger; + private readonly ILogger logger = logger; /// /// The mod patch collection service /// - private readonly IModPatchCollectionService modPatchCollectionService; + private readonly IModPatchCollectionService modPatchCollectionService = modPatchCollectionService; /// /// The notification action /// - private readonly INotificationAction notificationAction; + private readonly INotificationAction notificationAction = notificationAction; /// /// The cached invalids @@ -111,7 +143,7 @@ public class MainConflictSolverControlViewModel : BaseViewModel /// /// The filtering conflicts /// - private bool filteringConflicts = false; + private bool filteringConflicts; /// /// The invalids checked @@ -121,60 +153,10 @@ public class MainConflictSolverControlViewModel : BaseViewModel /// /// The take left binary /// - private bool takeLeftBinary = false; + private bool takeLeftBinary; #endregion Fields - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The external process handler service. - /// The hotkey pressed handler. - /// The identifier generator. - /// The mod patch collection service. - /// The localization manager. - /// The merge viewer. - /// The binary merge viewer. - /// The mod compare selector. - /// The ignore conflicts rules. - /// The mod filter. - /// The reset conflicts. - /// The database search. - /// The custom conflicts. - /// The logger. - /// The notification action. - /// The application action. - public MainConflictSolverControlViewModel(IExternalProcessHandlerService externalProcessHandlerService, - ConflictSolverViewHotkeyPressedHandler hotkeyPressedHandler, IIDGenerator idGenerator, - IModPatchCollectionService modPatchCollectionService, ILocalizationManager localizationManager, - MergeViewerControlViewModel mergeViewer, MergeViewerBinaryControlViewModel binaryMergeViewer, - ModCompareSelectorControlViewModel modCompareSelector, ModConflictIgnoreControlViewModel ignoreConflictsRules, - ConflictSolverModFilterControlViewModel modFilter, ConflictSolverResetConflictsControlViewModel resetConflicts, - ConflictSolverDBSearchControlViewModel dbSearch, ConflictSolverCustomConflictsControlViewModel customConflicts, - ILogger logger, INotificationAction notificationAction, IAppAction appAction) - { - this.idGenerator = idGenerator; - this.modPatchCollectionService = modPatchCollectionService; - this.localizationManager = localizationManager; - this.logger = logger; - this.notificationAction = notificationAction; - this.appAction = appAction; - this.hotkeyPressedHandler = hotkeyPressedHandler; - this.externalProcessHandlerService = externalProcessHandlerService; - MergeViewer = mergeViewer; - ModCompareSelector = modCompareSelector; - BinaryMergeViewer = binaryMergeViewer; - IgnoreConflictsRules = ignoreConflictsRules; - ModFilter = modFilter; - ResetConflicts = resetConflicts; - DatabaseSearch = dbSearch; - CustomConflicts = customConflicts; - } - - #endregion Constructors - #region Properties /// @@ -195,11 +177,12 @@ public MainConflictSolverControlViewModel(IExternalProcessHandlerService externa /// /// The back command. public virtual ReactiveCommand BackCommand { get; set; } + /// /// Gets or sets the binary merge viewer. /// /// The binary merge viewer. - public virtual MergeViewerBinaryControlViewModel BinaryMergeViewer { get; protected set; } + public virtual MergeViewerBinaryControlViewModel BinaryMergeViewer { get; protected set; } = binaryMergeViewer; /// /// Gets or sets the conflicted objects. @@ -224,13 +207,13 @@ public MainConflictSolverControlViewModel(IExternalProcessHandlerService externa /// Gets or sets the custom conflicts. /// /// The custom conflicts. - public virtual ConflictSolverCustomConflictsControlViewModel CustomConflicts { get; protected set; } + public virtual ConflictSolverCustomConflictsControlViewModel CustomConflicts { get; protected set; } = customConflicts; /// /// Gets or sets the database search. /// /// The database search. - public virtual ConflictSolverDBSearchControlViewModel DatabaseSearch { get; protected set; } + public virtual ConflictSolverDBSearchControlViewModel DatabaseSearch { get; protected set; } = dbSearch; /// /// Gets or sets a value indicating whether [editing ignore conflicts rules]. @@ -239,15 +222,15 @@ public MainConflictSolverControlViewModel(IExternalProcessHandlerService externa public virtual bool EditingIgnoreConflictsRules { get; protected set; } /// - /// Gets or sets the hierarchal conflicts. + /// Gets or sets the hierarchical conflicts. /// - /// The hierarchal conflicts. - public virtual AvaloniaList HierarchalConflicts { get; protected set; } + /// The hierarchical conflicts. + public virtual AvaloniaList HierarchicalConflicts { get; protected set; } /// - /// Gets or sets the ignore. + /// Gets or sets ignore. /// - /// The ignore. + /// ignore. [StaticLocalization(LocalizationResources.Conflict_Solver.Ignore)] public virtual string Ignore { get; protected set; } @@ -261,7 +244,7 @@ public MainConflictSolverControlViewModel(IExternalProcessHandlerService externa /// Gets or sets the ignore conflicts rules. /// /// The ignore conflicts rules. - public virtual ModConflictIgnoreControlViewModel IgnoreConflictsRules { get; protected set; } + public virtual ModConflictIgnoreControlViewModel IgnoreConflictsRules { get; protected set; } = ignoreConflictsRules; /// /// Gets or sets a value indicating whether [ignore enabled]. @@ -369,19 +352,19 @@ public MainConflictSolverControlViewModel(IExternalProcessHandlerService externa /// Gets or sets the merge viewer. /// /// The merge viewer. - public virtual MergeViewerControlViewModel MergeViewer { get; protected set; } + public virtual MergeViewerControlViewModel MergeViewer { get; protected set; } = mergeViewer; /// /// Gets or sets the mod compare selector. /// /// The mod compare selector. - public virtual ModCompareSelectorControlViewModel ModCompareSelector { get; protected set; } + public virtual ModCompareSelectorControlViewModel ModCompareSelector { get; protected set; } = modCompareSelector; /// /// Gets or sets the mod filter. /// /// The mod filter. - public virtual ConflictSolverModFilterControlViewModel ModFilter { get; protected set; } + public virtual ConflictSolverModFilterControlViewModel ModFilter { get; protected set; } = modFilter; /// /// Gets or sets the number of conflicts caption. @@ -405,7 +388,7 @@ public MainConflictSolverControlViewModel(IExternalProcessHandlerService externa /// Gets or sets the reset conflicts. /// /// The reset conflicts. - public virtual ConflictSolverResetConflictsControlViewModel ResetConflicts { get; protected set; } + public virtual ConflictSolverResetConflictsControlViewModel ResetConflicts { get; protected set; } = resetConflicts; /// /// Gets or sets the reset conflicts column. @@ -513,13 +496,11 @@ public async Task InitializeAsync(bool readOnly) case ResetType.Ignored: sbIgnored.AppendLine(IronyFormatter.Format(modListFormat, new { ParentDirectory = conflict.Name, child.Name })); break; - - default: - break; } } } - var msgFormat = string.Empty; + + string msgFormat; if (readOnly) { if (sbIgnored.Length > 0 && sbResolved.Length > 0) @@ -550,12 +531,8 @@ public async Task InitializeAsync(bool readOnly) msgFormat = localizationManager.GetResource(LocalizationResources.Conflict_Solver.ResetWarning.RegularModeIgnoredOnly); } } - var msg = IronyFormatter.Format(msgFormat, new - { - Environment.NewLine, - ListOfConflictsResolved = sbResolved.ToString(), - ListOfConflictsIgnored = sbIgnored.ToString() - }); + + var msg = IronyFormatter.Format(msgFormat, new { Environment.NewLine, ListOfConflictsResolved = sbResolved.ToString(), ListOfConflictsIgnored = sbIgnored.ToString() }); var title = localizationManager.GetResource(LocalizationResources.Conflict_Solver.ResetWarning.Title); Dispatcher.UIThread.SafeInvoke(() => notificationAction.ShowPromptAsync(title, title, msg, NotificationType.Warning, PromptType.OK)); } @@ -610,17 +587,22 @@ protected virtual async Task EvaluateDefinitionValidity() patchDefinition.UseSimpleValidation = true; } } + var validationResult = modPatchCollectionService.Validate(patchDefinition); if (!validationResult.IsValid) { var title = localizationManager.GetResource(LocalizationResources.Conflict_Solver.ResolutionSaveError.Title); - var message = localizationManager.GetResource(validationResult.ErrorLine.HasValue ? LocalizationResources.Conflict_Solver.ResolutionSaveError.MessageLine : LocalizationResources.Conflict_Solver.ResolutionSaveError.MessageNoLine); - await Dispatcher.UIThread.SafeInvokeAsync(async () => await notificationAction.ShowPromptAsync(title, title, message.FormatIronySmart(new { Environment.NewLine, validationResult.ErrorMessage, Line = validationResult.ErrorLine, Column = validationResult.ErrorColumn }), NotificationType.Error, PromptType.OK)); + var message = localizationManager.GetResource(validationResult.ErrorLine.HasValue + ? LocalizationResources.Conflict_Solver.ResolutionSaveError.MessageLine + : LocalizationResources.Conflict_Solver.ResolutionSaveError.MessageNoLine); + await Dispatcher.UIThread.SafeInvokeAsync(async () => await notificationAction.ShowPromptAsync(title, title, + message.FormatIronySmart(new { Environment.NewLine, validationResult.ErrorMessage, Line = validationResult.ErrorLine, Column = validationResult.ErrorColumn }), NotificationType.Error, PromptType.OK)); } else { await Dispatcher.UIThread.SafeInvokeAsync(async () => await ResolveConflictAsync(true).ConfigureAwait(true)); } + BackAllowed = true; } @@ -646,6 +628,7 @@ protected virtual async Task FilterHierarchalConflictsAsync(IConflictResult conf { await Task.Delay(25); } + filteringConflicts = true; var index = PreviousConflictIndex; PreviousConflictIndex = null; @@ -658,25 +641,29 @@ protected virtual async Task FilterHierarchalConflictsAsync(IConflictResult conf { resolved.AddRange(conflictResult.ResolvedConflicts.GetHierarchicalDefinitions()); } + if (conflictResult.IgnoredConflicts != null) { resolved.AddRange(conflictResult.IgnoredConflicts.GetHierarchicalDefinitions()); } + if (conflictResult.RuleIgnoredConflicts != null) { resolved.AddRange(conflictResult.RuleIgnoredConflicts.GetHierarchicalDefinitions()); } + foreach (var topLevelResolvedConflicts in resolved) { IEnumerable topLevelConflicts; - if (topLevelResolvedConflicts.Name.StartsWith(LocalizationDirectory, StringComparison.OrdinalIgnoreCase)) + if (topLevelResolvedConflicts.Name.StartsWith(localizationDirectory, StringComparison.OrdinalIgnoreCase)) { - topLevelConflicts = conflicts.Where(p => p.Name.StartsWith(LocalizationDirectory, StringComparison.OrdinalIgnoreCase)); + topLevelConflicts = conflicts.Where(p => p.Name.StartsWith(localizationDirectory, StringComparison.OrdinalIgnoreCase)); } else { topLevelConflicts = conflicts.Where(p => p.Name.Equals(topLevelResolvedConflicts.Name)); } + if (topLevelConflicts.Any()) { foreach (var topLevelConflict in topLevelConflicts) @@ -692,6 +679,7 @@ protected virtual async Task FilterHierarchalConflictsAsync(IConflictResult conf } } } + conflicts.RemoveAll(conflicts.Where(p => p.Children == null || p.Children.Count == 0).ToList()); if (!invalidsChecked) { @@ -707,9 +695,9 @@ protected virtual async Task FilterHierarchalConflictsAsync(IConflictResult conf { var invalidChild = DIResolver.Get(); invalidChild.Name = item.File; - var message = item.ErrorColumn.HasValue || item.ErrorLine.HasValue ? - localizationManager.GetResource(LocalizationResources.Conflict_Solver.InvalidConflicts.Error) : - localizationManager.GetResource(LocalizationResources.Conflict_Solver.InvalidConflicts.ErrorNoLine); + var message = item.ErrorColumn.HasValue || item.ErrorLine.HasValue + ? localizationManager.GetResource(LocalizationResources.Conflict_Solver.InvalidConflicts.Error) + : localizationManager.GetResource(LocalizationResources.Conflict_Solver.InvalidConflicts.ErrorNoLine); invalidChild.Key = IronyFormatter.Format(message, new { item.ModName, @@ -722,28 +710,33 @@ protected virtual async Task FilterHierarchalConflictsAsync(IConflictResult conf invalidChild.AdditionalData = item; children.Add(invalidChild); } + invalidDef.Children = children; cachedInvalids = invalidDef; conflicts.Add(invalidDef); } } + if (cachedInvalids != null && !conflicts.Any(p => p.Key == cachedInvalids.Key)) { conflicts.Add(cachedInvalids); } + var selectedParentConflict = SelectedParentConflict; - HierarchalConflicts = conflicts; - NumberOfConflictsCaption = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Conflict_Solver.ConflictCount), new { Count = conflicts.Where(p => p.Key != InvalidKey).SelectMany(p => p.Children).Count() }); + HierarchicalConflicts = conflicts; + NumberOfConflictsCaption = IronyFormatter.Format(localizationManager.GetResource(LocalizationResources.Conflict_Solver.ConflictCount), + new { Count = conflicts.Where(p => p.Key != InvalidKey).SelectMany(p => p.Children).Count() }); int? previousConflictIndex = null; - if (HierarchalConflicts.Any() && selectedParentConflict == null) + if (HierarchicalConflicts.Count != 0 && selectedParentConflict == null) { - selectedParentConflict = HierarchalConflicts.FirstOrDefault(); + selectedParentConflict = HierarchicalConflicts.FirstOrDefault(); } + if (selectedParentConflict != null) { var conflictName = selectedParentConflict.Name; selectedParentConflict = null; - var newSelected = HierarchalConflicts.FirstOrDefault(p => p.Name.Equals(conflictName)); + var newSelected = HierarchicalConflicts.FirstOrDefault(p => p.Name.Equals(conflictName)); if (newSelected != null) { previousConflictIndex = index; @@ -755,20 +748,24 @@ protected virtual async Task FilterHierarchalConflictsAsync(IConflictResult conf previousConflictIndex = newSelected.Children.ToList().IndexOf(overrideMatch); } } - if (previousConflictIndex.GetValueOrDefault() > (newSelected.Children.Count - 1)) + + if (previousConflictIndex.GetValueOrDefault() > newSelected.Children.Count - 1) { previousConflictIndex = newSelected.Children.Count - 1; } + selectedParentConflict = newSelected; } } + PreviousConflictIndex = previousConflictIndex; SelectedParentConflict = selectedParentConflict; } else { - HierarchalConflicts = null; + HierarchicalConflicts = null; } + filteringConflicts = false; } @@ -779,7 +776,7 @@ protected virtual async Task FilterHierarchalConflictsAsync(IConflictResult conf protected override void OnActivated(CompositeDisposable disposables) { var resolvingEnabled = this.WhenAnyValue(v => v.ResolvingConflict, v => !v); - var backAllowed = this.WhenAnyValue(v => v.BackAllowed, v => v == true); + var backAllowed = this.ObservableWhenAnyValue(v => v.BackAllowed, v => v); BackCommand = ReactiveCommand.CreateFromTask(async () => { @@ -795,10 +792,7 @@ protected override void OnActivated(CompositeDisposable disposables) SelectedModCollection = null; cachedInvalids = null; invalidsChecked = false; - var args = new NavigationEventArgs() - { - State = NavigationState.Main - }; + var args = new NavigationEventArgs { State = NavigationState.Main }; ReactiveUI.MessageBus.Current.SendMessage(args); BackAllowed = true; await TriggerOverlayAsync(id, false); @@ -807,10 +801,7 @@ protected override void OnActivated(CompositeDisposable disposables) GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); }, backAllowed).DisposeWith(disposables); - ResolveCommand = ReactiveCommand.CreateFromTask(() => - { - return EvaluateDefinitionValidity(); - }, resolvingEnabled).DisposeWith(disposables); + ResolveCommand = ReactiveCommand.CreateFromTask(EvaluateDefinitionValidity, resolvingEnabled).DisposeWith(disposables); IgnoreCommand = ReactiveCommand.Create(() => { @@ -844,13 +835,13 @@ protected override void OnActivated(CompositeDisposable disposables) MergeViewer.InitParameters(); if (ModFilter.IsActivated) { - ModFilter.SetConflictResult(Conflicts, SelectedModsOrder.ToList(), SelectedModCollection.Name); + ModFilter.SetConflictResult(Conflicts, [.. SelectedModsOrder], SelectedModCollection.Name); } }).DisposeWith(disposables); this.WhenAnyValue(v => v.SelectedParentConflict).Subscribe(s => { - IsConflictSolverAvailable = !(s?.Key == InvalidKey); + IsConflictSolverAvailable = s?.Key != InvalidKey; EvalViewerVisibility(); }).DisposeWith(disposables); @@ -874,18 +865,19 @@ protected override void OnActivated(CompositeDisposable disposables) } else { - if (HierarchalConflicts == null || !HierarchalConflicts.Any()) + if (HierarchicalConflicts == null || HierarchicalConflicts.Count == 0) { ModCompareSelector.Reset(); BinaryMergeViewer.Reset(false); MergeViewer.SetText(string.Empty, string.Empty, true, lockScroll: true); } + PreviousConflictIndex = null; IgnoreEnabled = false; } }).DisposeWith(disposables); - this.WhenAnyValue(v => v.ModCompareSelector.IsActivated).Where(p => p).Subscribe(s => + this.WhenAnyValue(v => v.ModCompareSelector.IsActivated).Where(p => p).Subscribe(_ => { this.WhenAnyValue(v => v.ModCompareSelector.DefinitionSelection).Subscribe(s => { @@ -899,17 +891,17 @@ protected override void OnActivated(CompositeDisposable disposables) if (!IsBinaryConflict) { BinaryMergeViewer.EnableSelection = ResolveEnabled = s.LeftSelectedDefinition != null && - s.RightSelectedDefinition != null && - s.LeftSelectedDefinition != s.RightSelectedDefinition && - (modPatchCollectionService.IsPatchMod(s.LeftSelectedDefinition.ModName) || modPatchCollectionService.IsPatchMod(s.RightSelectedDefinition.ModName)); + s.RightSelectedDefinition != null && + s.LeftSelectedDefinition != s.RightSelectedDefinition && + (modPatchCollectionService.IsPatchMod(s.LeftSelectedDefinition.ModName) || modPatchCollectionService.IsPatchMod(s.RightSelectedDefinition.ModName)); } else { BinaryMergeViewer.Reset(false); ResolveEnabled = false; BinaryMergeViewer.EnableSelection = s.LeftSelectedDefinition != null && - s.RightSelectedDefinition != null && - s.LeftSelectedDefinition != s.RightSelectedDefinition; + s.RightSelectedDefinition != null && + s.LeftSelectedDefinition != s.RightSelectedDefinition; BinaryMergeViewer.SetLeft(s.LeftSelectedDefinition); BinaryMergeViewer.SetRight(s.RightSelectedDefinition); } @@ -922,16 +914,16 @@ protected override void OnActivated(CompositeDisposable disposables) }).DisposeWith(disposables); }).DisposeWith(disposables); - this.WhenAnyValue(v => v.BinaryMergeViewer.IsActivated).Where(p => p).Subscribe(s => + this.WhenAnyValue(v => v.BinaryMergeViewer.IsActivated).Where(p => p).Subscribe(_ => { - Observable.Merge(BinaryMergeViewer.TakeLeftCommand.Select(s => true), BinaryMergeViewer.TakeRightCommand.Select(s => false)).Subscribe(s => + BinaryMergeViewer.TakeLeftCommand.Select(_ => true).Merge(BinaryMergeViewer.TakeRightCommand.Select(_ => false)).Subscribe(s => { takeLeftBinary = s; ResolveEnabled = true; }).DisposeWith(disposables); }).DisposeWith(disposables); - this.WhenAnyValue(p => p.MergeViewer.LeftSide).Where(p => !string.IsNullOrWhiteSpace(p)).Subscribe(s => + this.WhenAnyValue(p => p.MergeViewer.LeftSide).Where(p => !string.IsNullOrWhiteSpace(p)).Subscribe(_ => { var virtualDefinitions = ModCompareSelector.VirtualDefinitions; if (MergeViewer.LeftSidePatchMod && virtualDefinitions != null) @@ -941,7 +933,7 @@ protected override void OnActivated(CompositeDisposable disposables) } }).DisposeWith(disposables); - this.WhenAnyValue(p => p.MergeViewer.RightSide).Where(p => !string.IsNullOrWhiteSpace(p)).Subscribe(s => + this.WhenAnyValue(p => p.MergeViewer.RightSide).Where(p => !string.IsNullOrWhiteSpace(p)).Subscribe(_ => { var virtualDefinitions = ModCompareSelector.VirtualDefinitions; if (MergeViewer.RightSidePatchMod && virtualDefinitions != null) @@ -951,9 +943,9 @@ protected override void OnActivated(CompositeDisposable disposables) } }).DisposeWith(disposables); - this.WhenAnyValue(v => v.IgnoreConflictsRules.IsActivated).Where(p => p).Subscribe(s => + this.WhenAnyValue(v => v.IgnoreConflictsRules.IsActivated).Where(p => p).Subscribe(_ => { - Observable.Merge(IgnoreConflictsRules.SaveCommand, IgnoreConflictsRules.CancelCommand).Subscribe(result => + IgnoreConflictsRules.SaveCommand.Merge(IgnoreConflictsRules.CancelCommand).Subscribe(result => { switch (result.State) { @@ -965,9 +957,6 @@ protected override void OnActivated(CompositeDisposable disposables) case Implementation.CommandState.NotExecuted: EditingIgnoreConflictsRules = false; break; - - default: - break; } }).DisposeWith(disposables); }).DisposeWith(disposables); @@ -985,16 +974,16 @@ protected override void OnActivated(CompositeDisposable disposables) } }).DisposeWith(disposables); - this.WhenAnyValue(p => p.ModFilter.IsActivated).Where(p => p).Subscribe(s => + this.WhenAnyValue(p => p.ModFilter.IsActivated).Where(p => p).Subscribe(_ => { - ModFilter.SetConflictResult(Conflicts, SelectedModsOrder.ToList(), SelectedModCollection.Name); - this.WhenAnyValue(p => p.ModFilter.HasSavedState).Where(p => p).Subscribe(s => + ModFilter.SetConflictResult(Conflicts, [.. SelectedModsOrder], SelectedModCollection.Name); + this.WhenAnyValue(p => p.ModFilter.HasSavedState).Where(p => p).Subscribe(_ => { FilterHierarchalConflictsAsync(Conflicts, SelectedConflict).ConfigureAwait(false); }).DisposeWith(disposables); }).DisposeWith(disposables); - this.WhenAnyValue(p => p.ResetConflicts.IsActivated).Where(p => p).Subscribe(s => + this.WhenAnyValue(p => p.ResetConflicts.IsActivated).Where(p => p).Subscribe(_ => { ResetConflicts.ResetCommand.Subscribe(s => { @@ -1005,28 +994,19 @@ protected override void OnActivated(CompositeDisposable disposables) }).DisposeWith(disposables); }).DisposeWith(disposables); - this.WhenAnyValue(p => p.CustomConflicts.Saved).Where(p => p).Subscribe(s => + this.WhenAnyValue(p => p.CustomConflicts.Saved).Where(p => p).Subscribe(_ => { ResetConflicts.Refresh(); }).DisposeWith(disposables); - var previousEditTextState = false; - this.WhenAnyValue(v => v.EditingIgnoreConflictsRules).Subscribe(s => - { - if (s != previousEditTextState) - { - previousEditTextState = s; - } - }).DisposeWith(disposables); - hotkeyPressedHandler.Subscribe(m => { async Task performModSelectionAction() { - if (!filteringConflicts && (SelectedParentConflict?.Children.Any()).GetValueOrDefault()) + if (!filteringConflicts && SelectedParentConflict?.Children.Count != 0) { int? newSelectedConflict = null; - var col = SelectedParentConflict != null ? SelectedParentConflict.Children.ToList() : new List(); + var col = SelectedParentConflict != null ? [.. SelectedParentConflict.Children] : new List(); switch (m.Hotkey) { case Enums.HotKeys.Shift_Up: @@ -1042,8 +1022,10 @@ async Task performModSelectionAction() { index = 0; } + newSelectedConflict = index; } + break; case Enums.HotKeys.Shift_Down: @@ -1059,13 +1041,13 @@ async Task performModSelectionAction() { index = col.Count - 1; } + newSelectedConflict = index; } - break; - default: break; } + if (newSelectedConflict != null) { await Dispatcher.UIThread.SafeInvokeAsync(() => @@ -1075,12 +1057,13 @@ await Dispatcher.UIThread.SafeInvokeAsync(() => } } } + async Task performModParentSelectionAction() { - if (!filteringConflicts && (HierarchalConflicts?.Any()).GetValueOrDefault()) + if (!filteringConflicts && HierarchicalConflicts?.Count != 0) { IHierarchicalDefinitions parent = null; - var col = HierarchalConflicts.ToList(); + var col = HierarchicalConflicts!.ToList(); switch (m.Hotkey) { case Enums.HotKeys.Ctrl_Shift_P: @@ -1096,8 +1079,10 @@ async Task performModParentSelectionAction() { index = 0; } + parent = col[index]; } + break; case Enums.HotKeys.Ctrl_Shift_N: @@ -1113,13 +1098,13 @@ async Task performModParentSelectionAction() { index = col.Count - 1; } + parent = col[index]; } - break; - default: break; } + if (parent != null) { await Dispatcher.UIThread.SafeInvokeAsync(() => @@ -1129,6 +1114,7 @@ await Dispatcher.UIThread.SafeInvokeAsync(() => } } } + if (m.Hotkey == Enums.HotKeys.Shift_Down || m.Hotkey == Enums.HotKeys.Shift_Up) { performModSelectionAction().ConfigureAwait(false); @@ -1168,6 +1154,7 @@ protected virtual async Task ResolveConflictAsync(bool resolve) { return; } + if (await externalProcessHandlerService.IsParadoxLauncherRunningAsync()) { var title = localizationManager.GetResource(LocalizationResources.Notifications.ParadoxLauncherRunning.Title); @@ -1175,13 +1162,14 @@ protected virtual async Task ResolveConflictAsync(bool resolve) notificationAction.ShowNotification(title, message, NotificationType.Error, 30); return; } + ResolvingConflict = true; if (ModCompareSelector.VirtualDefinitions != null && ModCompareSelector.VirtualDefinitions.Any()) { IHierarchicalDefinitions conflictParent = null; int? conflictParentIdx = null; - int parentIdx = HierarchalConflicts.ToList().IndexOf(SelectedParentConflict); - foreach (var item in HierarchalConflicts) + var parentIdx = HierarchicalConflicts.ToList().IndexOf(SelectedParentConflict); + foreach (var item in HierarchicalConflicts) { if (item.Children.Contains(SelectedConflict)) { @@ -1191,9 +1179,10 @@ protected virtual async Task ResolveConflictAsync(bool resolve) break; } } + var id = idGenerator.GetNextId(); await TriggerOverlayAsync(id, true, localizationManager.GetResource(LocalizationResources.Conflict_Solver.OverlayResolve)); - IDefinition patchDefinition = null; + IDefinition patchDefinition; if (!IsBinaryConflict) { patchDefinition = ModCompareSelector.VirtualDefinitions.FirstOrDefault(p => modPatchCollectionService.IsPatchMod(p.ModName)); @@ -1209,6 +1198,7 @@ protected virtual async Task ResolveConflictAsync(bool resolve) patchDefinition = ModCompareSelector.Definitions.FirstOrDefault(); } } + if (patchDefinition != null) { var generatedFileNames = patchDefinition.GeneratedFileNames; @@ -1219,23 +1209,25 @@ protected virtual async Task ResolveConflictAsync(bool resolve) generatedFileNames.Add(item); } } + patchDefinition.GeneratedFileNames = generatedFileNames; SyncCode(patchDefinition); try { - if (await Task.Run(async () => resolve ? - await modPatchCollectionService.ApplyModPatchAsync(Conflicts, patchDefinition, SelectedModCollection.Name) : - await modPatchCollectionService.IgnoreModPatchAsync(Conflicts, patchDefinition, SelectedModCollection.Name))) + if (await Task.Run(async () => + resolve + ? await modPatchCollectionService.ApplyModPatchAsync(Conflicts, patchDefinition, SelectedModCollection.Name) + : await modPatchCollectionService.IgnoreModPatchAsync(Conflicts, patchDefinition, SelectedModCollection.Name))) { await FilterHierarchalConflictsAsync(Conflicts); IHierarchicalDefinitions selectedConflict = null; - if (conflictParentIdx.HasValue && HierarchalConflicts.Any()) + if (conflictParentIdx.HasValue && HierarchicalConflicts.Count != 0) { - foreach (var item in HierarchalConflicts) + foreach (var item in HierarchicalConflicts) { if (item.Name.Equals(conflictParent.Name)) { - if (conflictParentIdx.Value > (item.Children.Count - 1)) + if (conflictParentIdx.Value > item.Children.Count - 1) { conflictParentIdx = item.Children.Count - 1; } @@ -1243,11 +1235,13 @@ await modPatchCollectionService.IgnoreModPatchAsync(Conflicts, patchDefinition, { conflictParentIdx = 0; } + selectedConflict = item.Children.Select(p => p).ToList()[conflictParentIdx.GetValueOrDefault()]; break; } } } + SelectedConflict = selectedConflict; ResetConflicts.Refresh(); } @@ -1260,25 +1254,29 @@ await modPatchCollectionService.IgnoreModPatchAsync(Conflicts, patchDefinition, notificationAction.ShowNotification(title, message, NotificationType.Error, 30); } } + if (SelectedConflict == null) { - if (parentIdx > (HierarchalConflicts.Count - 1)) + if (parentIdx > HierarchicalConflicts.Count - 1) { - parentIdx = HierarchalConflicts.Count - 1; + parentIdx = HierarchicalConflicts.Count - 1; } else if (parentIdx < 0) { parentIdx = 0; } - if (HierarchalConflicts.Any()) + + if (HierarchicalConflicts.Count != 0) { // Force a refresh of the UI SelectedParentConflict = null; - SelectedParentConflict = HierarchalConflicts.ElementAt(parentIdx); + SelectedParentConflict = HierarchicalConflicts.ElementAt(parentIdx); } } + await TriggerOverlayAsync(id, false); } + ResolvingConflict = false; BackAllowed = true; } diff --git a/src/IronyModManager/ViewModels/MainControlViewModel.cs b/src/IronyModManager/ViewModels/MainControlViewModel.cs index 211e0065e..f842fa078 100644 --- a/src/IronyModManager/ViewModels/MainControlViewModel.cs +++ b/src/IronyModManager/ViewModels/MainControlViewModel.cs @@ -4,15 +4,17 @@ // Created : 01-20-2020 // // Last Modified By : Mario -// Last Modified On : 02-14-2021 +// Last Modified On : 02-21-2024 // *********************************************************************** // // Mario // // // *********************************************************************** -using System.Collections.Generic; + using System; +using System.Collections.Generic; +using System.Linq; using IronyModManager.Common.ViewModels; using IronyModManager.Shared; using IronyModManager.ViewModels.Controls; @@ -24,73 +26,59 @@ namespace IronyModManager.ViewModels /// Implements the /// /// + /// The theme control. + /// The language control. + /// The game control. + /// The mod control. + /// The options. + /// The actions. + /// Initializes a new instance of the class. [ExcludeFromCoverage("This should be tested via functional testing.")] - public class MainControlViewModel : BaseViewModel + public class MainControlViewModel( + ThemeControlViewModel themeControl, + LanguageControlViewModel languageControl, + GameControlViewModel gameControl, + ModHolderControlViewModel modControl, + OptionsControlViewModel options, + ActionsControlViewModel actions) : BaseViewModel { - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The theme control. - /// The language control. - /// The game control. - /// The mod control. - /// The options. - /// The actions. - public MainControlViewModel(ThemeControlViewModel themeControl, - LanguageControlViewModel languageControl, - GameControlViewModel gameControl, - ModHolderControlViewModel modControl, - OptionsControlViewModel options, ActionsControlViewModel actions) - { - ThemeSelector = themeControl; - LanguageSelector = languageControl; - GameSelector = gameControl; - ModHolder = modControl; - Options = options; - Actions = actions; - } - - #endregion Constructors - #region Properties /// /// Gets or sets the actions. /// /// The actions. - public virtual ActionsControlViewModel Actions { get; protected set; } + public virtual ActionsControlViewModel Actions { get; protected set; } = actions; /// /// Gets or sets the game selector. /// /// The game selector. - public virtual GameControlViewModel GameSelector { get; protected set; } + public virtual GameControlViewModel GameSelector { get; protected set; } = gameControl; /// /// Gets or sets the language selector. /// /// The language selector. - public virtual LanguageControlViewModel LanguageSelector { get; protected set; } + public virtual LanguageControlViewModel LanguageSelector { get; protected set; } = languageControl; /// /// Gets or sets the mod holder. /// /// The mod holder. - public virtual ModHolderControlViewModel ModHolder { get; protected set; } + public virtual ModHolderControlViewModel ModHolder { get; protected set; } = modControl; /// /// Gets or sets the options. /// /// The options. - public virtual OptionsControlViewModel Options { get; protected set; } + public virtual OptionsControlViewModel Options { get; protected set; } = options; /// /// Gets the theme selector. /// /// The theme selector. - public virtual ThemeControlViewModel ThemeSelector { get; protected set; } + public virtual ThemeControlViewModel ThemeSelector { get; protected set; } = themeControl; #endregion Properties diff --git a/src/IronyModManager/ViewModels/MainWindowViewModel.cs b/src/IronyModManager/ViewModels/MainWindowViewModel.cs index 46bf62821..7a67998fc 100644 --- a/src/IronyModManager/ViewModels/MainWindowViewModel.cs +++ b/src/IronyModManager/ViewModels/MainWindowViewModel.cs @@ -1,11 +1,10 @@ - -// *********************************************************************** +// *********************************************************************** // Assembly : IronyModManager // Author : Mario // Created : 01-10-2020 // // Last Modified By : Mario -// Last Modified On : 06-23-2023 +// Last Modified On : 02-21-2024 // *********************************************************************** // // Mario @@ -15,6 +14,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reactive; using System.Reactive.Disposables; @@ -29,7 +29,6 @@ namespace IronyModManager.ViewModels { - /// /// Class MainWindowViewModel. /// Implements the @@ -43,12 +42,12 @@ public class MainWindowViewModel : BaseViewModel /// /// The overlay stack /// - protected readonly List overlayStack = new(); + protected readonly List OverlayStack = []; /// /// The loop running /// - protected bool loopRunning = false; + protected bool LoopRunning; /// /// The hotkey manager @@ -78,7 +77,7 @@ public class MainWindowViewModel : BaseViewModel /// /// The last message identifier /// - private long lastMessageId = 0; + private long lastMessageId; /// /// The overlay disposable @@ -177,12 +176,7 @@ public MainWindowViewModel() /// The message. public void TriggerManualOverlay(long id, bool isVisible, string message) { - QueueOverlay(new OverlayProgressEvent() - { - Id = id, - IsVisible = isVisible, - Message = message - }); + QueueOverlay(new OverlayProgressEvent { Id = id, IsVisible = isVisible, Message = message }); } /// @@ -202,6 +196,7 @@ protected virtual async Task AnimateTransitionAsync(bool mainVisible) { opacity = 1; } + MainOpacity = opacity; await Task.Delay(5); } @@ -214,10 +209,7 @@ protected void BindOverlay() { InitOverlayLoop(); overlayDisposable?.Dispose(); - overlayDisposable = overlayProgressHandler.Subscribe(s => - { - QueueOverlay(s); - }); + overlayDisposable = overlayProgressHandler.Subscribe(QueueOverlay); if (Disposables != null) { overlayDisposable.DisposeWith(Disposables); @@ -244,9 +236,9 @@ protected async Task FreeMemoryAsync() /// protected void InitOverlayLoop() { - if (!loopRunning) + if (!LoopRunning) { - loopRunning = true; + LoopRunning = true; Task.Run(() => OverlayLoopAsync().ConfigureAwait(false)); } } @@ -313,41 +305,45 @@ void setOverlayProperties(OverlayProgressEvent e) { OverlayVisible = e.IsVisible; } + if (e.Message != OverlayMessage) { OverlayMessage = e.Message; } + if (e.MessageProgress != OverlayMessageProgress) { OverlayMessageProgress = e.MessageProgress; HasProgress = !string.IsNullOrWhiteSpace(e.MessageProgress); } } + while (true) { await Task.Delay(2); lock (queueLock) { var now = DateTime.Now; - if (overlayStack.Any(p => now >= p.DateAdded) && currentMessageId.HasValue) + if (OverlayStack.Any(p => now >= p.DateAdded) && currentMessageId.HasValue) { - var overlays = overlayStack.Where(p => now >= p.DateAdded && p.Event.Id == currentMessageId.GetValueOrDefault()).OrderBy(p => p.DateAdded).ToList(); + var overlays = OverlayStack.Where(p => now >= p.DateAdded && p.Event.Id == currentMessageId.GetValueOrDefault()).OrderBy(p => p.DateAdded).ToList(); if (overlays.Count > 0) { - OverlayQueue overlay = null; + OverlayQueue overlay; if (overlays.Any(p => p.Event.IsVisible != OverlayVisible)) { if (overlays.Any(p => p.Event.IsVisible == false)) { - overlayStack.RemoveAll(p => p.Event.Id <= currentMessageId.GetValueOrDefault()); - if (overlayStack.Count > 0) + OverlayStack.RemoveAll(p => p.Event.Id <= currentMessageId.GetValueOrDefault()); + if (OverlayStack.Count > 0) { - currentMessageId = overlayStack.OrderByDescending(p => p.Event.Id).FirstOrDefault().Event.Id; + currentMessageId = OverlayStack.OrderByDescending(p => p.Event.Id)!.FirstOrDefault()!.Event.Id; } else { currentMessageId = null; } + if (currentMessageId.HasValue) { lastMessageId = currentMessageId.GetValueOrDefault(); @@ -356,6 +352,7 @@ void setOverlayProperties(OverlayProgressEvent e) { lastMessageId++; } + overlay = overlays.FirstOrDefault(p => p.Event.IsVisible == false); } else @@ -363,7 +360,7 @@ void setOverlayProperties(OverlayProgressEvent e) overlay = overlays.FirstOrDefault(p => p.Event.IsVisible != OverlayVisible); if (overlay != null) { - overlayStack.Remove(overlay); + OverlayStack.Remove(overlay); } } } @@ -371,10 +368,12 @@ void setOverlayProperties(OverlayProgressEvent e) { foreach (var item in overlays) { - overlayStack.Remove(item); + OverlayStack.Remove(item); } + overlay = overlays.LastOrDefault(); } + if (overlay != null) { setOverlayProperties(overlay.Event); @@ -393,18 +392,19 @@ protected void QueueOverlay(OverlayProgressEvent e) { lock (queueLock) { - if (!overlayStack.Any(p => p.Event == e)) + if (OverlayStack.All(p => p.Event != e)) { - if (!currentMessageId.HasValue && e.Id < lastMessageId) + switch (currentMessageId) { - return; - } - if (!currentMessageId.HasValue && OverlayVisible != e.IsVisible && e.IsVisible) - { - currentMessageId = e.Id; - lastMessageId = e.Id; + case null when e.Id < lastMessageId: + return; + case null when OverlayVisible != e.IsVisible && e.IsVisible: + currentMessageId = e.Id; + lastMessageId = e.Id; + break; } - overlayStack.Add(new OverlayQueue() { Event = e, DateAdded = DateTime.Now }); + + OverlayStack.Add(new OverlayQueue { Event = e, DateAdded = DateTime.Now }); } } } diff --git a/src/IronyModManager/Views/Controls/MergeViewerControlView.xaml b/src/IronyModManager/Views/Controls/MergeViewerControlView.xaml index 66b045894..e9daaa780 100644 --- a/src/IronyModManager/Views/Controls/MergeViewerControlView.xaml +++ b/src/IronyModManager/Views/Controls/MergeViewerControlView.xaml @@ -6,91 +6,100 @@ xmlns:controls="clr-namespace:IronyModManager.Controls;assembly=IronyModManager" xmlns:converter="clr-namespace:IronyModManager.Converters;assembly=IronyModManager" x:Class="IronyModManager.Views.Controls.MergeViewerControlView"> - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + private readonly IResourceLoader resourceLoader; + /// + /// A private IronyModManager.Controls.TextEditor named diffLeft. + /// + private IronyModManager.Controls.TextEditor diffLeft; + + /// + /// A private IronyModManager.Controls.TextEditor named diffRight. + /// + private IronyModManager.Controls.TextEditor diffRight; + + /// + /// A private AvaloniaEdit.Search.SearchPanel named diffSearchPanelLeft. + /// + private SearchPanel diffSearchPanelLeft; + + /// + /// A private AvaloniaEdit.Search.SearchPanel named diffSearchPanelRight. + /// + private SearchPanel diffSearchPanelRight; + /// /// The editor left /// @@ -72,17 +97,22 @@ public class MergeViewerControlView : BaseControl /// /// The search panel left /// - private AvaloniaEdit.Search.SearchPanel searchPanelLeft; + private SearchPanel editorSearchPanelLeft; /// /// The search panel right /// - private AvaloniaEdit.Search.SearchPanel searchPanelRight; + private SearchPanel editorSearchPanelRight; + + /// + /// A private bool named syncingDiffScroll. + /// + private bool syncingDiffScroll; /// /// The syncing scroll /// - private bool syncingScroll = false; + private bool syncingScroll; #endregion Fields @@ -113,6 +143,36 @@ protected virtual void FocusConflict(int line, ListBox leftListBox, ListBox righ { leftListBox.SelectedIndex = line; rightListBox.SelectedIndex = line; + diffLeft.ScrollToLine(line); + diffRight.ScrollToLine(line); + ViewModel!.DiffEditorTopLine = diffLeft.GetTopVisibleLine(); + ViewModel.DiffEditorBottomLine = diffLeft.GetBottomVisibleLine(); + if (!(ViewModel.DiffEditorTopLine <= line && ViewModel.DiffEditorBottomLine >= line)) + { + diffLeft.TextArea.Caret.Location = new TextLocation(line, 1); + diffRight.TextArea.Caret.Location = new TextLocation(line, 1); + } + } + + /// + /// Gets a text editor selected text range. + /// + /// The editor. + /// a Tuple with int,int . + protected virtual Tuple GetTextEditorSelectedTextRange(TextEditor editor) + { + var segments = editor.TextArea.Selection.Segments; + var doc = editor.TextArea.Document; + foreach (var segment in segments) + { + var start = doc.GetLineByOffset(segment.StartOffset).LineNumber; + var end = doc.GetLineByOffset(segment.EndOffset).LineNumber; + var min = Math.Min(start, end); + var max = Math.Max(start, end); + return Tuple.Create(min - 1, max - 1); + } + + return Tuple.Create(-1, -1); } /// @@ -126,7 +186,7 @@ protected virtual void HandleContextMenu(IronyModManager.Controls.ListBox listBo List menuItems = null; if (hoveredItem != null) { - if ((!ViewModel.RightSidePatchMod && !ViewModel.LeftSidePatchMod) || ViewModel.IsReadOnlyMode) + if ((!ViewModel!.RightSidePatchMod && !ViewModel.LeftSidePatchMod) || ViewModel.IsReadOnlyMode) { menuItems = GetNonEditableMenuItems(leftSide); } @@ -134,39 +194,95 @@ protected virtual void HandleContextMenu(IronyModManager.Controls.ListBox listBo { if (leftSide) { - menuItems = ViewModel.RightSidePatchMod ? GetActionsMenuItems(leftSide) : GetEditableMenuItems(leftSide); + menuItems = ViewModel.RightSidePatchMod ? GetActionsMenuItems(true) : GetEditableMenuItems(true); } else { - menuItems = ViewModel.LeftSidePatchMod ? GetActionsMenuItems(leftSide) : GetEditableMenuItems(leftSide); + menuItems = ViewModel.LeftSidePatchMod ? GetActionsMenuItems(false) : GetEditableMenuItems(false); } } } + listBox.SetContextMenuItems(menuItems); } + /// + /// Handles an editor context menu. + /// + /// The editor. + /// The search panel. + /// If true, left side. + protected virtual void HandleEditorContextMenu(IronyModManager.Controls.TextEditor editor, SearchPanel searchPanel, bool leftSide) + { + if (editor == null || searchPanel == null) + { + return; + } + + var ctx = new MenuFlyout { Items = new List() }; + + void setMenuItems() + { + var canReplace = false; + List menuItems; + if ((!ViewModel!.RightSidePatchMod && !ViewModel.LeftSidePatchMod) || ViewModel.IsReadOnlyMode) + { + menuItems = GetNonEditableMenuItems(leftSide); + } + else + { + if (leftSide) + { + menuItems = ViewModel.RightSidePatchMod ? GetActionsMenuItems(true) : GetEditableMenuItems(true); + canReplace = ViewModel.LeftSidePatchMod; + } + else + { + menuItems = ViewModel.LeftSidePatchMod ? GetActionsMenuItems(false) : GetEditableMenuItems(false); + canReplace = ViewModel.RightSidePatchMod; + } + } + + menuItems.AddRange( + [ + new MenuItem { Header = "-" }, + new MenuItem { Header = ViewModel.EditorFind, Command = ReactiveCommand.Create(() => HandleEditorFindOrReplace(editor, searchPanel, false)).DisposeWith(Disposables) } + ]); + if (canReplace) + { + menuItems.Add(new MenuItem { Header = ViewModel.EditorReplace, Command = ReactiveCommand.Create(() => HandleEditorFindOrReplace(editor, searchPanel, true)).DisposeWith(Disposables) }); + } + + ctx.Items = menuItems; + } + + editor.ContextFlyout = ctx; + editor.ContextFlyout.Opening += (_, _) => setMenuItems(); + } + /// /// Handles the listbox property changed. /// /// The instance containing the event data. - /// The this ListBox. - /// The other ListBox. + /// This ListBox. + /// Other ListBox. protected virtual void HandleListBoxPropertyChanged(AvaloniaPropertyChangedEventArgs args, ListBox thisListBox, ListBox otherListBox) { if (args.Property == ListBox.ScrollProperty && thisListBox.Scroll != null) { var scroll = (ScrollViewer)thisListBox.Scroll; - scroll.PropertyChanged += (scrollSender, scrollArgs) => + scroll.PropertyChanged += (_, scrollArgs) => { if (scrollArgs.Property == ScrollViewer.HorizontalScrollBarValueProperty || scrollArgs.Property == ScrollViewer.VerticalScrollBarValueProperty) { if (thisListBox.Scroll == null || otherListBox.Scroll == null || syncingScroll || - (otherListBox.Scroll.Offset.X == thisListBox.Scroll.Offset.X && otherListBox.Scroll.Offset.Y == thisListBox.Scroll.Offset.Y)) + (otherListBox.Scroll.Offset.X.IsNearlyEqual(thisListBox.Scroll.Offset.X) && otherListBox.Scroll.Offset.Y.IsNearlyEqual(thisListBox.Scroll.Offset.Y))) { return; } + syncingScroll = true; Dispatcher.UIThread.SafeInvoke(async () => { @@ -178,6 +294,32 @@ protected virtual void HandleListBoxPropertyChanged(AvaloniaPropertyChangedEvent } } + /// + /// Handles a text editor property changed. + /// + /// This text editor. + /// Other text editor. + protected virtual void HandleTextEditorPropertyChanged(IronyModManager.Controls.TextEditor thisTextEditor, IronyModManager.Controls.TextEditor otherTextEditor) + { + thisTextEditor.ScrollViewer.ScrollChanged += (_, _) => + { + if (syncingDiffScroll || thisTextEditor.ScrollViewer == null || otherTextEditor.ScrollViewer == null || (otherTextEditor.ScrollViewer.Offset.X.IsNearlyEqual(thisTextEditor.ScrollViewer.Offset.X) && + otherTextEditor.ScrollViewer.Offset.Y.IsNearlyEqual(thisTextEditor.ScrollViewer.Offset.Y))) + { + return; + } + + syncingDiffScroll = true; + Dispatcher.UIThread.SafeInvoke(async () => + { + await SyncDiffScrollAsync(thisTextEditor, otherTextEditor); + ViewModel!.DiffEditorTopLine = thisTextEditor.GetTopVisibleLine(); + ViewModel.DiffEditorBottomLine = thisTextEditor.GetBottomVisibleLine(); + syncingDiffScroll = false; + }); + }; + } + /// /// Called when [activated]. /// @@ -186,77 +328,117 @@ protected override void OnActivated(CompositeDisposable disposables) { editorLeft = this.FindControl("editorLeft"); editorRight = this.FindControl("editorRight"); - SetEditorOptions(editorLeft, true); - SetEditorOptions(editorRight, false); - searchPanelLeft = AvaloniaEdit.Search.SearchPanel.Install(editorLeft); - searchPanelRight = AvaloniaEdit.Search.SearchPanel.Install(editorRight); - SetEditorContextMenu(editorLeft, searchPanelLeft); - SetEditorContextMenu(editorRight, searchPanelRight); + SetEditorOptions(editorLeft); + SetEditorOptions(editorRight); + editorSearchPanelLeft = SearchPanel.Install(editorLeft); + editorSearchPanelRight = SearchPanel.Install(editorRight); + SetEditorContextMenu(editorLeft, editorSearchPanelLeft); + SetEditorContextMenu(editorRight, editorSearchPanelRight); + + diffLeft = this.FindControl("diffLeft"); + diffRight = this.FindControl("diffRight"); + SetDiffOptions(diffLeft, true); + SetDiffOptions(diffRight, false); + diffSearchPanelLeft = SearchPanel.Install(diffLeft); + diffSearchPanelRight = SearchPanel.Install(diffRight); + HandleEditorContextMenu(diffLeft, diffSearchPanelLeft, true); + HandleEditorContextMenu(diffRight, diffSearchPanelRight, false); + diffLeft.ScrollInitialized += (_, _) => HandleTextEditorPropertyChanged(diffLeft, diffRight); + diffRight.ScrollInitialized += (_, _) => HandleTextEditorPropertyChanged(diffRight, diffLeft); + var diffLeftMargin = new DiffMargin { Lines = ViewModel!.LeftDiff }; + var diffRightMargin = new DiffMargin { Lines = ViewModel.RightDiff }; + var diffLeftRenderer = new DiffBackgroundRenderer { Lines = ViewModel.LeftDiff }; + var diffRightRenderer = new DiffBackgroundRenderer { Lines = ViewModel.RightDiff }; + diffLeft.TextArea.LeftMargins.Add(diffLeftMargin); + diffLeft.TextArea.TextView.BackgroundRenderers.Add(diffLeftRenderer); + diffRight.TextArea.LeftMargins.Add(diffRightMargin); + diffRight.TextArea.TextView.BackgroundRenderers.Add(diffRightRenderer); + diffLeft.SafeSetText(string.Join(Environment.NewLine, ViewModel.LeftDiff)); + diffRight.SafeSetText(string.Join(Environment.NewLine, ViewModel.RightDiff)); + diffLeft.IsReadOnly = !ViewModel.LeftSidePatchMod; + diffRight.IsReadOnly = !ViewModel.RightSidePatchMod; var leftSide = this.FindControl("leftSide"); var rightSide = this.FindControl("rightSide"); + leftSide.PropertyChanged += (_, args) => { HandleListBoxPropertyChanged(args, leftSide, rightSide); }; + rightSide.PropertyChanged += (_, args) => { HandleListBoxPropertyChanged(args, rightSide, leftSide); }; + leftSide.ContextMenuOpening += item => { HandleContextMenu(leftSide, item, true); }; + rightSide.ContextMenuOpening += item => { HandleContextMenu(rightSide, item, false); }; - leftSide.PropertyChanged += (sender, args) => - { - HandleListBoxPropertyChanged(args, leftSide, rightSide); - }; - rightSide.PropertyChanged += (sender, args) => - { - HandleListBoxPropertyChanged(args, rightSide, leftSide); - }; - leftSide.ContextMenuOpening += (item) => - { - HandleContextMenu(leftSide, item, true); - }; - rightSide.ContextMenuOpening += (item) => - { - HandleContextMenu(rightSide, item, false); - }; - ViewModel.ConflictFound += (line) => - { - FocusConflict(line, leftSide, rightSide); - }; + ViewModel!.ConflictFound += line => { FocusConflict(line, leftSide, rightSide); }; + + var diffHotkeyActionLeft = -1; + var diffHotkeyActionRight = -1; int? focusSideScrollItem = null; - int previousCount = 0; + var previousCount = 0; var autoScroll = leftSide.AutoScrollToSelectedItem; - ViewModel.PreFocusSide += (left) => + ViewModel.PreFocusSide += left => { - leftSide.AutoScrollToSelectedItem = rightSide.AutoScrollToSelectedItem = false; - var listBox = left ? leftSide : rightSide; - var visibleItems = listBox.ItemContainerGenerator.Containers.ToList(); - if (visibleItems.Any()) + if (ViewModel.UsingNewMergeType) { - focusSideScrollItem = visibleItems.LastOrDefault().Index; - previousCount = (left ? leftSide : rightSide).ItemCount; + diffHotkeyActionLeft = diffLeft.GetMiddleVisibleLine(); + diffHotkeyActionRight = diffRight.GetMiddleVisibleLine(); + } + else + { + leftSide.AutoScrollToSelectedItem = rightSide.AutoScrollToSelectedItem = false; + var listBox = left ? leftSide : rightSide; + var visibleItems = listBox.ItemContainerGenerator.Containers.ToList(); + if (visibleItems.Count != 0) + { + focusSideScrollItem = visibleItems.LastOrDefault()!.Index; + previousCount = (left ? leftSide : rightSide).ItemCount; + } } }; - ViewModel.PostFocusSide += (left) => + ViewModel.PostFocusSide += left => { async Task delay() { await Task.Delay(50); - leftSide.AutoScrollToSelectedItem = rightSide.AutoScrollToSelectedItem = autoScroll; - var listBox = left ? leftSide : rightSide; - listBox.Focus(); - if (focusSideScrollItem.HasValue) + if (ViewModel!.UsingNewMergeType) { - if (listBox.ItemCount != previousCount) + diffLeft.ScrollToLine(diffHotkeyActionLeft); + diffRight.ScrollToLine(diffHotkeyActionRight); + ViewModel!.DiffEditorTopLine = diffLeft.GetTopVisibleLine(); + ViewModel.DiffEditorBottomLine = diffLeft.GetBottomVisibleLine(); + if (!(ViewModel.DiffEditorTopLine <= diffHotkeyActionLeft && ViewModel.DiffEditorBottomLine >= diffHotkeyActionLeft)) { - focusSideScrollItem -= Math.Abs(previousCount - listBox.ItemCount); + diffLeft.TextArea.Caret.Location = new TextLocation(diffHotkeyActionLeft, 1); + diffRight.TextArea.Caret.Location = new TextLocation(diffHotkeyActionLeft, 1); } - FocusConflict(-1, leftSide, rightSide); - if (focusSideScrollItem.GetValueOrDefault() < 0) - { - focusSideScrollItem = 0; - } - else if (focusSideScrollItem.GetValueOrDefault() >= listBox.ItemCount) + + RedrawEditorDiffs(); + } + else + { + leftSide.AutoScrollToSelectedItem = rightSide.AutoScrollToSelectedItem = autoScroll; + var listBox = left ? leftSide : rightSide; + listBox.Focus(); + if (focusSideScrollItem.HasValue) { - focusSideScrollItem = listBox.ItemCount - 1; + if (listBox.ItemCount != previousCount) + { + focusSideScrollItem -= Math.Abs(previousCount - listBox.ItemCount); + } + + FocusConflict(-1, leftSide, rightSide); + if (focusSideScrollItem.GetValueOrDefault() < 0) + { + focusSideScrollItem = 0; + } + else if (focusSideScrollItem.GetValueOrDefault() >= listBox.ItemCount) + { + focusSideScrollItem = listBox.ItemCount - 1; + } + + listBox.ScrollIntoView(focusSideScrollItem.GetValueOrDefault()); } - listBox.ScrollIntoView(focusSideScrollItem.GetValueOrDefault()); } + focusSideScrollItem = null; } + Dispatcher.UIThread.SafeInvoke(() => delay().ConfigureAwait(false)); }; @@ -265,61 +447,73 @@ async Task delay() DiffPieceWithIndex findItem(bool searchUp) { var visibleItems = leftSide.ItemContainerGenerator.Containers.ToList(); - if (visibleItems.Any()) + if (visibleItems.Count != 0) { if (leftSide.Items is IEnumerable items) { var itemsList = items.ToList(); if (searchUp) { - if (visibleItems.FirstOrDefault().Item is DiffPieceWithIndex visibleItem) + if (visibleItems.FirstOrDefault()!.Item is DiffPieceWithIndex visibleItem) { var index = itemsList.IndexOf(visibleItem) - 2; if (index < 0) { index = 0; } + return itemsList[index]; } } else { - if (visibleItems.LastOrDefault().Item is DiffPieceWithIndex visibleItem) + if (visibleItems.LastOrDefault()!.Item is DiffPieceWithIndex visibleItem) { var index = itemsList.IndexOf(visibleItem) + 2; if (index > leftSide.ItemCount - 1) { index = leftSide.ItemCount - 1; } + return itemsList[index]; } } } } + return null; } + void evalKey() { // Yeah, it sucks that we can't access a property from a different thread - if (ViewModel.CanPerformHotKeyActions) + if (ViewModel!.CanPerformHotKeyActions) { - DiffPiece item = null; - switch (hotkey.Hotkey) + if (ViewModel.UsingNewMergeType) { - case Enums.HotKeys.Ctrl_Shift_Up: - item = findItem(true); - break; - - case Enums.HotKeys.Ctrl_Shift_Down: - item = findItem(false); - break; - - default: - break; + switch (hotkey.Hotkey) + { + case Enums.HotKeys.Ctrl_Shift_Up: + diffLeft.ScrollViewer.LineUp(); + break; + case Enums.HotKeys.Ctrl_Shift_Down: + diffLeft.ScrollViewer.LineDown(); + break; + } } - if (item != null) + else { - leftSide.ScrollIntoView(item); + DiffPiece item = hotkey.Hotkey switch + { + Enums.HotKeys.Ctrl_Shift_Up => findItem(true), + Enums.HotKeys.Ctrl_Shift_Down => findItem(false), + _ => null + }; + + if (item != null) + { + leftSide.ScrollIntoView(item); + } } } } @@ -327,6 +521,104 @@ void evalKey() Dispatcher.UIThread.SafeInvoke(evalKey); }).DisposeWith(disposables); + this.WhenAnyValue(v => v.ViewModel.LeftDiff).Subscribe(s => + { + diffLeftMargin.Lines = s; + diffLeftRenderer.Lines = s; + + var oldText = diffLeft.Text; + var newText = string.Join(Environment.NewLine, s.Select(p => p.Text)); + if (oldText.Equals(newText)) + { + return; + } + + var lineDeleted = ViewModel!.PreviousLeftDiff != null && s != null && s.Count < ViewModel!.PreviousLeftDiff.Count && s.Count > 0 && ViewModel!.PreviousLeftDiff.Count > 0; + var locLine = diffLeft.TextArea.Caret.Location.Line; + if (lineDeleted) + { + locLine--; + if (locLine < 1) + { + locLine = 1; + } + } + + var locColumn = diffLeft.TextArea.Caret.Location.Column; + diffLeft.SafeSetText(newText); + + if (lineDeleted) + { + locColumn = diffLeft.GetLastColumnByLine(locLine); + } + + diffLeft.TextArea.Caret.Location = new TextLocation(locLine, locColumn); + }).DisposeWith(disposables); + + this.WhenAnyValue(v => v.ViewModel.RightDiff).Subscribe(s => + { + diffRightMargin.Lines = s; + diffRightRenderer.Lines = s; + + var oldText = diffRight.Text; + var newText = string.Join(Environment.NewLine, s.Select(p => p.Text)); + if (oldText.Equals(newText)) + { + return; + } + + var lineDeleted = ViewModel!.PreviousRightDiff != null && s != null && s.Count < ViewModel!.PreviousRightDiff.Count && s.Count > 0 && ViewModel!.PreviousRightDiff.Count > 0; + var locLine = diffRight.TextArea.Caret.Location.Line; + if (lineDeleted) + { + locLine--; + if (locLine < 1) + { + locLine = 1; + } + } + + var locColumn = diffRight.TextArea.Caret.Location.Column; + diffRight.SafeSetText(newText); + + if (lineDeleted) + { + locColumn = diffRight.GetLastColumnByLine(locLine); + } + + diffRight.TextArea.Caret.Location = new TextLocation(locLine, locColumn); + }); + + this.WhenAnyValue(v => v.ViewModel.LeftSidePatchMod).Subscribe(s => + { + diffLeft.IsReadOnly = !s; + }).DisposeWith(disposables); + + this.WhenAnyValue(v => v.ViewModel.RightSidePatchMod).Subscribe(s => + { + diffRight.IsReadOnly = !s; + }).DisposeWith(disposables); + + ViewModel!.HotkeyBeforePerform += (_, _) => + { + diffHotkeyActionLeft = diffLeft.GetMiddleVisibleLine(); + diffHotkeyActionRight = diffRight.GetMiddleVisibleLine(); + }; + ViewModel.HotkeyAfterPerform += (_, _) => + { + diffLeft.ScrollToLine(diffHotkeyActionLeft); + diffRight.ScrollToLine(diffHotkeyActionRight); + ViewModel!.DiffEditorTopLine = diffLeft.GetTopVisibleLine(); + ViewModel.DiffEditorBottomLine = diffLeft.GetBottomVisibleLine(); + if (!(ViewModel.DiffEditorTopLine <= diffHotkeyActionLeft && ViewModel.DiffEditorBottomLine >= diffHotkeyActionLeft)) + { + diffLeft.TextArea.Caret.Location = new TextLocation(diffHotkeyActionLeft, 1); + diffRight.TextArea.Caret.Location = new TextLocation(diffHotkeyActionLeft, 1); + } + + RedrawEditorDiffs(); + }; + base.OnActivated(disposables); } @@ -337,55 +629,194 @@ void evalKey() /// The old locale. protected override void OnLocaleChanged(string newLocale, string oldLocale) { - SetEditorContextMenu(editorLeft, searchPanelLeft); - SetEditorContextMenu(editorRight, searchPanelRight); + SetEditorContextMenu(editorLeft, editorSearchPanelLeft); + SetEditorContextMenu(editorRight, editorSearchPanelRight); + HandleEditorContextMenu(diffLeft, diffSearchPanelLeft, true); + HandleEditorContextMenu(diffRight, diffSearchPanelRight, false); base.OnLocaleChanged(newLocale, oldLocale); } /// - /// Sets the editor options. + /// Sets a diff options. /// - /// The editor. - /// if set to true [left side]. - protected virtual void SetEditorOptions(TextEditor editor, bool leftSide) + /// The diff. + /// If true, left diff. + protected virtual void SetDiffOptions(TextEditor diff, bool leftDiff) { - editor.Options = new TextEditorOptions() - { - ConvertTabsToSpaces = true, - IndentationSize = 4 - }; - - ViewModel.WhenAnyValue(p => p.EditingYaml).Subscribe(s => - { - setEditMode(); - }).DisposeWith(Disposables); + diff.Options = new TextEditorOptions { ConvertTabsToSpaces = true, IndentationSize = 4 }; + ViewModel.WhenAnyValue(p => p.EditingYaml).Subscribe(_ => { setEditMode(); }).DisposeWith(Disposables); setEditMode(); void setEditMode() { - if (ViewModel.EditingYaml) + diff.SyntaxHighlighting = ViewModel!.EditingYaml ? resourceLoader.GetYAMLDefinition() : resourceLoader.GetPDXScriptDefinition(); + } + + diff.TextChanged += (_, _) => + { + var lines = diff.Text.SplitOnNewLine().ToList(); + var text = string.Join(Environment.NewLine, lines); + text = text.Trim().Trim(Environment.NewLine); + if (leftDiff) { - editor.SyntaxHighlighting = resourceLoader.GetYAMLDefinition(); + if (text.Equals(ViewModel!.LeftSide.Trim().Trim(Environment.NewLine))) + { + return; + } } else { - editor.SyntaxHighlighting = resourceLoader.GetPDXScriptDefinition(); + if (text.Equals(ViewModel!.RightSide.Trim().Trim(Environment.NewLine))) + { + return; + } + } + + if (leftDiff) + { + ViewModel!.SetText(text, ViewModel.RightSide); + diffRight.TextArea.TextView.Redraw(); + } + else + { + ViewModel!.SetText(ViewModel.LeftSide, text); + diffLeft.TextArea.TextView.Redraw(); + } + }; + diff.TextArea.SelectionChanged += (_, _) => + { + var range = GetTextEditorSelectedTextRange(diff); + if (range.Item1 == -1 || range.Item2 == -1) + { + return; + } + + var col = leftDiff ? ViewModel!.LeftSideSelected : ViewModel!.RightSideSelected; + var sourceCol = leftDiff ? ViewModel.LeftDiff : ViewModel.RightDiff; + + if (range.Item1 > sourceCol.Count - 1 || range.Item2 > sourceCol.Count) + { + return; + } + + col.Clear(); + for (var i = range.Item1; i < range.Item2 + 1; i++) + { + col.Add(sourceCol[i]); } + }; + + void handlePointerPressed(PointerPressedEventArgs args) + { + var pos = args.GetPosition(diff); + pos = new Point(0, pos.Y.CoerceValue(0, diff.Bounds.Height) + diff.VerticalOffset); + var line = diff.TextArea.TextView.GetVisualLineFromVisualTop(pos.Y); + if (line != null) + { + var col = leftDiff ? ViewModel!.LeftSideSelected : ViewModel!.RightSideSelected; + var sourceCol = leftDiff ? ViewModel.LeftDiff : ViewModel.RightDiff; + var idx = line.FirstDocumentLine.LineNumber - 1; + if (idx >= sourceCol.Count) + { + return; + } + + if (col.Count == 0) + { + col.Add(sourceCol[idx]); + } + else + { + var item = sourceCol[idx]; + if (!col.Contains(item)) + { + col.Clear(); + col.Add(sourceCol[idx]); + } + } + } + } + + diff.PointerPressed += (_, args) => + { + handlePointerPressed(args); + }; + + ((IronyModManager.Controls.TextArea)diff.TextArea).LeftPointerPressed += (_, eventArgs) => + { + handlePointerPressed(eventArgs); + }; + } + + /// + /// Sets an editor options. + /// + /// The editor. + protected virtual void SetEditorOptions(TextEditor editor) + { + editor.Options = new TextEditorOptions { ConvertTabsToSpaces = true, IndentationSize = 4 }; + + ViewModel.WhenAnyValue(p => p.EditingYaml).Subscribe(_ => { setEditMode(); }).DisposeWith(Disposables); + setEditMode(); + + void setEditMode() + { + editor.SyntaxHighlighting = ViewModel!.EditingYaml ? resourceLoader.GetYAMLDefinition() : resourceLoader.GetPDXScriptDefinition(); } - editor.TextChanged += (sender, args) => + editor.TextChanged += (_, _) => { var lines = editor.Text.SplitOnNewLine().ToList(); - string text = string.Join(Environment.NewLine, lines); - ViewModel.CurrentEditText = text; + var text = string.Join(Environment.NewLine, lines); + ViewModel!.CurrentEditText = text; }; } + /// + /// Syncs a diff scroll async. + /// + /// This text editor. + /// The other text editor. + /// A Task. + protected virtual Task SyncDiffScrollAsync(IronyModManager.Controls.TextEditor thisTextEditor, IronyModManager.Controls.TextEditor otherTextEditor) + { + var thisMaxX = Math.Abs(thisTextEditor.ScrollViewer.Extent.Width - thisTextEditor.ScrollViewer.Viewport.Width); + var thisMaxY = Math.Abs(thisTextEditor.ScrollViewer.Extent.Height - thisTextEditor.ScrollViewer.Viewport.Height); + var otherMaxX = Math.Abs(otherTextEditor.ScrollViewer.Extent.Width - otherTextEditor.ScrollViewer.Viewport.Width); + var otherMaxY = Math.Abs(otherTextEditor.ScrollViewer.Extent.Height - otherTextEditor.ScrollViewer.Viewport.Height); + var offset = thisTextEditor.ScrollViewer.Offset; + if (thisTextEditor.ScrollViewer.Offset.X > otherMaxX || thisTextEditor.ScrollViewer.Offset.X.IsNearlyEqual(thisMaxX)) + { + offset = offset.WithX(otherMaxX); + } + + if (thisTextEditor.ScrollViewer.Offset.Y > otherMaxY || thisTextEditor.ScrollViewer.Offset.Y.IsNearlyEqual(thisMaxY)) + { + offset = offset.WithY(otherMaxY); + } + + if (!otherTextEditor.ScrollViewer.Offset.X.IsNearlyEqual(offset.X) || !otherTextEditor.ScrollViewer.Offset.Y.IsNearlyEqual(offset.Y)) + { + try + { + otherTextEditor.InvalidateArrange(); + thisTextEditor.InvalidateArrange(); + otherTextEditor.ScrollViewer.Offset = offset; + } + catch (Exception ex) + { + logger.Error(ex); + } + } + + return Task.CompletedTask; + } + /// /// Synchronizes the scroll asynchronous. /// - /// The this ListBox. - /// The other ListBox. + /// This ListBox. + /// Other ListBox. /// Task. protected virtual Task SyncScrollAsync(ListBox thisListBox, ListBox otherListBox) { @@ -394,15 +825,17 @@ protected virtual Task SyncScrollAsync(ListBox thisListBox, ListBox otherListBox var otherMaxX = Math.Abs(otherListBox.Scroll.Extent.Width - otherListBox.Scroll.Viewport.Width); var otherMaxY = Math.Abs(otherListBox.Scroll.Extent.Height - otherListBox.Scroll.Viewport.Height); var offset = thisListBox.Scroll.Offset; - if (thisListBox.Scroll.Offset.X > otherMaxX || thisListBox.Scroll.Offset.X == thisMaxX) + if (thisListBox.Scroll.Offset.X > otherMaxX || thisListBox.Scroll.Offset.X.IsNearlyEqual(thisMaxX)) { offset = offset.WithX(otherMaxX); } - if (thisListBox.Scroll.Offset.Y > otherMaxY || thisListBox.Scroll.Offset.Y == thisMaxY) + + if (thisListBox.Scroll.Offset.Y > otherMaxY || thisListBox.Scroll.Offset.Y.IsNearlyEqual(thisMaxY)) { offset = offset.WithY(otherMaxY); } - if (otherListBox.Scroll.Offset.X != offset.X || otherListBox.Scroll.Offset.Y != offset.Y) + + if (!otherListBox.Scroll.Offset.X.IsNearlyEqual(offset.X) || !otherListBox.Scroll.Offset.Y.IsNearlyEqual(offset.Y)) { try { @@ -415,7 +848,8 @@ protected virtual Task SyncScrollAsync(ListBox thisListBox, ListBox otherListBox logger.Error(ex); } } - return Task.FromResult(true); + + return Task.CompletedTask; } /// @@ -426,71 +860,69 @@ protected virtual Task SyncScrollAsync(ListBox thisListBox, ListBox otherListBox private List GetActionsMenuItems(bool leftSide) { var menuItems = new List(); - if (ViewModel.EditorAvailable) + if (ViewModel!.EditorAvailable) { - menuItems.Add(new MenuItem() - { - Header = ViewModel.Editor, - Command = ViewModel.EditorCommand, - CommandParameter = !leftSide - }); - menuItems.Add(new MenuItem() - { - Header = "-" - }); + menuItems.Add(new MenuItem { Header = ViewModel.Editor, Command = ViewModel.EditorCommand, CommandParameter = !leftSide }); + menuItems.Add(new MenuItem { Header = ViewModel.ToggleMergeTypeCaption, Command = ViewModel.ToggleMergeTypeCommand }); + menuItems.Add(new MenuItem { Header = "-" }); } - var mainEditingItems = new List() + var mainEditingItems = new List { - new MenuItem() - { - Header = ViewModel.NextConflict, - Command = ViewModel.NextConflictCommand, - CommandParameter = leftSide - }, - new MenuItem() - { - Header = ViewModel.PrevConflict, - Command = ViewModel.PrevConflictCommand, - CommandParameter = leftSide - }, - new MenuItem() - { - Header = "-" - }, - new MenuItem() - { - Header = ViewModel.CopyText, - Command = ViewModel.CopyTextCommand, - CommandParameter = leftSide - }, - new MenuItem() + new() { Header = ViewModel.NextConflict, Command = ViewModel.NextConflictCommand, CommandParameter = leftSide }, + new() { Header = ViewModel.PrevConflict, Command = ViewModel.PrevConflictCommand, CommandParameter = leftSide }, + new() { Header = "-" }, + new() { Header = ViewModel.CopyText, Command = ViewModel.CopyTextCommand, CommandParameter = leftSide }, + new() { Header = "-" // Separator magic string, and it's documented... NOT really!!! }, - new MenuItem() + new() { Header = ViewModel.CopyAll, - Command = ViewModel.CopyAllCommand, - CommandParameter = leftSide + Command = ReactiveCommand.Create(() => + { + var visibleLine = diffLeft.GetMiddleVisibleLine(); + ViewModel.CopyAllCommand.Execute(leftSide).Subscribe(); + diffLeft.ScrollToLine(visibleLine); + diffRight.ScrollToLine(visibleLine); + ViewModel!.DiffEditorTopLine = diffLeft.GetTopVisibleLine(); + ViewModel.DiffEditorBottomLine = diffLeft.GetBottomVisibleLine(); + if (!(ViewModel.DiffEditorTopLine <= visibleLine && ViewModel.DiffEditorBottomLine >= visibleLine)) + { + diffLeft.TextArea.Caret.Location = new TextLocation(visibleLine, 1); + diffRight.TextArea.Caret.Location = new TextLocation(visibleLine, 1); + } + + RedrawEditorDiffs(); + }) }, - new MenuItem() + new() { Header = ViewModel.CopyThis, - Command = ViewModel.CopyThisCommand, - CommandParameter = leftSide + Command = ReactiveCommand.Create(() => + { + ViewModel.CopyThisCommand.Execute(leftSide).Subscribe(); + RedrawEditorDiffs(); + }) }, - new MenuItem() + new() { Header = ViewModel.CopyThisBeforeLine, - Command = ViewModel.CopyThisBeforeLineCommand, - CommandParameter = leftSide + Command = ReactiveCommand.Create(() => + { + ViewModel.CopyThisBeforeLineCommand.Execute(leftSide).Subscribe(); + RedrawEditorDiffs(); + }) }, - new MenuItem() + new() { Header = ViewModel.CopyThisAfterLine, - Command = ViewModel.CopyThisAfterLineCommand, - CommandParameter = leftSide + Command = ReactiveCommand.Create(() => + { + ViewModel.CopyThisAfterLineCommand.Execute(leftSide).Subscribe(); + RedrawEditorDiffs(); + }) } }; menuItems.AddRange(mainEditingItems); @@ -506,72 +938,24 @@ private List GetActionsMenuItems(bool leftSide) private List GetEditableMenuItems(bool leftSide) { var menuItems = new List(); - if (ViewModel.EditorAvailable) + if (ViewModel!.EditorAvailable) { - menuItems.Add(new MenuItem() - { - Header = ViewModel.Editor, - Command = ViewModel.EditorCommand, - CommandParameter = leftSide - }); - menuItems.Add(new MenuItem() - { - Header = "-" - }); + menuItems.Add(new MenuItem { Header = ViewModel.Editor, Command = ViewModel.EditorCommand, CommandParameter = leftSide }); + menuItems.Add(new MenuItem { Header = ViewModel.ToggleMergeTypeCaption, Command = ViewModel.ToggleMergeTypeCommand }); + menuItems.Add(new MenuItem { Header = "-" }); } - var mainEditingItems = new List() + var mainEditingItems = new List { - new MenuItem() - { - Header = ViewModel.NextConflict, - Command = ViewModel.NextConflictCommand, - CommandParameter = leftSide - }, - new MenuItem() - { - Header = ViewModel.PrevConflict, - Command = ViewModel.PrevConflictCommand, - CommandParameter = leftSide - }, - new MenuItem() - { - Header = "-" - }, - new MenuItem() - { - Header = ViewModel.EditThis, - Command = ViewModel.EditThisCommand, - CommandParameter = leftSide - }, - new MenuItem() - { - Header = ViewModel.CopyText, - Command = ViewModel.CopyTextCommand, - CommandParameter = leftSide - }, - new MenuItem() - { - Header = "-" - }, - new MenuItem() - { - Header = ViewModel.DeleteText, - Command = ViewModel.DeleteTextCommand, - CommandParameter = leftSide - }, - new MenuItem() - { - Header = ViewModel.MoveUp, - Command = ViewModel.MoveUpCommand, - CommandParameter = leftSide - }, - new MenuItem() - { - Header = ViewModel.MoveDown, - Command = ViewModel.MoveDownCommand, - CommandParameter = leftSide - }, + new() { Header = ViewModel.NextConflict, Command = ViewModel.NextConflictCommand, CommandParameter = leftSide }, + new() { Header = ViewModel.PrevConflict, Command = ViewModel.PrevConflictCommand, CommandParameter = leftSide }, + new() { Header = "-" }, + new() { Header = ViewModel.EditThis, Command = ViewModel.EditThisCommand, CommandParameter = leftSide }, + new() { Header = ViewModel.CopyText, Command = ViewModel.CopyTextCommand, CommandParameter = leftSide }, + new() { Header = "-" }, + new() { Header = ViewModel.DeleteText, Command = ViewModel.DeleteTextCommand, CommandParameter = leftSide }, + new() { Header = ViewModel.MoveUp, Command = ViewModel.MoveUpCommand, CommandParameter = leftSide }, + new() { Header = ViewModel.MoveDown, Command = ViewModel.MoveDownCommand, CommandParameter = leftSide } }; menuItems.AddRange(mainEditingItems); @@ -579,78 +963,79 @@ private List GetEditableMenuItems(bool leftSide) var undoAvailable = ViewModel.IsUndoAvailable(); if (redoAvailable || undoAvailable) { - menuItems.Add(new MenuItem() - { - Header = "-" - }); + menuItems.Add(new MenuItem { Header = "-" }); if (undoAvailable) { - menuItems.Add(new MenuItem() + menuItems.Add(new MenuItem { Header = ViewModel.Undo, - Command = ViewModel.UndoCommand, - CommandParameter = leftSide + Command = ReactiveCommand.Create(() => + { + var visibleLine = diffLeft.GetMiddleVisibleLine(); + ViewModel.UndoCommand.Execute(leftSide).Subscribe(); + diffLeft.ScrollToLine(visibleLine); + diffRight.ScrollToLine(visibleLine); + ViewModel!.DiffEditorTopLine = diffLeft.GetTopVisibleLine(); + ViewModel.DiffEditorBottomLine = diffLeft.GetBottomVisibleLine(); + if (!(ViewModel.DiffEditorTopLine <= visibleLine && ViewModel.DiffEditorBottomLine >= visibleLine)) + { + diffLeft.TextArea.Caret.Location = new TextLocation(visibleLine, 1); + diffRight.TextArea.Caret.Location = new TextLocation(visibleLine, 1); + } + + RedrawEditorDiffs(); + }) }); } + if (redoAvailable) { - menuItems.Add(new MenuItem() + menuItems.Add(new MenuItem { Header = ViewModel.Redo, - Command = ViewModel.RedoCommand, - CommandParameter = leftSide + Command = ReactiveCommand.Create(() => + { + var visibleLine = diffLeft.GetMiddleVisibleLine(); + ViewModel.RedoCommand.Execute(leftSide).Subscribe(); + diffLeft.ScrollToLine(visibleLine); + diffRight.ScrollToLine(visibleLine); + ViewModel!.DiffEditorTopLine = diffLeft.GetTopVisibleLine(); + ViewModel.DiffEditorBottomLine = diffLeft.GetBottomVisibleLine(); + if (!(ViewModel.DiffEditorTopLine <= visibleLine && ViewModel.DiffEditorBottomLine >= visibleLine)) + { + diffLeft.TextArea.Caret.Location = new TextLocation(visibleLine, 1); + diffRight.TextArea.Caret.Location = new TextLocation(visibleLine, 1); + } + + RedrawEditorDiffs(); + }) }); } } + return menuItems; } /// - /// Gets the non editable menu items. + /// Gets the non-editable menu items. /// /// The left side. /// System.Collections.Generic.List<Avalonia.Controls.MenuItem>. private List GetNonEditableMenuItems(bool leftSide) { var menuItems = new List(); - if (ViewModel.EditorAvailable) + if (ViewModel!.EditorAvailable) { - menuItems.Add(new MenuItem() - { - Header = ViewModel.ReadOnlyEditor, - Command = ViewModel.ReadOnlyEditorCommand, - CommandParameter = leftSide - }); - menuItems.Add(new MenuItem() - { - Header = "-" - }); + menuItems.Add(new MenuItem { Header = ViewModel.ReadOnlyEditor, Command = ViewModel.ReadOnlyEditorCommand, CommandParameter = leftSide }); + menuItems.Add(new MenuItem { Header = "-" }); } - var mainEditingItems = new List() + var mainEditingItems = new List { - new MenuItem() - { - Header = ViewModel.NextConflict, - Command = ViewModel.NextConflictCommand, - CommandParameter = leftSide - }, - new MenuItem() - { - Header = ViewModel.PrevConflict, - Command = ViewModel.PrevConflictCommand, - CommandParameter = leftSide - }, - new MenuItem() - { - Header = "-" - }, - new MenuItem() - { - Header = ViewModel.CopyText, - Command = ViewModel.CopyTextCommand, - CommandParameter = leftSide - } + new() { Header = ViewModel.NextConflict, Command = ViewModel.NextConflictCommand, CommandParameter = leftSide }, + new() { Header = ViewModel.PrevConflict, Command = ViewModel.PrevConflictCommand, CommandParameter = leftSide }, + new() { Header = "-" }, + new() { Header = ViewModel.CopyText, Command = ViewModel.CopyTextCommand, CommandParameter = leftSide } }; menuItems.AddRange(mainEditingItems); @@ -662,19 +1047,21 @@ private List GetNonEditableMenuItems(bool leftSide) /// /// The text editor. /// The search panel. - /// if set to true [is replace mode]. - private void HandleEditorFindOrReplace(IronyModManager.Controls.TextEditor textEditor, AvaloniaEdit.Search.SearchPanel searchPanel, bool isReplaceMode) + /// if set to true [if replace mode]. + private void HandleEditorFindOrReplace(IronyModManager.Controls.TextEditor textEditor, SearchPanel searchPanel, bool isReplaceMode) { if (searchPanel == null || textEditor == null) { return; } + searchPanel.IsReplaceMode = isReplaceMode; searchPanel.Open(); if (!(textEditor.TextArea.Selection.IsEmpty || textEditor.TextArea.Selection.IsMultiline)) { searchPanel.SearchPattern = textEditor.TextArea.Selection.GetText(); } + Dispatcher.UIThread.Post(searchPanel.Reactivate, DispatcherPriority.Input); } @@ -686,74 +1073,42 @@ private void InitializeComponent() AvaloniaXamlLoader.Load(this); } + /// + /// Redraws an editor diffs. + /// + private void RedrawEditorDiffs() + { + diffLeft.TextArea.TextView.Redraw(); + diffRight.TextArea.TextView.Redraw(); + } + /// /// Sets the editor context menu. /// /// The text editor. /// The search panel. - private void SetEditorContextMenu(IronyModManager.Controls.TextEditor textEditor, AvaloniaEdit.Search.SearchPanel searchPanel) + private void SetEditorContextMenu(IronyModManager.Controls.TextEditor textEditor, SearchPanel searchPanel) { if (textEditor == null || searchPanel == null) { return; } + var ctx = new MenuFlyout { - Items = new List() + Items = new List { - new MenuItem() - { - Header = ViewModel.EditorCopy, - Command = ReactiveCommand.Create(() => textEditor.Copy()).DisposeWith(Disposables) - }, - new MenuItem() - { - Header = ViewModel.EditorCut, - Command = ReactiveCommand.Create(() => textEditor.Cut()).DisposeWith(Disposables) - }, - new MenuItem() - { - Header = ViewModel.EditorPaste, - Command = ReactiveCommand.Create(() => textEditor.Paste()).DisposeWith(Disposables) - }, - new MenuItem() - { - Header = ViewModel.EditorDelete, - Command = ReactiveCommand.Create(() => textEditor.Delete()).DisposeWith(Disposables) - }, - new MenuItem() - { - Header = ViewModel.EditorSelectAll, - Command = ReactiveCommand.Create(() => textEditor.SelectAll()).DisposeWith(Disposables) - }, - new MenuItem() - { - Header = "-" - }, - new MenuItem() - { - Header = ViewModel.EditorUndo, - Command = ReactiveCommand.Create(() => textEditor.Undo()).DisposeWith(Disposables) - }, - new MenuItem() - { - Header = ViewModel.EditorRedo, - Command = ReactiveCommand.Create(() => textEditor.Redo()).DisposeWith(Disposables) - }, - new MenuItem() - { - Header = "-" - }, - new MenuItem() - { - Header = ViewModel.EditorFind, - Command = ReactiveCommand.Create(() => HandleEditorFindOrReplace(textEditor, searchPanel, false)).DisposeWith(Disposables) - }, - new MenuItem() - { - Header = ViewModel.EditorReplace, - Command = ReactiveCommand.Create(() => HandleEditorFindOrReplace(textEditor, searchPanel, true)).DisposeWith(Disposables) - }, + new() { Header = ViewModel!.EditorCopy, Command = ReactiveCommand.Create(textEditor.Copy).DisposeWith(Disposables) }, + new() { Header = ViewModel.EditorCut, Command = ReactiveCommand.Create(textEditor.Cut).DisposeWith(Disposables) }, + new() { Header = ViewModel.EditorPaste, Command = ReactiveCommand.Create(textEditor.Paste).DisposeWith(Disposables) }, + new() { Header = ViewModel.EditorDelete, Command = ReactiveCommand.Create(textEditor.Delete).DisposeWith(Disposables) }, + new() { Header = ViewModel.EditorSelectAll, Command = ReactiveCommand.Create(textEditor.SelectAll).DisposeWith(Disposables) }, + new() { Header = "-" }, + new() { Header = ViewModel.EditorUndo, Command = ReactiveCommand.Create(textEditor.Undo).DisposeWith(Disposables) }, + new() { Header = ViewModel.EditorRedo, Command = ReactiveCommand.Create(textEditor.Redo).DisposeWith(Disposables) }, + new() { Header = "-" }, + new() { Header = ViewModel.EditorFind, Command = ReactiveCommand.Create(() => HandleEditorFindOrReplace(textEditor, searchPanel, false)).DisposeWith(Disposables) }, + new() { Header = ViewModel.EditorReplace, Command = ReactiveCommand.Create(() => HandleEditorFindOrReplace(textEditor, searchPanel, true)).DisposeWith(Disposables) } } }; textEditor.ContextFlyout = ctx; diff --git a/src/IronyModManager/Views/Controls/OptionsControlView.xaml b/src/IronyModManager/Views/Controls/OptionsControlView.xaml index cd77e5fdc..6cf3c57a0 100644 --- a/src/IronyModManager/Views/Controls/OptionsControlView.xaml +++ b/src/IronyModManager/Views/Controls/OptionsControlView.xaml @@ -9,53 +9,53 @@ + Foreground="{DynamicResource IronyAccentBrush}" x:Name="openPopupButton" /> - - + + - + - - - - - - + HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" MaxHeight="30" /> + + + - - - - - - - - + HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" /> + + + - - - + + + + HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" /> + HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" MaxHeight="30" /> + + + + + + + + + + + + + + + + + + + + + + + - + - - + + + HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" MaxHeight="30" /> - + @@ -101,24 +124,24 @@ - + - - + + - + HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" MaxHeight="30" /> - +