From 8bed73fb5a6b0e6d4b4642c63e6d64c7b1b036e5 Mon Sep 17 00:00:00 2001 From: bird_egop Date: Sun, 7 Jun 2026 00:41:16 +0300 Subject: [PATCH] add config file --- NResUI/Abstractions/IConfigProvider.cs | 27 +++++++++ NResUI/Abstractions/ILaunchReceiver.cs | 6 ++ NResUI/App.cs | 20 +++++++ NResUI/ConfigJson.cs | 9 +++ NResUI/ILater.cs | 25 +++++++++ NResUI/ImGuiUI/ViewportPanel.cs | 8 ++- NResUI/LoadConfigOnLaunch.cs | 53 ++++++++++++++++++ NResUI/Rendering.zip | Bin 18195 -> 0 bytes .../Viewport/Msh/MshViewportLoader.cs | 11 ++-- .../Viewport/Msh/WeaMaterialLibrary.cs | 45 ++++++--------- 10 files changed, 169 insertions(+), 35 deletions(-) create mode 100644 NResUI/Abstractions/IConfigProvider.cs create mode 100644 NResUI/Abstractions/ILaunchReceiver.cs create mode 100644 NResUI/ConfigJson.cs create mode 100644 NResUI/ILater.cs create mode 100644 NResUI/LoadConfigOnLaunch.cs delete mode 100644 NResUI/Rendering.zip diff --git a/NResUI/Abstractions/IConfigProvider.cs b/NResUI/Abstractions/IConfigProvider.cs new file mode 100644 index 0000000..9e9b504 --- /dev/null +++ b/NResUI/Abstractions/IConfigProvider.cs @@ -0,0 +1,27 @@ +namespace NResUI.Abstractions; + +public interface IConfigProvider +{ + ConfigJson GetConfig(); + void Set(ConfigJson config); +} + +public class ConfigProvider : IConfigProvider +{ + private ConfigJson _instance; + + public ConfigProvider(ConfigJson instance) + { + _instance = instance; + } + + public void Set(ConfigJson config) + { + _instance = config; + } + + public ConfigJson GetConfig() + { + return _instance; + } +} diff --git a/NResUI/Abstractions/ILaunchReceiver.cs b/NResUI/Abstractions/ILaunchReceiver.cs new file mode 100644 index 0000000..388f1fe --- /dev/null +++ b/NResUI/Abstractions/ILaunchReceiver.cs @@ -0,0 +1,6 @@ +namespace NResUI.Abstractions; + +public interface ILaunchReceiver +{ + void OnLaunch(); +} diff --git a/NResUI/App.cs b/NResUI/App.cs index 9c0461d..1d68397 100644 --- a/NResUI/App.cs +++ b/NResUI/App.cs @@ -26,6 +26,7 @@ public class App public ImFontPtr OpenSansFont; private List _imGuiPanels = []; + private Later _later = new(); public App() { @@ -40,6 +41,9 @@ public class App IServiceCollection serviceCollection = new ServiceCollection(); + serviceCollection.AddSingleton(_later); + serviceCollection.AddSingleton(new ConfigProvider(new ConfigJson())); + foreach (var type in Utils.GetAssignableTypes()) { serviceCollection.AddSingleton(type); @@ -50,6 +54,11 @@ public class App serviceCollection.AddSingleton(type); } + foreach (var type in Utils.GetAssignableTypes()) + { + serviceCollection.AddSingleton(type); + } + serviceCollection.AddSingleton(openGl); serviceCollection.AddSingleton(window); @@ -68,6 +77,12 @@ public class App _imGuiPanels = Utils.GetAssignableTypes() .Select(t => (serviceProvider.GetService(t) as IImGuiPanel)!) .ToList(); + + foreach (var type in Utils.GetAssignableTypes()) + { + var launchReceiver = serviceProvider.GetService(type) as ILaunchReceiver; + launchReceiver?.OnLaunch(); + } } public void OnImGuiRender() @@ -123,6 +138,11 @@ public class App { imGuiPanel.OnImGuiRender(); } + + foreach (var action in _later.Enumerate()) + { + action(); + } // ImGui.ShowMetricsWindow(); // ImGui.ShowDemoWindow(); diff --git a/NResUI/ConfigJson.cs b/NResUI/ConfigJson.cs new file mode 100644 index 0000000..34ac2b4 --- /dev/null +++ b/NResUI/ConfigJson.cs @@ -0,0 +1,9 @@ +namespace NResUI; + +public class ConfigJson +{ + /// + /// Path to the game itself (the folder that contains DATA, MISSIONS, UNITS etc) + /// + public string GameBasePath { get; set; } +} diff --git a/NResUI/ILater.cs b/NResUI/ILater.cs new file mode 100644 index 0000000..e62508c --- /dev/null +++ b/NResUI/ILater.cs @@ -0,0 +1,25 @@ +namespace NResUI; + +public interface ILater +{ + void Execute(Action action); +} + +public class Later : ILater +{ + private Queue _queue = new(); + + public void Execute(Action action) + { + _queue.Enqueue(action); + } + + public IEnumerable Enumerate() + { + while (_queue.Count > 0) + { + var action = _queue.Dequeue(); + yield return action; + } + } +} \ No newline at end of file diff --git a/NResUI/ImGuiUI/ViewportPanel.cs b/NResUI/ImGuiUI/ViewportPanel.cs index 523a1b0..e5ba576 100644 --- a/NResUI/ImGuiUI/ViewportPanel.cs +++ b/NResUI/ImGuiUI/ViewportPanel.cs @@ -16,12 +16,14 @@ public sealed class ViewportPanel : IImGuiPanel private readonly ViewportScene _scene; private readonly ViewportCamera _camera = new(); private readonly ViewportInputController _inputController = new(); + private readonly IConfigProvider _configProvider; private string? _loadedModelPath; private string? _loadError; - public ViewportPanel(GL gl, IWindow window) + public ViewportPanel(GL gl, IWindow window, IConfigProvider configProvider) { + _configProvider = configProvider; var cubeMesh = PrimitiveMeshes.CreateCube(gl); var gridMesh = PrimitiveMeshes.CreateWorldGrid(gl); @@ -94,7 +96,7 @@ public sealed class ViewportPanel : IImGuiPanel if (ImGui.Button("Reset cube")) { var cubeMesh = PrimitiveMeshes.CreateCube(_renderer.Gl); - _scene.ReplacePieces(new[] { ViewportPiece.CreateUnitCube(0, "Cube", cubeMesh) }); + _scene.ReplacePieces([ViewportPiece.CreateUnitCube(0, "Cube", cubeMesh)]); _loadedModelPath = null; _loadError = null; _camera.Reset(); @@ -109,7 +111,7 @@ public sealed class ViewportPanel : IImGuiPanel private void LoadMsh(string path) { - var loadResult = MshViewportLoader.LoadFromFile(_renderer.Gl, path); + var loadResult = MshViewportLoader.LoadFromFile(_renderer.Gl, path, _configProvider); if (!loadResult.IsSuccess) { _loadError = loadResult.Error ?? "Unknown error."; diff --git a/NResUI/LoadConfigOnLaunch.cs b/NResUI/LoadConfigOnLaunch.cs new file mode 100644 index 0000000..7897958 --- /dev/null +++ b/NResUI/LoadConfigOnLaunch.cs @@ -0,0 +1,53 @@ +using System.Text.Json; +using NResUI.Abstractions; +using NResUI.ImGuiUI; + +namespace NResUI; + +public class LoadConfigOnLaunch : ILaunchReceiver +{ + private readonly ILater _later; + private readonly MessageBoxModalPanel _messageBoxModalPanel; + private readonly IConfigProvider _configProvider; + + public LoadConfigOnLaunch(ILater later, MessageBoxModalPanel messageBoxModalPanel, IConfigProvider configProvider) + { + _later = later; + _messageBoxModalPanel = messageBoxModalPanel; + _configProvider = configProvider; + } + + public void OnLaunch() + { + if (!File.Exists("config.json")) + { + _later.Execute(() => + { + _messageBoxModalPanel.Show( + "config.json is not present\nViewport may be broken without game files\nPlaceholder file has been created"); + }); + var config = new ConfigJson() { GameBasePath = "" }; + File.WriteAllText("config.json", JsonSerializer.Serialize(config)); + _configProvider.Set(config); + } + else + { + var content = File.ReadAllText("config.json"); + var config = JsonSerializer.Deserialize(content); + if (config is null) + { + _later.Execute(() => + { + _messageBoxModalPanel.Show("config.json was corrupted\nReplacement has been created"); + }); + config = new ConfigJson() { GameBasePath = "" }; + File.WriteAllText("config.json", JsonSerializer.Serialize(config)); + _configProvider.Set(config); + } + else + { + _configProvider.Set(config); + } + } + } +} diff --git a/NResUI/Rendering.zip b/NResUI/Rendering.zip deleted file mode 100644 index 00430b11a6cd47235020317ab204b2cdd7951066..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18195 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 LoadModelPieces(GL gl, FileStream fs, NResArchive archive, string path) + private static List LoadModelPieces( + GL gl, FileStream fs, NResArchive archive, string path, IConfigProvider configProvider + ) { var nodes = Msh0x01.ReadComponent(fs, archive); var geometry = Msh0x02.ReadComponent(fs, archive); @@ -54,7 +57,7 @@ public static class MshViewportLoader var restPoses = MshRestPoseBuilder.BuildRestPose(nodes, animationDescriptors); var mshToViewportTransform = Matrix4x4.CreateRotationX(-MathF.PI * 0.5f); var names = TryReadNames(fs, archive); - var materialLibrary = WeaMaterialLibrary.TryLoadForMsh(gl, path); + var materialLibrary = WeaMaterialLibrary.TryLoadForMsh(gl, path, configProvider); var pieces = new List(); diff --git a/NResUI/Rendering/Viewport/Msh/WeaMaterialLibrary.cs b/NResUI/Rendering/Viewport/Msh/WeaMaterialLibrary.cs index 05c4419..4b57318 100644 --- a/NResUI/Rendering/Viewport/Msh/WeaMaterialLibrary.cs +++ b/NResUI/Rendering/Viewport/Msh/WeaMaterialLibrary.cs @@ -1,5 +1,6 @@ using MaterialLib; using NResLib; +using NResUI.Abstractions; using NResUI.Rendering.Viewport.OpenGL; using Silk.NET.OpenGL; using TexmLib; @@ -24,7 +25,7 @@ public sealed class WeaMaterialLibrary : null; } - public static WeaMaterialLibrary TryLoadForMsh(GL gl, string mshPath) + public static WeaMaterialLibrary TryLoadForMsh(GL gl, string mshPath, IConfigProvider configProvider) { var weaPath = FindMatchingWeaPath(mshPath); if (!File.Exists(weaPath)) @@ -34,9 +35,13 @@ public sealed class WeaMaterialLibrary if (materialRefs.Count == 0) return Empty; - // TODO: Hardcoded for now - var materialLibFs = "C:\\IronStrategy\\Material.lib"; + var materialLibFs = Path.Combine(configProvider.GetConfig().GameBasePath, "Material.lib"); + if (!File.Exists(materialLibFs)) + { + return new WeaMaterialLibrary([]); + } + using var materialFs = new FileStream(materialLibFs, FileMode.Open, FileAccess.Read, FileShare.Read); var parseResult = NResParser.ReadFile(materialFs); if (parseResult.Archive == null) @@ -46,7 +51,7 @@ public sealed class WeaMaterialLibrary foreach (var materialRef in materialRefs) { - var texture = TryLoadMaterialTexture(gl, materialFs, parseResult.Archive, materialRef.Name, out var texm); + var texture = TryLoadMaterialTexture(gl, materialFs, parseResult.Archive, materialRef.Name, configProvider, out var texm); result[materialRef.Id] = texture != null ? new ViewportMaterial(materialRef.Name, texture.Value, texm) : new ViewportMaterial(materialRef.Name); @@ -194,7 +199,8 @@ public sealed class WeaMaterialLibrary } private static uint? TryLoadMaterialTexture( - GL gl, FileStream materialFs, NResArchive materialArchive, string materialName, out TexmFile? texm + GL gl, FileStream materialFs, NResArchive materialArchive, string materialName, IConfigProvider configProvider, + out TexmFile? texm ) { texm = null; @@ -212,7 +218,12 @@ public sealed class WeaMaterialLibrary var texture = materialFile.Stages[0].TextureName; - var textureLibFs = "C:\\IronStrategy\\Textures.lib"; + var textureLibFs = Path.Combine(configProvider.GetConfig().GameBasePath, "Textures.lib"); + + if (!File.Exists(textureLibFs)) + { + return null; + } using var textureFs = new FileStream(textureLibFs, FileMode.Open, FileAccess.Read, FileShare.Read); var textureResult = NResParser.ReadFile(textureFs); @@ -269,27 +280,5 @@ public sealed class WeaMaterialLibrary Normalize(x.FileName) == normalized); } - private static string? FindTexturesLib(string? startDirectory) - { - if (string.IsNullOrWhiteSpace(startDirectory)) - return null; - - var directory = new DirectoryInfo(startDirectory); - while (directory != null) - { - var candidate = Path.Combine(directory.FullName, "Textures.lib"); - if (Directory.Exists(candidate)) - return candidate; - - candidate = Path.Combine(directory.FullName, "textures.lib"); - if (Directory.Exists(candidate)) - return candidate; - - directory = directory.Parent; - } - - return null; - } - private readonly record struct WeaMaterialRef(int Id, string Name); }