Skip to content

Commit 4cac2a8

Browse files
authored
Adds a UI component to view and edit program settings (#134)
* adds primitive window to view existing settings * working and completed * remove commented out code * remove unnecessary usings
1 parent 904d6ff commit 4cac2a8

12 files changed

+234
-179
lines changed

Dat/FileParsing/ByteReader.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using OpenLoco.Dat.Types;
2-
using Zenith.Core;
32

43
namespace OpenLoco.Dat.FileParsing
54
{

Definitions/Migrations/20241013053058_InitialCreate.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
using System;
2-
using Microsoft.EntityFrameworkCore.Migrations;
1+
using Microsoft.EntityFrameworkCore.Migrations;
32

43
#nullable disable
54

65
namespace Definitions.Migrations
76
{
8-
/// <inheritdoc />
9-
public partial class InitialCreate : Migration
7+
/// <inheritdoc />
8+
public partial class InitialCreate : Migration
109
{
1110
/// <inheritdoc />
1211
protected override void Up(MigrationBuilder migrationBuilder)

Gui/EditorSettings.cs

Lines changed: 67 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
using OpenLoco.Common.Logging;
2+
using OpenLoco.Dat;
3+
using System;
14
using System.Collections.Generic;
25
using System.IO;
6+
using System.Text.Json;
7+
using System.Text.Json.Serialization;
38

49
namespace OpenLoco.Gui
510
{
@@ -24,56 +29,81 @@ public HashSet<string> ObjDataDirectories
2429
}
2530
HashSet<string> objDataDirectories;
2631

27-
public string DataDirectory
32+
public bool AutoObjectDiscoveryAndUpload { get; set; }
33+
34+
public bool UseHttps { get; set; }
35+
public string ServerAddressHttp { get; set; } = "http://openloco.leftofzen.dev/";
36+
public string ServerAddressHttps { get; set; } = "https://openloco.leftofzen.dev/";
37+
38+
public string DownloadFolder { get; set; } = string.Empty;
39+
40+
[JsonIgnore]
41+
public string IndexFileName
42+
=> GetObjDataFullPath(ObjectIndex.DefaultIndexFileName);
43+
44+
public string GetObjDataFullPath(string fileName)
45+
=> Path.Combine(ObjDataDirectory, fileName);
46+
47+
[JsonIgnore]
48+
public const string DefaultFileName = "settings.json"; // "settings-dev.json" for dev, "settings.json" for prod
49+
50+
[JsonIgnore]
51+
static readonly JsonSerializerOptions SerializerOptions = new() { WriteIndented = true, AllowTrailingCommas = true };
52+
53+
public static EditorSettings Load(string filename, ILogger logger)
2854
{
29-
get => dataDirectory;
30-
set
55+
if (!File.Exists(filename))
3156
{
32-
dataDirectory = value;
33-
DataDirectories ??= [];
34-
_ = DataDirectories.Add(dataDirectory);
57+
logger.Info($"Settings file doesn't exist; creating now at \"{filename}\"");
58+
var newSettings = new EditorSettings();
59+
Save(newSettings, filename, logger);
60+
return newSettings;
3561
}
36-
}
37-
string dataDirectory;
3862

39-
public HashSet<string> DataDirectories
40-
{
41-
get => dataDirectories ??= [];
42-
set => dataDirectories = value;
63+
var text = File.ReadAllText(filename);
64+
var settings = JsonSerializer.Deserialize<EditorSettings>(text, options: SerializerOptions); // todo: try-catch this for invalid settings files
65+
ArgumentNullException.ThrowIfNull(settings);
66+
return settings;
4367
}
44-
HashSet<string> dataDirectories;
4568

46-
public string SCV5Directory
69+
public void Save(string filename, ILogger logger)
70+
=> Save(this, filename, logger);
71+
72+
static void Save(EditorSettings settings, string filename, ILogger logger)
4773
{
48-
get => scv5Directory;
49-
set
74+
var text = JsonSerializer.Serialize(settings, options: SerializerOptions);
75+
76+
var parentDir = Path.GetDirectoryName(filename);
77+
if (parentDir != null && !Directory.Exists(parentDir))
5078
{
51-
scv5Directory = value;
52-
SCV5Directories ??= [];
53-
_ = SCV5Directories.Add(scv5Directory);
79+
_ = Directory.CreateDirectory(parentDir);
5480
}
55-
}
56-
string scv5Directory;
5781

58-
public HashSet<string> SCV5Directories
59-
{
60-
get => scv5Directories ??= [];
61-
set => scv5Directories = value;
82+
try
83+
{
84+
File.WriteAllText(filename, text);
85+
}
86+
catch (Exception ex)
87+
{
88+
logger.Error(ex);
89+
}
6290
}
63-
HashSet<string> scv5Directories;
64-
65-
public bool AutoObjectDiscoveryAndUpload { get; set; }
6691

67-
public bool UseHttps { get; set; }
68-
public string ServerAddressHttp { get; set; } = "http://openloco.leftofzen.dev/";
69-
public string ServerAddressHttps { get; set; } = "https://openloco.leftofzen.dev/";
92+
public bool Validate(ILogger logger)
93+
{
94+
if (string.IsNullOrEmpty(ObjDataDirectory))
95+
{
96+
logger.Warning("Invalid settings file: Object directory was null or empty");
97+
return false;
98+
}
7099

71-
public string PaletteFile { get; set; } = "palette.png";
72-
public string IndexFileName { get; set; } = "objectIndex.json"; // this should be the same as ObjectIndex.IndexFileName
73-
public string G1DatFileName { get; set; } = "g1.DAT";
74-
public string DownloadFolder { get; set; } = string.Empty;
100+
if (!Directory.Exists(ObjDataDirectory))
101+
{
102+
logger.Warning($"Invalid settings file: Directory \"{ObjDataDirectory}\" does not exist");
103+
return false;
104+
}
75105

76-
public string GetObjDataFullPath(string fileName) => Path.Combine(ObjDataDirectory, fileName);
77-
public string GetDataFullPath(string fileName) => Path.Combine(DataDirectory, fileName);
106+
return true;
107+
}
78108
}
79109
}

Gui/Models/ObjectEditorModel.cs

Lines changed: 14 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
using System.IO;
1616
using System.Linq;
1717
using System.Net.Http;
18-
using System.Text.Json;
1918
using System.Threading;
2019
using System.Threading.Tasks;
2120

@@ -50,11 +49,10 @@ public class ObjectEditorModel
5049
public const string ApplicationName = "OpenLoco Object Editor";
5150
public const string LoggingFileName = "objectEditor.log";
5251

53-
public static string SettingsPath => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ApplicationName);
54-
55-
public static string SettingsFile => Path.Combine(SettingsPath, Environment.GetEnvironmentVariable("ENV_SETTINGS_FILE") ?? "settings.json"); // "settings-dev.json" for dev, "settings.json" for prod
56-
57-
public static string LoggingFile => Path.Combine(SettingsPath, LoggingFileName);
52+
// stores settings.json, objectEditor.log, etc
53+
public static string ProgramDataPath => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ApplicationName);
54+
public static string SettingsFile => Path.Combine(ProgramDataPath, Environment.GetEnvironmentVariable("ENV_SETTINGS_FILE") ?? EditorSettings.DefaultFileName);
55+
public static string LoggingFile => Path.Combine(ProgramDataPath, LoggingFileName);
5856

