• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ParadoxGameConverters / Fronter.NET / 18805147680

25 Oct 2025 03:43PM UTC coverage: 19.491% (+0.2%) from 19.325%
18805147680

Pull #426

github

web-flow
Merge c1fac0087 into a53943d43
Pull Request #426: Target playset selection

106 of 706 branches covered (15.01%)

Branch coverage included in aggregate %.

17 of 90 new or added lines in 9 files covered. (18.89%)

4 existing lines in 1 file now uncovered.

575 of 2788 relevant lines covered (20.62%)

9.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

31.23
/Fronter.NET/Models/Configuration/Config.cs
1
using Avalonia.Controls.ApplicationLifetimes;
2
using commonItems;
3
using Fronter.Models.Configuration.Options;
4
using Fronter.Models.Database;
5
using Fronter.Services;
6
using Fronter.ViewModels;
7
using log4net;
8
using System;
9
using System.Collections.Generic;
10
using System.Collections.ObjectModel;
11
using System.Globalization;
12
using System.IO;
13
using System.Linq;
14

15
namespace Fronter.Models.Configuration;
16

17
internal sealed class Config {
18
        public string Name { get; private set; } = string.Empty;
13✔
19
        public string ConverterFolder { get; private set; } = string.Empty;
29✔
20
        public string BackendExePath { get; private set; } = string.Empty; // relative to ConverterFolder
13✔
21
        public string DisplayName { get; private set; } = string.Empty;
13✔
22
        public string SourceGame { get; private set; } = string.Empty;
13✔
23
        public string TargetGame { get; private set; } = string.Empty;
13✔
24
        public string? SentryDsn { get; private set; }
×
25
        public bool TargetPlaysetSelectionEnabled { get; private set; } = false;
8✔
26
        public ObservableCollection<Playset> AutoLocatedPlaysets { get; } = [];
8✔
NEW
27
        public Playset? SelectedPlayset { get; set; }
×
28
        public bool CopyToTargetGameModDirectory { get; set; } = true;
6✔
29
        public ushort ProgressOnCopyingComplete { get; set; } = 109;
12✔
30
        public bool UpdateCheckerEnabled { get; private set; } = false;
13✔
31
        public bool CheckForUpdatesOnStartup { get; private set; } = false;
13✔
32
        public string ConverterReleaseForumThread { get; private set; } = string.Empty;
13✔
33
        public string LatestGitHubConverterReleaseUrl { get; private set; } = string.Empty;
13✔
34
        public string PagesCommitIdUrl { get; private set; } = string.Empty;
13✔
35
        public List<RequiredFile> RequiredFiles { get; } = [];
135✔
36
        public List<RequiredFolder> RequiredFolders { get; } = [];
153✔
37
        public List<Option> Options { get; } = [];
192✔
38
        private int optionCounter;
39

40
        private static readonly ILog logger = LogManager.GetLogger("Configuration");
1✔
41

42
        public Config() {
12✔
43
                var parser = new Parser();
6✔
44
                RegisterKeys(parser);
6✔
45
                var fronterConfigurationPath = Path.Combine("Configuration", "fronter-configuration.txt");
6✔
46
                if (File.Exists(fronterConfigurationPath)) {
12!
47
                        parser.ParseFile(fronterConfigurationPath);
6✔
48
                        logger.Info("Frontend configuration loaded.");
6✔
49
                } else {
6✔
50
                        logger.Warn($"{fronterConfigurationPath} not found!");
×
51
                }
×
52

53
                var fronterOptionsPath = Path.Combine("Configuration", "fronter-options.txt");
6✔
54
                if (File.Exists(fronterOptionsPath)) {
12!
55
                        parser.ParseFile(fronterOptionsPath);
6✔
56
                        logger.Info("Frontend options loaded.");
6✔
57
                } else {
6✔
58
                        logger.Warn($"{fronterOptionsPath} not found!");
×
59
                }
×
60

61
                InitializePaths();
6✔
62

63
                LoadExistingConfiguration();
6✔
64
        }
6✔
65

66
        private void RegisterKeys(Parser parser) {
6✔
67
                parser.RegisterKeyword("name", reader => Name = reader.GetString());
12✔
68
                parser.RegisterKeyword("sentryDsn", reader => SentryDsn = reader.GetString());
6✔
69
                parser.RegisterKeyword("converterFolder", reader => ConverterFolder = reader.GetString());
12✔
70
                parser.RegisterKeyword("backendExePath", reader => BackendExePath = reader.GetString());
12✔
71
                parser.RegisterKeyword("requiredFolder", reader => {
30✔
72
                        var newFolder = new RequiredFolder(reader, this);
24✔
73
                        if (!string.IsNullOrEmpty(newFolder.Name)) {
48✔
74
                                RequiredFolders.Add(newFolder);
24✔
75
                        } else {
24✔
76
                                logger.Error("Required Folder has no mandatory field: name!");
×
77
                        }
×
78
                });
30✔
79
                parser.RegisterKeyword("requiredFile", reader => {
12✔
80
                        var newFile = new RequiredFile(reader);
6✔
81
                        if (!string.IsNullOrEmpty(newFile.Name)) {
12✔
82
                                RequiredFiles.Add(newFile);
6✔
83
                        } else {
6✔
84
                                logger.Error("Required File has no mandatory field: name!");
×
85
                        }
×
86
                });
12✔
87
                parser.RegisterKeyword("option", reader => {
60✔
88
                        var newOption = new Option(reader, ++optionCounter);
54✔
89
                        Options.Add(newOption);
54✔
90
                });
60✔
91
                parser.RegisterKeyword("displayName", reader => DisplayName = reader.GetString());
12✔
92
                parser.RegisterKeyword("sourceGame", reader => SourceGame = reader.GetString());
12✔
93
                parser.RegisterKeyword("targetGame", reader => TargetGame = reader.GetString());
12✔
94
                parser.RegisterKeyword("targetPlaysetSelectionEnabled", reader => {
6✔
NEW
95
                        TargetPlaysetSelectionEnabled = reader.GetBool();
×
96
                });
6✔
97
                parser.RegisterKeyword("copyToTargetGameModDirectory", reader => {
6✔
98
                        CopyToTargetGameModDirectory = reader.GetString().Equals("true");
×
99
                });
6✔
100
                parser.RegisterKeyword("progressOnCopyingComplete", reader => {
12✔
101
                        ProgressOnCopyingComplete = (ushort)reader.GetInt();
6✔
102
                });
12✔
103
                parser.RegisterKeyword("enableUpdateChecker", reader => {
12✔
104
                        UpdateCheckerEnabled = reader.GetString().Equals("true");
6✔
105
                });
12✔
106
                parser.RegisterKeyword("checkForUpdatesOnStartup", reader => {
12✔
107
                        CheckForUpdatesOnStartup = reader.GetString().Equals("true");
6✔
108
                });
12✔
109
                parser.RegisterKeyword("converterReleaseForumThread", reader => {
12✔
110
                        ConverterReleaseForumThread = reader.GetString();
6✔
111
                });
12✔
112
                parser.RegisterKeyword("latestGitHubConverterReleaseUrl", reader => {
12✔
113
                        LatestGitHubConverterReleaseUrl = reader.GetString();
6✔
114
                });
12✔
115
                parser.RegisterKeyword("pagesCommitIdUrl", reader => PagesCommitIdUrl = reader.GetString());
12✔
116
                parser.IgnoreAndLogUnregisteredItems();
6✔
117
        }
6✔
118

119
        private void RegisterPreloadKeys(Parser parser) {
8✔
120
                parser.RegisterRegex(CommonRegexes.String, (reader, incomingKey) => {
128✔
121
                        StringOfItem valueStringOfItem = reader.GetStringOfItem();
120✔
122
                        string valueStr = valueStringOfItem.ToString().RemQuotes();
120✔
123
                        var valueReader = new BufferedReader(valueStr);
120✔
124

8✔
125
                        foreach (var folder in RequiredFolders) {
1,800✔
126
                                if (folder.Name.Equals(incomingKey) && Directory.Exists(valueStr)) {
480✔
127
                                        folder.Value = valueStr;
×
128
                                }
×
129
                        }
480✔
130

8✔
131
                        foreach (var file in RequiredFiles) {
720✔
132
                                if (file.Name.Equals(incomingKey) && File.Exists(valueStr)) {
120✔
133
                                        file.Value = valueStr;
×
134
                                }
×
135
                        }
120✔
136
                        foreach (var option in Options) {
3,600✔
137
                                if (option.Name.Equals(incomingKey) && option.CheckBoxSelector is null) {
1,152✔
138
                                        option.SetValue(valueStr);
72✔
139
                                } else if (option.Name.Equals(incomingKey) && option.CheckBoxSelector is not null) {
1,080✔
140
                                        var selections = valueReader.GetStrings();
×
141
                                        var values = selections.ToHashSet(StringComparer.Ordinal);
×
142
                                        option.SetValue(values);
×
143
                                        option.SetCheckBoxSelectorPreloaded();
×
144
                                }
×
145
                        }
1,080✔
146
                });
128✔
147
                parser.RegisterRegex(CommonRegexes.Catchall, ParserHelpers.IgnoreAndLogItem);
8✔
148
        }
8✔
149

150
        private void InitializePaths() {
6✔
151
                if (!OperatingSystem.IsWindows()) {
12!
152
                        return;
6✔
153
                }
154

155
                string documentsDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
×
156
                InitializeFolders(documentsDir);
×
157
                InitializeFiles(documentsDir);
×
158
        }
6✔
159

160
        private void InitializeFolders(string documentsDir) {
×
161
                foreach (var folder in RequiredFolders) {
×
162
                        string? initialValue = null;
×
163

164
                        if (!string.IsNullOrEmpty(folder.Value)) {
×
165
                                continue;
×
166
                        }
167

168
                        if (folder.SearchPathType.Equals("windowsUsersFolder")) {
×
169
                                initialValue = Path.Combine(documentsDir, folder.SearchPath);
×
170
                        } else if (folder.SearchPathType.Equals("storeFolder")) {
×
171
                                string? possiblePath = null;
×
172
                                if (uint.TryParse(folder.SteamGameId, out uint steamId)) {
×
173
                                        possiblePath = CommonFunctions.GetSteamInstallPath(steamId);
×
174
                                }
×
NEW
175
                                if (possiblePath is null && long.TryParse(folder.GOGGameId, CultureInfo.InvariantCulture, out long gogId)) {
×
176
                                        possiblePath = CommonFunctions.GetGOGInstallPath(gogId);
×
177
                                }
×
178

179
                                if (possiblePath is null) {
×
180
                                        continue;
×
181
                                }
182

183
                                initialValue = possiblePath;
×
184
                                if (!string.IsNullOrEmpty(folder.SearchPath)) {
×
185
                                        initialValue = Path.Combine(initialValue, folder.SearchPath);
×
186
                                }
×
187
                        } else if (folder.SearchPathType.Equals("direct")) {
×
188
                                initialValue = folder.SearchPath;
×
189
                        }
×
190

191
                        if (Directory.Exists(initialValue)) {
×
192
                                folder.Value = initialValue;
×
193
                        }
×
UNCOV
194
                }
×
195
        }
×
196

197
        private void InitializeFiles(string documentsDir) {
×
198
                foreach (var file in RequiredFiles) {
×
199
                        string? initialDirectory = null;
×
200
                        string? initialValue = null;
×
201

202
                        if (!string.IsNullOrEmpty(file.Value)) {
×
203
                                initialDirectory = CommonFunctions.GetPath(file.Value);
×
204
                        } else if (file.SearchPathType.Equals("windowsUsersFolder")) {
×
205
                                initialDirectory = Path.Combine(documentsDir, file.SearchPath);
×
206
                                if (!string.IsNullOrEmpty(file.FileName)) {
×
207
                                        initialValue = Path.Combine(initialDirectory, file.FileName);
×
208
                                }
×
209
                        } else if (file.SearchPathType.Equals("converterFolder")) {
×
210
                                var currentDir = Directory.GetCurrentDirectory();
×
211
                                initialDirectory = Path.Combine(currentDir, file.SearchPath);
×
212
                                if (!string.IsNullOrEmpty(file.FileName)) {
×
213
                                        initialValue = Path.Combine(initialDirectory, file.FileName);
×
214
                                }
×
215
                        }
×
216

217
                        if (string.IsNullOrEmpty(file.Value) && File.Exists(initialValue)) {
×
218
                                file.Value = initialValue;
×
219
                        }
×
220

221
                        if (Directory.Exists(initialDirectory)) {
×
222
                                file.InitialDirectory = initialDirectory;
×
223
                        }
×
224
                }
×
225
        }
×
226

227
        public void LoadExistingConfiguration() {
8✔
228
                var parser = new Parser();
8✔
229
                RegisterPreloadKeys(parser);
8✔
230
                var converterConfigurationPath = Path.Combine(ConverterFolder, "configuration.txt");
8✔
231
                if (string.IsNullOrEmpty(ConverterFolder) || !File.Exists(converterConfigurationPath)) {
8!
232
                        return;
×
233
                }
234

235
                logger.Info("Previous configuration located, preloading selections...");
8✔
236
                parser.ParseFile(converterConfigurationPath);
8✔
237
        }
8✔
238

239
        public bool ExportConfiguration() {
×
240
                SetSavingStatus("CONVERTSTATUSIN");
×
241

242
                if (string.IsNullOrEmpty(ConverterFolder)) {
×
243
                        logger.Error("Converter folder is not set!");
×
244
                        SetSavingStatus("CONVERTSTATUSPOSTFAIL");
×
245
                        return false;
×
246
                }
247
                if (!Directory.Exists(ConverterFolder)) {
×
248
                        logger.Error("Could not find converter folder!");
×
249
                        SetSavingStatus("CONVERTSTATUSPOSTFAIL");
×
250
                        return false;
×
251
                }
252

253
                var outConfPath = Path.Combine(ConverterFolder, "configuration.txt");
×
254
                try {
×
255
                        using var writer = new StreamWriter(outConfPath);
×
256

257
                        WriteRequiredFolders(writer);
×
258
                        WriteRequiredFiles(writer);
×
NEW
259
                        if (SelectedPlayset is not null) {
×
NEW
260
                                writer.WriteLine($"selectedPlayset = {SelectedPlayset.Id}");
×
UNCOV
261
                        }
×
262

263
                        WriteOptions(writer);
×
264

265
                        SetSavingStatus("CONVERTSTATUSPOSTSUCCESS");
×
266
                        return true;
×
267
                } catch (Exception ex) {
×
268
                        logger.Error($"Could not open configuration.txt! Error: {ex}");
×
269
                        SetSavingStatus("CONVERTSTATUSPOSTFAIL");
×
270
                        return false;
×
271
                }
272
        }
×
273

274
        private void WriteOptions(StreamWriter writer) {
×
275
                foreach (var option in Options) {
×
276
                        if (option.CheckBoxSelector is not null) {
×
277
                                writer.Write($"{option.Name} = {{ ");
×
278
                                foreach (var value in option.GetValues()) {
×
279
                                        writer.Write($"\"{value}\" ");
×
280
                                }
×
281

282
                                writer.WriteLine("}");
×
283
                        } else {
×
284
                                writer.WriteLine($"{option.Name} = \"{option.GetValue()}\"");
×
285
                        }
×
286
                }
×
287
        }
×
288

289
        private void WriteRequiredFiles(StreamWriter writer) {
×
290
                foreach (var file in RequiredFiles) {
×
291
                        if (!file.Outputtable) {
×
292
                                continue;
×
293
                        }
294

295
                        // In the file path, replace backslashes with forward slashes.
296
                        string pathToWrite = file.Value.Replace('\\', '/');
×
297
                        writer.WriteLine($"{file.Name} = \"{pathToWrite}\"");
×
298
                }
×
299
        }
×
300

301
        private void WriteRequiredFolders(StreamWriter writer) {
×
302
                foreach (var folder in RequiredFolders) {
×
303
                        // In the folder path, replace backslashes with forward slashes.
304
                        string pathToWrite = folder.Value.Replace('\\', '/');
×
305
                        writer.WriteLine($"{folder.Name} = \"{pathToWrite}\"");
×
306
                }
×
307
        }
×
308

309
        private static void SetSavingStatus(string locKey) {
×
310
                if (Avalonia.Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) {
×
311
                        return;
×
312
                }
313

314
                if (desktop.MainWindow?.DataContext is MainWindowViewModel mainWindowDataContext) {
×
315
                        mainWindowDataContext.SaveStatus = locKey;
×
316
                }
×
317
        }
×
318

319
        public string? TargetGameModsPath {
NEW
320
                get {
×
NEW
321
                        var targetGameModPath = RequiredFolders
×
NEW
322
                                .FirstOrDefault(f => f?.Name == "targetGameModPath", defaultValue: null);
×
NEW
323
                        return targetGameModPath?.Value;
×
UNCOV
324
                }
×
325
        }
326

NEW
327
        public void AutoLocatePlaysets() {
×
NEW
328
                logger.Debug("Clearing previously located playsets...");
×
NEW
329
                AutoLocatedPlaysets.Clear();
×
NEW
330
                logger.Debug("Autolocating playsets...");
×
331

NEW
332
                var destModsFolder = TargetGameModsPath;
×
NEW
333
                var locatedPlaysetsCount = 0;
×
NEW
334
                if (destModsFolder is not null) {
×
NEW
335
                        var dbContext = TargetDbManager.GetLauncherDbContext(this);
×
NEW
336
                        if (dbContext is not null) {
×
NEW
337
                                foreach (var playset in dbContext.Playsets.Where(p => p.IsRemoved == null || p.IsRemoved == false )) {
×
NEW
338
                                        AutoLocatedPlaysets.Add(playset);
×
NEW
339
                                }
×
340
                        }
×
341
                        
NEW
342
                        locatedPlaysetsCount = AutoLocatedPlaysets.Count;
×
343
                }
×
344
                
NEW
345
                logger.Debug($"Autolocated {locatedPlaysetsCount} playsets.");
×
UNCOV
346
        }
×
347

348
        private static List<string> GetValidModFiles(string modPath) {
×
349
                var validModFiles = new List<string>();
×
350
                foreach (var file in SystemUtils.GetAllFilesInFolder(modPath)) {
×
351
                        var lastDot = file.LastIndexOf('.');
×
352
                        if (lastDot == -1) {
×
353
                                continue;
×
354
                        }
355

356
                        var extension = CommonFunctions.GetExtension(file);
×
357
                        if (!extension.Equals("mod")) {
×
358
                                continue;
×
359
                        }
360

361
                        validModFiles.Add(file);
×
362
                }
×
363

364
                return validModFiles;
×
365
        }
×
366
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc