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

ParadoxGameConverters / Fronter.NET / 14571234271

21 Apr 2025 09:38AM UTC coverage: 18.257% (-0.01%) from 18.268%
14571234271

Pull #849

github

web-flow
Merge fd5fbcede into 82576ae98
Pull Request #849: Output all folder and file paths in configuration with forward slashes

73 of 656 branches covered (11.13%)

Branch coverage included in aggregate %.

0 of 4 new or added lines in 1 file covered. (0.0%)

2 existing lines in 1 file now uncovered.

547 of 2740 relevant lines covered (19.96%)

9.34 hits per line

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

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

12
namespace Fronter.Models.Configuration;
13

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

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

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

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

57
                InitializePaths();
6✔
58

59
                LoadExistingConfiguration();
6✔
60
        }
6✔
61

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

113
        private void RegisterPreloadKeys(Parser parser) {
8✔
114
                parser.RegisterRegex(CommonRegexes.String, (reader, incomingKey) => {
128✔
115
                        var valueStringOfItem = reader.GetStringOfItem();
120✔
116
                        var valueStr = valueStringOfItem.ToString().RemQuotes();
120✔
117
                        var valueReader = new BufferedReader(valueStr);
120✔
118

8✔
119
                        foreach (var folder in RequiredFolders) {
1,800✔
120
                                if (folder.Name.Equals(incomingKey) && Directory.Exists(valueStr)) {
480✔
121
                                        folder.Value = valueStr;
×
122
                                }
×
123
                        }
480✔
124

8✔
125
                        foreach (var file in RequiredFiles) {
720✔
126
                                if (file.Name.Equals(incomingKey) && File.Exists(valueStr)) {
120✔
127
                                        file.Value = valueStr;
×
128
                                }
×
129
                        }
120✔
130
                        foreach (var option in Options) {
3,600✔
131
                                if (option.Name.Equals(incomingKey) && option.CheckBoxSelector is null) {
1,152✔
132
                                        option.SetValue(valueStr);
72✔
133
                                } else if (option.Name.Equals(incomingKey) && option.CheckBoxSelector is not null) {
1,080✔
134
                                        var selections = valueReader.GetStrings();
×
135
                                        var values = selections.ToHashSet(StringComparer.Ordinal);
×
136
                                        option.SetValue(values);
×
137
                                        option.SetCheckBoxSelectorPreloaded();
×
138
                                }
×
139
                        }
1,080✔
140
                        if (incomingKey.Equals("selectedMods")) {
128✔
141
                                var theList = valueReader.GetStrings();
8✔
142
                                var matchingMods = AutoLocatedMods.Where(m => theList.Contains(m.FileName, StringComparer.Ordinal));
8✔
143
                                foreach (var mod in matchingMods) {
24✔
144
                                        mod.Enabled = true;
×
145
                                }
×
146
                        }
8✔
147
                });
128✔
148
                parser.RegisterRegex(CommonRegexes.Catchall, ParserHelpers.IgnoreAndLogItem);
8✔
149
        }
8✔
150

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

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

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

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

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

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

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

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

196
                        if (folder.Name.Equals(ModAutoGenerationSource)) {
×
197
                                AutoLocateMods();
×
198
                        }
×
199
                }
×
200
        }
×
201

202
        private void InitializeFiles(string documentsDir) {
×
203
                foreach (var file in RequiredFiles) {
×
204
                        string? initialDirectory = null;
×
205
                        string? initialValue = null;
×
206

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

222
                        if (string.IsNullOrEmpty(file.Value) && File.Exists(initialValue)) {
×
223
                                file.Value = initialValue;
×
224
                        }
×
225

226
                        if (Directory.Exists(initialDirectory)) {
×
227
                                file.InitialDirectory = initialDirectory;
×
228
                        }
×
229
                }
×
230
        }
×
231

232
        public void LoadExistingConfiguration() {
8✔
233
                var parser = new Parser();
8✔
234
                RegisterPreloadKeys(parser);
8✔
235
                var converterConfigurationPath = Path.Combine(ConverterFolder, "configuration.txt");
8✔
236
                if (string.IsNullOrEmpty(ConverterFolder) || !File.Exists(converterConfigurationPath)) {
8!
237
                        return;
×
238
                }
239

240
                logger.Info("Previous configuration located, preloading selections...");
8✔
241
                parser.ParseFile(converterConfigurationPath);
8✔
242
        }
8✔
243