5957
public ObservableCollection<LogLine> LoggerObservableLogs = [];
6058

@@ -65,9 +63,9 @@ public ObjectEditorModel()
6563
Logger = new Logger();
6664
LoggerObservableLogs = [];
6765
Logger.LogAdded += (sender, laea) => Dispatcher.UIThread.Post(() => LoggerObservableLogs.Insert(0, laea.Log));
68-
//Logger.LogAdded += (sender, laea) => File.AppendAllLines(LoggingFile, [laea.Log.ToString()]);
6966

7067
LoadSettings();
68+
InitialiseDownloadDirectory();
7169

7270
var serverAddress = Settings!.UseHttps ? Settings.ServerAddressHttps : Settings.ServerAddressHttp;
7371

@@ -82,38 +80,26 @@ public ObjectEditorModel()
8280
}
8381
}
8482

85-
public void LoadSettings()
83+
void LoadSettings()
8684
{
87-
if (!File.Exists(SettingsFile))
88-
{
89-
Logger.Info($"Settings file doesn't exist; creating now at \"{SettingsFile}\"");
90-
Settings = new();
91-
SaveSettings();
92-
return;
93-
}
94-
95-
var text = File.ReadAllText(SettingsFile);
96-
var settings = JsonSerializer.Deserialize<EditorSettings>(text, options: new() { WriteIndented = true }); // todo: try-catch this for invalid settings files
97-
ArgumentNullException.ThrowIfNull(settings);
98-
99-
Settings = settings!;
100-
InitialiseDownloadDirectory();
85+
Settings = EditorSettings.Load(SettingsFile, Logger);
10186

102-
if (ValidateSettings(Settings, Logger) && File.Exists(IndexFilename))
87+
if (Settings.Validate(Logger))
10388
{
10489
Logger.Info("Settings loaded and validated successfully.");
10590
}
10691
else
10792
{
10893
Logger.Error("Unable to validate settings file - please delete it and it will be recreated on next editor start-up.");
10994
}
95+
11096
}
11197

