diff --git a/Fronter.NET.sln.DotSettings b/Fronter.NET.sln.DotSettings
index f6bf17a8..d28ae2fc 100644
--- a/Fronter.NET.sln.DotSettings
+++ b/Fronter.NET.sln.DotSettings
@@ -21,4 +21,4 @@
True
True
True
- True
\ No newline at end of file
+ True
diff --git a/Fronter.NET/Models/Configuration/Config.cs b/Fronter.NET/Models/Configuration/Config.cs
index 824139c7..2174b0be 100644
--- a/Fronter.NET/Models/Configuration/Config.cs
+++ b/Fronter.NET/Models/Configuration/Config.cs
@@ -1,12 +1,15 @@
using Avalonia.Controls.ApplicationLifetimes;
using commonItems;
using Fronter.Models.Configuration.Options;
+using Fronter.Models.Database;
+using Fronter.Services;
using Fronter.ViewModels;
using log4net;
using Sentry;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.Globalization;
using System.IO;
using System.Linq;
@@ -20,8 +23,9 @@ public class Config {
public string SourceGame { get; private set; } = string.Empty;
public string TargetGame { get; private set; } = string.Empty;
public string? SentryDsn { get; private set; }
- public string? ModAutoGenerationSource { get; private set; } = null;
- public ObservableCollection AutoLocatedMods { get; } = new();
+ public bool TargetPlaysetSelectionEnabled { get; private set; } = false;
+ public ObservableCollection AutoLocatedPlaysets { get; } = [];
+ public Playset? SelectedPlayset { get; set; }
public bool CopyToTargetGameModDirectory { get; set; } = true;
public ushort ProgressOnCopyingComplete { get; set; } = 109;
public bool UpdateCheckerEnabled { get; private set; } = false;
@@ -92,7 +96,9 @@ private void RegisterKeys(Parser parser) {
parser.RegisterKeyword("displayName", reader => DisplayName = reader.GetString());
parser.RegisterKeyword("sourceGame", reader => SourceGame = reader.GetString());
parser.RegisterKeyword("targetGame", reader => TargetGame = reader.GetString());
- parser.RegisterKeyword("autoGenerateModsFrom", reader => ModAutoGenerationSource = reader.GetString());
+ parser.RegisterKeyword("targetPlaysetSelectionEnabled", reader => {
+ TargetPlaysetSelectionEnabled = reader.GetBool();
+ });
parser.RegisterKeyword("copyToTargetGameModDirectory", reader => {
CopyToTargetGameModDirectory = reader.GetString().Equals("true");
});
@@ -162,8 +168,8 @@ private void InitSentry(string dsn) {
private void RegisterPreloadKeys(Parser parser) {
parser.RegisterRegex(CommonRegexes.String, (reader, incomingKey) => {
- var valueStringOfItem = reader.GetStringOfItem();
- var valueStr = valueStringOfItem.ToString().RemQuotes();
+ StringOfItem valueStringOfItem = reader.GetStringOfItem();
+ string valueStr = valueStringOfItem.ToString().RemQuotes();
var valueReader = new BufferedReader(valueStr);
foreach (var folder in RequiredFolders) {
@@ -187,13 +193,6 @@ private void RegisterPreloadKeys(Parser parser) {
option.SetCheckBoxSelectorPreloaded();
}
}
- if (incomingKey.Equals("selectedMods")) {
- var theList = valueReader.GetStrings();
- var matchingMods = AutoLocatedMods.Where(m => theList.Contains(m.FileName));
- foreach (var mod in matchingMods) {
- mod.Enabled = true;
- }
- }
});
parser.RegisterRegex(CommonRegexes.Catchall, ParserHelpers.IgnoreAndLogItem);
}
@@ -223,7 +222,7 @@ private void InitializeFolders(string documentsDir) {
if (uint.TryParse(folder.SteamGameId, out uint steamId)) {
possiblePath = CommonFunctions.GetSteamInstallPath(steamId);
}
- if (possiblePath is null && long.TryParse(folder.GOGGameId, out long gogId)) {
+ if (possiblePath is null && long.TryParse(folder.GOGGameId, CultureInfo.InvariantCulture, out long gogId)) {
possiblePath = CommonFunctions.GetGOGInstallPath(gogId);
}
@@ -242,10 +241,6 @@ private void InitializeFolders(string documentsDir) {
if (Directory.Exists(initialValue)) {
folder.Value = initialValue;
}
-
- if (folder.Name.Equals(ModAutoGenerationSource)) {
- AutoLocateMods();
- }
}
}
@@ -318,15 +313,9 @@ public bool ExportConfiguration() {
}
writer.WriteLine($"{file.Name} = \"{file.Value}\"");
}
-
- if (ModAutoGenerationSource is not null) {
- writer.WriteLine("selectedMods = {");
- foreach (var mod in AutoLocatedMods) {
- if (mod.Enabled) {
- writer.WriteLine($"\t\"{mod.FileName}\"");
- }
- }
- writer.WriteLine("}");
+
+ if (SelectedPlayset is not null) {
+ writer.WriteLine($"selectedPlayset = {SelectedPlayset.Id}");
}
foreach (var option in Options) {
@@ -360,72 +349,32 @@ private static void SetSavingStatus(string locKey) {
}
}
- public void AutoLocateMods() {
- logger.Debug("Clearing previously located mods...");
- AutoLocatedMods.Clear();
- logger.Debug("Autolocating mods...");
-
- // Do we have a mod path?
- string? modPath = null;
- foreach (var folder in RequiredFolders) {
- if (folder.Name.Equals(ModAutoGenerationSource)) {
- modPath = folder.Value;
- }
- }
- if (modPath is null) {
- logger.Warn("No folder found as source for mods autolocation.");
- return;
- }
-
- // Does it exist?
- if (!Directory.Exists(modPath)) {
- logger.Warn($"Mod path \"{modPath}\" does not exist or can not be accessed!");
- return;
- }
-
- // Are we looking at documents directory?
- var combinedPath = Path.Combine(modPath, "mod");
- if (Directory.Exists(combinedPath)) {
- modPath = combinedPath;
- }
- logger.Debug($"Mods autolocation path set to: \"{modPath}\"");
-
- // Are there mods inside?
- var validModFiles = new List();
- foreach (var file in SystemUtils.GetAllFilesInFolder(modPath)) {
- var lastDot = file.LastIndexOf('.');
- if (lastDot == -1) {
- continue;
- }
-
- var extension = CommonFunctions.GetExtension(file);
- if (!extension.Equals("mod")) {
- continue;
- }
-
- validModFiles.Add(file);
- }
-
- if (validModFiles.Count == 0) {
- logger.Debug($"No mod files could be found in \"{modPath}\"");
- return;
+ public string? TargetGameModsPath {
+ get {
+ var targetGameModPath = RequiredFolders
+ .FirstOrDefault(f => f?.Name == "targetGameModPath", defaultValue: null);
+ return targetGameModPath?.Value;
}
+ }
- foreach (var modFile in validModFiles) {
- var path = Path.Combine(modPath, modFile);
- Mod theMod;
- try {
- theMod = new Mod(path);
- } catch (IOException ex) {
- logger.Warn($"Failed to parse mod file {modFile}: {ex.Message}");
- continue;
- }
- if (string.IsNullOrEmpty(theMod.Name)) {
- logger.Warn($"Mod at \"{path}\" has no defined name, skipping.");
- continue;
+ public void AutoLocatePlaysets() {
+ logger.Debug("Clearing previously located playsets...");
+ AutoLocatedPlaysets.Clear();
+ logger.Debug("Autolocating playsets...");
+
+ var destModsFolder = TargetGameModsPath;
+ var locatedPlaysetsCount = 0;
+ if (destModsFolder is not null) {
+ var dbContext = TargetDbManager.GetLauncherDbContext(this);
+ if (dbContext is not null) {
+ foreach (var playset in dbContext.Playsets.Where(p => p.IsRemoved == null || p.IsRemoved == false )) {
+ AutoLocatedPlaysets.Add(playset);
+ }
}
- AutoLocatedMods.Add(theMod);
+
+ locatedPlaysetsCount = AutoLocatedPlaysets.Count;
}
- logger.Debug($"Autolocated {AutoLocatedMods.Count} mods");
+
+ logger.Debug($"Autolocated {locatedPlaysetsCount} playsets.");
}
}
\ No newline at end of file
diff --git a/Fronter.NET/Models/Configuration/Mod.cs b/Fronter.NET/Models/Configuration/Mod.cs
deleted file mode 100644
index 9c3a71de..00000000
--- a/Fronter.NET/Models/Configuration/Mod.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using commonItems;
-using Fronter.ViewModels;
-
-namespace Fronter.Models.Configuration;
-
-public class Mod : ViewModelBase {
- public Mod(string modPath) {
- var parser = new Parser();
- parser.RegisterKeyword("name", reader => Name = reader.GetString());
- parser.IgnoreUnregisteredItems();
-
- parser.ParseFile(modPath);
- FileName = CommonFunctions.TrimPath(modPath);
- }
- public string Name { get; private set; } = string.Empty;
- public string FileName { get; private set; }
- public bool Enabled { get; set; } = false;
-}
\ No newline at end of file
diff --git a/Fronter.NET/Models/Configuration/RequiredFolder.cs b/Fronter.NET/Models/Configuration/RequiredFolder.cs
index d4f06e73..5cccd839 100644
--- a/Fronter.NET/Models/Configuration/RequiredFolder.cs
+++ b/Fronter.NET/Models/Configuration/RequiredFolder.cs
@@ -44,9 +44,9 @@ public override string Value {
base.Value = value;
logger.Info($"{TranslationSource.Instance[DisplayName]} set to: {value}");
-
- if (Name == config.ModAutoGenerationSource) {
- config.AutoLocateMods();
+
+ if (config.TargetPlaysetSelectionEnabled) {
+ config.AutoLocatePlaysets();
}
}
}
diff --git a/Fronter.NET/Resources/Configuration/converter_l_english.yml b/Fronter.NET/Resources/Configuration/converter_l_english.yml
index 60cd6280..13a7636f 100644
--- a/Fronter.NET/Resources/Configuration/converter_l_english.yml
+++ b/Fronter.NET/Resources/Configuration/converter_l_english.yml
@@ -30,6 +30,7 @@ l_english:
OPTIONSTAB: "Options"
CONVERTTAB: "Convert"
MODSTAB: "Mods"
+ TARGET_PLAYSET_TAB: "Target Playset"
MODSDISABLED: "Mod autodetection disabled by configuration/not required."
MODSNOTFOUND: "Mods not found in mod directory."
MODSFOUND: "Mods autodetected:"
diff --git a/Fronter.NET/Services/ModCopier.cs b/Fronter.NET/Services/ModCopier.cs
index 36f70fcb..bc9eab94 100644
--- a/Fronter.NET/Services/ModCopier.cs
+++ b/Fronter.NET/Services/ModCopier.cs
@@ -3,7 +3,6 @@
using Fronter.Models.Database;
using log4net;
using System;
-using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -32,13 +31,11 @@ public bool CopyMod() {
return false;
}
- var requiredFolders = config.RequiredFolders;
- var targetGameModPath = requiredFolders.FirstOrDefault(f => f?.Name == "targetGameModPath", null);
- if (targetGameModPath is null) {
+ string? destModsFolder = config.TargetGameModsPath;
+ if (destModsFolder is null) {
logger.Error("Copy failed - Target Folder isn't loaded!");
return false;
}
- var destModsFolder = targetGameModPath.Value;
if (!Directory.Exists(destModsFolder)) {
logger.Error("Copy failed - Target Folder does not exist!");
return false;
@@ -144,7 +141,7 @@ private void CreatePlayset(string targetModsDirectory, string modName, string de
logger.Warn($"Couldn't get parent directory of \"{targetModsDirectory}\".");
return;
}
- var latestDbFilePath = GetLastUpdatedLauncherDbPath(gameDocsDirectory);
+ var latestDbFilePath = TargetDbManager.GetLastUpdatedLauncherDbPath(gameDocsDirectory);
if (latestDbFilePath is null) {
logger.Debug("Launcher's database not found.");
return;
@@ -234,16 +231,6 @@ private void CreatePlayset(string targetModsDirectory, string modName, string de
}
}
- private static string? GetLastUpdatedLauncherDbPath(string gameDocsDirectory) {
- var possibleDbFileNames = new List { "launcher-v2.sqlite", "launcher-v2_openbeta.sqlite" };
- var latestDbFilePath = possibleDbFileNames
- .Select(name => Path.Join(gameDocsDirectory, name))
- .Where(File.Exists)
- .OrderByDescending(File.GetLastWriteTimeUtc)
- .FirstOrDefault(defaultValue: null);
- return latestDbFilePath;
- }
-
// Returns saved mod.
private Mod AddModToDb(LauncherDbContext dbContext, string modName, string gameRegistryId, string dirPath) {
logger.Debug($"Saving mod \"{modName}\" to DB...");
diff --git a/Fronter.NET/Services/TargetDbManager.cs b/Fronter.NET/Services/TargetDbManager.cs
new file mode 100644
index 00000000..835c39c4
--- /dev/null
+++ b/Fronter.NET/Services/TargetDbManager.cs
@@ -0,0 +1,37 @@
+using Fronter.Models.Configuration;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace Fronter.Services;
+
+public class TargetDbManager {
+ public static string? GetLastUpdatedLauncherDbPath(string gameDocsDirectory) {
+ var possibleDbFileNames = new List { "launcher-v2.sqlite", "launcher-v2_openbeta.sqlite" };
+ var latestDbFilePath = possibleDbFileNames
+ .Select(name => Path.Join(gameDocsDirectory, name))
+ .Where(File.Exists)
+ .OrderByDescending(File.GetLastWriteTimeUtc)
+ .FirstOrDefault(defaultValue: null);
+ return latestDbFilePath;
+ }
+
+ public static LauncherDbContext? GetLauncherDbContext(Config config) {
+ var targetGameModsPath = config.TargetGameModsPath;
+ if (string.IsNullOrWhiteSpace(targetGameModsPath)) {
+ return null;
+ }
+ var gameDocsDirectory = Directory.GetParent(targetGameModsPath)?.FullName;
+ if (gameDocsDirectory is null) {
+ return null;
+ }
+
+ var dbPath = GetLastUpdatedLauncherDbPath(gameDocsDirectory);
+ if (dbPath is null) {
+ return null;
+ }
+
+ string connectionString = $"Data Source={dbPath};";
+ return new LauncherDbContext(connectionString);
+ }
+}
\ No newline at end of file
diff --git a/Fronter.NET/ViewModels/MainWindowViewModel.cs b/Fronter.NET/ViewModels/MainWindowViewModel.cs
index bc452ff0..340f3ba4 100644
--- a/Fronter.NET/ViewModels/MainWindowViewModel.cs
+++ b/Fronter.NET/ViewModels/MainWindowViewModel.cs
@@ -59,8 +59,8 @@ public sealed class MainWindowViewModel : ViewModelBase {
internal Config Config { get; }
internal PathPickerViewModel PathPicker { get; }
- internal ModsPickerViewModel ModsPicker { get; }
- public bool ModsPickerTabVisible => Config.ModAutoGenerationSource is not null;
+ internal TargetPlaysetPickerViewModel TargetPlaysetPicker { get; }
+ public bool TargetPlaysetPickerTabVisible => Config.TargetPlaysetSelectionEnabled;
public OptionsViewModel Options { get; }
public bool OptionsTabVisible => Options.Items.Any();
@@ -104,7 +104,7 @@ public MainWindowViewModel(DataGrid logGrid) {
LogGridAppender.LogGrid = logGrid;
PathPicker = new PathPickerViewModel(Config);
- ModsPicker = new ModsPickerViewModel(Config);
+ TargetPlaysetPicker = new TargetPlaysetPickerViewModel(Config);
Options = new OptionsViewModel(Config.Options);
// Create reactive commands.
diff --git a/Fronter.NET/ViewModels/ModsPickerViewModel.cs b/Fronter.NET/ViewModels/ModsPickerViewModel.cs
deleted file mode 100644
index 2a80d0c9..00000000
--- a/Fronter.NET/ViewModels/ModsPickerViewModel.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using DynamicData;
-using DynamicData.Binding;
-using Fronter.Models.Configuration;
-using System;
-using System.Collections.ObjectModel;
-
-namespace Fronter.ViewModels;
-
-///
-/// The ModsPickerViewModel lets the user select paths to various stuff the converter needs to know where to find.
-///
-internal sealed class ModsPickerViewModel : ViewModelBase {
- public ModsPickerViewModel(Config config) {
- config.AutoLocatedMods.ToObservableChangeSet()
- .Bind(out autoLocatedMods)
- .Subscribe();
-
- if (config.ModAutoGenerationSource is null) {
- ModsDisabled = true;
- }
- }
-
- private readonly ReadOnlyObservableCollection autoLocatedMods;
- public ReadOnlyObservableCollection AutoLocatedMods => autoLocatedMods;
-
- public bool ModsDisabled { get; } = false;
-}
\ No newline at end of file
diff --git a/Fronter.NET/ViewModels/TargetPlaysetPickerViewModel.cs b/Fronter.NET/ViewModels/TargetPlaysetPickerViewModel.cs
new file mode 100644
index 00000000..3074f81b
--- /dev/null
+++ b/Fronter.NET/ViewModels/TargetPlaysetPickerViewModel.cs
@@ -0,0 +1,42 @@
+using DynamicData;
+using DynamicData.Binding;
+using Fronter.Models.Configuration;
+using Fronter.Models.Database;
+using System;
+using System.Collections.ObjectModel;
+
+namespace Fronter.ViewModels;
+
+///
+/// The TargetPlaysetPickerViewModel lets the user select paths to various stuff the converter needs to know where to find.
+///
+public class TargetPlaysetPickerViewModel : ViewModelBase {
+ private Config config;
+
+ public TargetPlaysetPickerViewModel(Config config) {
+ this.config = config;
+
+ config.AutoLocatedPlaysets.ToObservableChangeSet()
+ .Bind(out targetPlaysets)
+ .Subscribe();
+
+ if (!config.TargetPlaysetSelectionEnabled) {
+ TabDisabled = true;
+ }
+ }
+
+ private readonly ReadOnlyObservableCollection targetPlaysets;
+ public ReadOnlyObservableCollection TargetPlaysets => targetPlaysets;
+
+ public bool TabDisabled { get; } = false;
+
+ public void ReloadPlaysets() {
+ config.SelectedPlayset = null;
+ config.AutoLocatePlaysets();
+ }
+
+ public Playset? SelectedPlayset {
+ get => config.SelectedPlayset;
+ set => config.SelectedPlayset = value;
+ }
+}
\ No newline at end of file
diff --git a/Fronter.NET/Views/MainWindow.axaml b/Fronter.NET/Views/MainWindow.axaml
index f90d7f09..febf25a3 100644
--- a/Fronter.NET/Views/MainWindow.axaml
+++ b/Fronter.NET/Views/MainWindow.axaml
@@ -78,9 +78,9 @@
-
+
-
+
diff --git a/Fronter.NET/Views/ModsPickerView.axaml b/Fronter.NET/Views/TargetPlaysetPickerView.axaml
similarity index 57%
rename from Fronter.NET/Views/ModsPickerView.axaml
rename to Fronter.NET/Views/TargetPlaysetPickerView.axaml
index 02dd8114..08659380 100644
--- a/Fronter.NET/Views/ModsPickerView.axaml
+++ b/Fronter.NET/Views/TargetPlaysetPickerView.axaml
@@ -4,13 +4,13 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:viewModels="clr-namespace:Fronter.ViewModels"
xmlns:ns="clr-namespace:Fronter.Extensions"
- x:Class="Fronter.Views.ModsPickerView"
- x:DataType="viewModels:ModsPickerViewModel"
+ x:Class="Fronter.Views.TargetPlaysetPickerView"
+ x:DataType="viewModels:TargetPlaysetPickerViewModel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
-
+
@@ -37,23 +37,27 @@
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Fronter.NET/Views/ModsPickerView.axaml.cs b/Fronter.NET/Views/TargetPlaysetPickerView.axaml.cs
similarity index 65%
rename from Fronter.NET/Views/ModsPickerView.axaml.cs
rename to Fronter.NET/Views/TargetPlaysetPickerView.axaml.cs
index a05f7e54..b6e9d74b 100644
--- a/Fronter.NET/Views/ModsPickerView.axaml.cs
+++ b/Fronter.NET/Views/TargetPlaysetPickerView.axaml.cs
@@ -3,8 +3,8 @@
namespace Fronter.Views;
-public partial class ModsPickerView : UserControl {
- public ModsPickerView() {
+public partial class TargetPlaysetPickerView : UserControl {
+ public TargetPlaysetPickerView() {
InitializeComponent();
}