244
        public bool ExportConfiguration() {
×
245
                SetSavingStatus("CONVERTSTATUSIN");
×
246

247
                if (string.IsNullOrEmpty(ConverterFolder)) {
×
248
                        logger.Error("Converter folder is not set!");
×
249
                        SetSavingStatus("CONVERTSTATUSPOSTFAIL");
×
250
                        return false;
×
251
                }
252
                if (!Directory.Exists(ConverterFolder)) {
×
253
                        logger.Error("Could not find converter folder!");
×
254
                        SetSavingStatus("CONVERTSTATUSPOSTFAIL");
×
255
                        return false;
×
256
                }
257

258
                var outConfPath = Path.Combine(ConverterFolder, "configuration.txt");
×
259
                try {
×
260
                        using var writer = new StreamWriter(outConfPath);
×
261
                        foreach (var folder in RequiredFolders) {
×
262
                                // In the folder path, replace backslashes with forward slashes.
NEW
263
                                string pathToWrite = folder.Value.Replace('\\', '/');
×
NEW
264
                                writer.WriteLine($"{folder.Name} = \"{pathToWrite}\"");
×
UNCOV
265
                        }
×
266

267
                        foreach (var file in RequiredFiles) {
×
268
                                if (!file.Outputtable) {
×
269
                                        continue;
×
270
                                }
271
                                // In the file path, replace backslashes with forward slashes.
NEW
272
                                string pathToWrite = file.Value.Replace('\\', '/');
×
NEW
273
                                writer.WriteLine($"{file.Name} = \"{pathToWrite}\"");
×
UNCOV
274
                        }
×
275

276
                        if (ModAutoGenerationSource is not null) {
×
277
                                writer.WriteLine("selectedMods = {");
×
278
                                foreach (var mod in AutoLocatedMods) {
×
279
                                        if (mod.Enabled) {
×
280
                                                writer.WriteLine($"\t\"{mod.FileName}\"");
×
281
                                        }
×
282
                                }
×
283
                                writer.WriteLine("}");
×
284
                        }
×
285

286
                        foreach (var option in Options) {
×
287
                                if (option.CheckBoxSelector is not null) {
×
288
                                        writer.Write($"{option.Name} = {{ ");
×
289
                                        foreach (var value in option.GetValues()) {
×
290
                                                writer.Write($"\"{value}\" ");
×
291
                                        }
×
292
                                        writer.WriteLine("}");
×
293
                                } else {
×
294
                                        writer.WriteLine($"{option.Name} = \"{option.GetValue()}\"");
×
295
                                }
×
296
                        }
×
297

298
                        SetSavingStatus("CONVERTSTATUSPOSTSUCCESS");
×
299
                        return true;
×
300
                } catch (Exception ex) {
×
301
                        logger.Error($"Could not open configuration.txt! Error: {ex}");
×
302
                        SetSavingStatus("CONVERTSTATUSPOSTFAIL");
×
303
                        return false;
×
304
                }
305
        }
×
306

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

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

317
        public void AutoLocateMods() {
×
318
                logger.Debug("Clearing previously located mods...");
×
319
                AutoLocatedMods.Clear();
×
320
                logger.Debug("Autolocating mods...");
×
321

322
                // Do we have a mod path?
323
                string? modPath = null;
×
324
                foreach (var folder in RequiredFolders) {
×
325
                        if (folder.Name.Equals(ModAutoGenerationSource)) {
×
326
                                modPath = folder.Value;
×
327
                        }
×
328
                }
×
329
                if (modPath is null) {
×
330
                        logger.Warn("No folder found as source for mods autolocation.");
×
331
                        return;
×
332
                }
333

334
                // Does it exist?
335
                if (!Directory.Exists(modPath)) {
×
336
                        logger.Warn($"Mod path \"{modPath}\" does not exist or can not be accessed!");
×
337
                        return;
×
338
                }
339

340
                // Are we looking at documents directory?
341
                var combinedPath = Path.Combine(modPath, "mod");
×
342
                if (Directory.Exists(combinedPath)) {
×
343
                        modPath = combinedPath;
×
344
                }
×
345
                logger.Debug($"Mods autolocation path set to: \"{modPath}\"");
×
346

347
                // Are there mods inside?
348
                var validModFiles = new List<string>();
×
349
                foreach (var file in SystemUtils.GetAllFilesInFolder(modPath)) {
×
350
                        var lastDot = file.LastIndexOf('.');
×
351
                        if (lastDot == -1) {
×
352
                                continue;
×
353
                        }
354

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

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

363
                if (validModFiles.Count == 0) {
×
364
                        logger.Debug($"No mod files could be found in \"{modPath}\"");
×
365
                        return;
×
366
                }
367

368
                foreach (var modFile in validModFiles) {
×
369
                        var path = Path.Combine(modPath, modFile);
×
370
                        Mod theMod;
371
                        try {
×
372
                                theMod = new Mod(path);
×
373
                        } catch (IOException ex) {
×
374
                                logger.Warn($"Failed to parse mod file {modFile}: {ex.Message}");
×
375
                                continue;
×
376
                        }
377
                        if (string.IsNullOrEmpty(theMod.Name)) {
×
378
                                logger.Warn($"Mod at \"{path}\" has no defined name, skipping.");
×
379
                                continue;
×
380
                        }
381
                        AutoLocatedMods.Add(theMod);
×
382
                }
×
383
                logger.Debug($"Autolocated {AutoLocatedMods.Count} mods");
×
384
        }
×
385
}
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