11298
void InitialiseDownloadDirectory()
11399
{
114100
if (string.IsNullOrEmpty(Settings.DownloadFolder))
115101
{
116-
Settings.DownloadFolder = Path.Combine(SettingsPath, "downloads");
102+
Settings.DownloadFolder = Path.Combine(ProgramDataPath, "downloads");
117103
}
118104

119105
if (!Directory.Exists(Settings.DownloadFolder))
@@ -123,52 +109,6 @@ void InitialiseDownloadDirectory()
123109
}
124110
}
125111

126-
public string IndexFilename
127-
=> Settings.GetObjDataFullPath(Settings.IndexFileName);
128-
129-
static bool ValidateSettings(EditorSettings settings, ILogger? logger)
130-
{
131-
if (settings == null)
132-
{
133-
logger?.Error("Invalid settings file: Unable to deserialise settings file");
134-
return false;
135-
}
136-
137-
if (string.IsNullOrEmpty(settings.ObjDataDirectory))
138-
{
139-
logger?.Warning("Invalid settings file: Object directory was null or empty");
140-
return false;
141-
}
142-
143-
if (!Directory.Exists(settings.ObjDataDirectory))
144-
{
145-
logger?.Warning($"Invalid settings file: Directory \"{settings.ObjDataDirectory}\" does not exist");
146-
return false;
147-
}
148-
149-
return true;
150-
}
151-
152-
public void SaveSettings()
153-
{
154-
var text = JsonSerializer.Serialize(Settings, options: new() { WriteIndented = true });
155-
156-
var parentDir = Path.GetDirectoryName(SettingsFile);
157-
if (parentDir != null && !Directory.Exists(parentDir))
158-
{
159-
_ = Directory.CreateDirectory(parentDir);
160-
}
161-
162-
try
163-
{
164-
File.WriteAllText(SettingsFile, text);
165-
}
166-
catch (Exception ex)
167-
{
168-
Logger.Error(ex);
169-
}
170-
}
171-
172112
public bool TryLoadObject(FileSystemItem filesystemItem, out UiDatLocoFile? uiLocoFile)
173113
{
174114
if (string.IsNullOrEmpty(filesystemItem.Filename))
@@ -309,59 +249,6 @@ public bool TryLoadObject(FileSystemItem filesystemItem, out UiDatLocoFile? uiLo
309249
return true;
310250
}
311251

312-
public bool LoadDataDirectory(string directory)
313-
{
314-
if (!Directory.Exists(directory))
315-
{
316-
Logger.Warning("Invalid directory: doesn't exist");
317-
return false;
318-
}
319-
320-
Settings.DataDirectory = directory;
321-
322-
var allDataFiles = Directory.GetFiles(Settings.DataDirectory).Select(f => Path.GetFileName(f).ToLower()).ToHashSet();
323-
324-
void LoadKnownData(HashSet<string> allFilesInDir, HashSet<string> knownFilenames, Dictionary<string, byte[]> dict)
325-
{
326-
dict.Clear();
327-
var expectedMusicFiles = knownFilenames.Select(f => f.ToLower());
328-
foreach (var music in expectedMusicFiles)
329-
{
330-
var matching = allFilesInDir.Where(f => f.EndsWith(music));
331-
if (matching.Any())
332-
{
333-
dict.Add(music, File.ReadAllBytes(Path.Combine(Settings.DataDirectory, music)));
334-
_ = allFilesInDir.RemoveWhere(f => f.EndsWith(music));
335-
}
336-
}
337-
}
338-
339-
LoadKnownData(allDataFiles, [.. OriginalDataFiles.Music.Keys], Music);
340-
LoadKnownData(allDataFiles, [.. OriginalDataFiles.MiscellaneousTracks.Keys], MiscellaneousTracks);
341-
LoadKnownData(allDataFiles, [OriginalDataFiles.SoundEffect], SoundEffects);
342-
LoadKnownData(allDataFiles, OriginalDataFiles.Tutorials, Tutorials);
343-
344-
//MiscFiles = [.. allDataFiles];
345-
346-
// load G1 only for now since we need it for palette
347-
//G1 = SawyerStreamReader.LoadG1(Settings.GetDataFullPath(Settings.G1DatFileName));
348-
349-
//LoadPalette(); // update palette from g1
350-
351-
//await SaveSettings();
352-
353-
return true;
354-
}
355-
356-
// this method will load any supported file type
357-
//public void LoadDirectory(string directory)
358-
//{
359-
// var allFiles = Directory.GetFiles(directory, "*.dat|*.sv5|*.sc5", SearchOption.AllDirectories);
360-
//}
361-
362-
//public void LoadObjDirectory(string directory)
363-
// => LoadObjDirectory(directory, new Progress<float>(), true);
364-
365252
static Task? indexerTask;
366253
static readonly SemaphoreSlim taskLock = new(1, 1);
367254

@@ -393,15 +280,15 @@ async Task LoadObjDirectoryAsyncCore(string directory, IProgress<float> progress
393280
}
394281

395282
Settings.ObjDataDirectory = directory;
396-
SaveSettings();
283+
Settings.Save(SettingsFile, Logger);
397284

398-
if (useExistingIndex && File.Exists(IndexFilename))
285+
if (useExistingIndex && File.Exists(Settings.IndexFileName))
399286
{
400287
var exception = false;
401288

402289
try
403290
{
404-
ObjectIndex = await ObjectIndex.LoadIndexAsync(IndexFilename) ?? ObjectIndex;
291+
ObjectIndex = await ObjectIndex.LoadIndexAsync(Settings.IndexFileName) ?? ObjectIndex;
405292
}
406293
catch (Exception ex)
407294
{
@@ -433,7 +320,7 @@ async Task RecreateIndex(string directory, IProgress<float> progress)
433320
{
434321
Logger.Info("Recreating index file");
435322
ObjectIndex = await ObjectIndex.CreateIndexAsync(directory, Logger, progress);
436-
ObjectIndex?.SaveIndex(IndexFilename);
323+
ObjectIndex?.SaveIndex(Settings.IndexFileName);
437324
}
438325
}
439326

Gui/ViewModels/DatTypes/MusicViewModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ static string GetDisplayName(string filename)
4343
public override void Save()
4444
{
4545
var savePath = CurrentFile.FileLocation == FileLocation.Local
46-
? Path.Combine(Model.Settings.DataDirectory, CurrentFile.Filename)
46+
? CurrentFile.Filename
4747
: Path.Combine(Model.Settings.DownloadFolder, Path.ChangeExtension(CurrentFile.DisplayName, ".dat"));
4848

4949
Logger?.Info($"Saving music to {savePath}");

Gui/ViewModels/DatTypes/SoundEffectsViewModel.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using OpenLoco.Dat.Data;
1+
using OpenLoco.Dat.Data;
22
using OpenLoco.Dat.FileParsing;
33
using OpenLoco.Gui.Models;
44
using PropertyModels.Extensions;
@@ -30,7 +30,7 @@ public override void Load()
3030
public override void Save()
3131
{
3232
var savePath = CurrentFile.FileLocation == FileLocation.Local
33-
? Path.Combine(Model.Settings.DataDirectory, CurrentFile.Filename)
33+
? CurrentFile.Filename
3434
: Path.Combine(Model.Settings.DownloadFolder, Path.ChangeExtension(CurrentFile.DisplayName, ".dat"));
3535

3636
Logger?.Info($"Saving sound effects to {savePath}");

0 commit comments

Comments
 (0)