From fcc0c676e63cf928a8b3cd7febf7b7e6e3bc87a9 Mon Sep 17 00:00:00 2001 From: Julien Date: Sat, 17 May 2025 09:49:54 +0200 Subject: [PATCH 1/4] Allow custom location for project drive data --- GameRealisticMap.Arma3/Arma3ToolsHelper.cs | 59 ++++++++++++++++++- .../IO/WorkspaceSettings.cs | 4 +- .../Modules/Arma3Data/Arma3DataModule.cs | 4 ++ 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/GameRealisticMap.Arma3/Arma3ToolsHelper.cs b/GameRealisticMap.Arma3/Arma3ToolsHelper.cs index f8d48bdd..75a482ce 100644 --- a/GameRealisticMap.Arma3/Arma3ToolsHelper.cs +++ b/GameRealisticMap.Arma3/Arma3ToolsHelper.cs @@ -1,6 +1,6 @@ using System.Diagnostics; using System.Runtime.Versioning; -using GameRealisticMap.Reporting; +using GameRealisticMap.Arma3.IO; using Microsoft.Win32; using Pmad.ProgressTracking; @@ -8,6 +8,8 @@ namespace GameRealisticMap.Arma3 { public static class Arma3ToolsHelper { + public static WorkspaceSettings? WorkspaceSettings { get; set; } + public static void EnsureProjectDrive(bool auto = true) { if (OperatingSystem.IsWindows() && !Directory.Exists("P:\\")) @@ -16,7 +18,21 @@ public static void EnsureProjectDrive(bool auto = true) string path = GetArma3ToolsPath(); if (!string.IsNullOrEmpty(path)) { - var processs = Process.Start(Path.Combine(path, @"WorkDrive\WorkDrive.exe"), auto ? "/Mount /y" : "/Mount"); + var args = new List + { + "/Mount" + }; + if (auto) + { + args.Add("/y"); + } + var customPath = GetCustomProjectDriveMappedPath(); + if (!string.IsNullOrEmpty(customPath)) + { + args.Add("P"); + args.Add(customPath); + } + var processs = Process.Start(Path.Combine(path, @"WorkDrive\WorkDrive.exe"), args); processs.WaitForExit(); } } @@ -218,13 +234,50 @@ public static string GetPboProjectPath() return @"C:\Program Files (x86)\Mikero\DePboTools\bin\pboProject.exe"; // Hard-coded because tool can only work from this location } - public static string GetProjectDrivePath() + public static string GetProjectDrivePath(WorkspaceSettings? settings = null) { if (Directory.Exists("P:")) { return "P:"; } + return GetCustomProjectDriveMappedPath(settings) ?? GetDefaultProjectDriveMappedPath(); + } + + /// + /// Default value used by Arma 3 Tools + /// + /// + public static string GetDefaultProjectDriveMappedPath() + { return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Arma 3 Projects"); } + + /// + /// Get the custom project drive path from the settings + /// + /// + /// + private static string? GetCustomProjectDriveMappedPath(WorkspaceSettings? settings = null) + { + var path = GetSettings(settings).ProjectDriveBasePath; + if (!string.IsNullOrEmpty(path) && Directory.Exists(path)) + { + return path; + } + return null; + } + + private static WorkspaceSettings GetSettings(WorkspaceSettings? settings = null) + { + if (settings != null) + { + return settings; + } + if (WorkspaceSettings == null) + { + WorkspaceSettings = WorkspaceSettings.Load().GetAwaiter().GetResult(); + } + return WorkspaceSettings; + } } } diff --git a/GameRealisticMap.Arma3/IO/WorkspaceSettings.cs b/GameRealisticMap.Arma3/IO/WorkspaceSettings.cs index c15e1a97..07243d01 100644 --- a/GameRealisticMap.Arma3/IO/WorkspaceSettings.cs +++ b/GameRealisticMap.Arma3/IO/WorkspaceSettings.cs @@ -23,7 +23,7 @@ public class WorkspaceSettings public ProjectDrive CreateProjectDrive() { var drive = new ProjectDrive( - ProjectDriveBasePath ?? Arma3ToolsHelper.GetProjectDrivePath(), + Arma3ToolsHelper.GetProjectDrivePath(this), new PboFileSystem(PboFileSystem.GetArma3Paths(Arma3BasePath ?? Arma3ToolsHelper.GetArma3Path()), ModsPaths)); foreach(var pair in ProjectDriveMountPoints) { @@ -34,7 +34,7 @@ public ProjectDrive CreateProjectDrive() public ProjectDrive CreateProjectDriveStandalone() { - var drive = new ProjectDrive(ProjectDriveBasePath ?? Arma3ToolsHelper.GetProjectDrivePath()); + var drive = new ProjectDrive(Arma3ToolsHelper.GetProjectDrivePath(this)); foreach (var pair in ProjectDriveMountPoints) { drive.AddMountPoint(pair.Key, pair.Value); diff --git a/GameRealisticMap.Studio/Modules/Arma3Data/Arma3DataModule.cs b/GameRealisticMap.Studio/Modules/Arma3Data/Arma3DataModule.cs index c8472259..6be8d477 100644 --- a/GameRealisticMap.Studio/Modules/Arma3Data/Arma3DataModule.cs +++ b/GameRealisticMap.Studio/Modules/Arma3Data/Arma3DataModule.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using GameRealisticMap.Arma3; using GameRealisticMap.Arma3.GameEngine; using GameRealisticMap.Arma3.IO; using GameRealisticMap.Arma3.TerrainBuilder; @@ -56,6 +57,7 @@ private void CommitSettings(WorkspaceSettings settings) { settings.Save(); Settings = settings; + Arma3ToolsHelper.WorkspaceSettings = settings; } } @@ -68,6 +70,8 @@ private void Initialize(WorkspaceSettings settings) { Settings = settings; + Arma3ToolsHelper.WorkspaceSettings = settings; + ProjectDrive = settings.CreateProjectDrive(); Library = new ModelInfoLibrary(ProjectDrive); From 0e7421a8b05991a7d3583778cf310b3c99900f08 Mon Sep 17 00:00:00 2001 From: Julien Date: Sat, 17 May 2025 09:52:44 +0200 Subject: [PATCH 2/4] Fix --- GameRealisticMap.Arma3/Arma3ToolsHelper.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/GameRealisticMap.Arma3/Arma3ToolsHelper.cs b/GameRealisticMap.Arma3/Arma3ToolsHelper.cs index 75a482ce..09f7344c 100644 --- a/GameRealisticMap.Arma3/Arma3ToolsHelper.cs +++ b/GameRealisticMap.Arma3/Arma3ToolsHelper.cs @@ -22,16 +22,16 @@ public static void EnsureProjectDrive(bool auto = true) { "/Mount" }; - if (auto) - { - args.Add("/y"); - } var customPath = GetCustomProjectDriveMappedPath(); if (!string.IsNullOrEmpty(customPath)) { args.Add("P"); args.Add(customPath); } + if (auto) + { + args.Add("/y"); + } var processs = Process.Start(Path.Combine(path, @"WorkDrive\WorkDrive.exe"), args); processs.WaitForExit(); } From ba08b4d2a528a6f36a2045e71c6b9881fa74d46a Mon Sep 17 00:00:00 2001 From: Julien Date: Sat, 17 May 2025 10:50:10 +0200 Subject: [PATCH 3/4] UI for ProjectDriveBasePath --- .../Arma3ToolsHelperTest.cs | 51 ++++++++++++++++ .../Mocks/Arma3DataModuleMock.cs | 6 ++ .../Modules/Arma3Data/Arma3DataModule.cs | 21 +++++++ .../Modules/Arma3Data/IArma3DataModule.cs | 3 + .../ViewModels/Arma3DataSettingsViewModel.cs | 15 +++-- .../Views/Arma3DataSettingsView.xaml | 61 +++++++++---------- 6 files changed, 122 insertions(+), 35 deletions(-) create mode 100644 GameRealisticMap.Arma3.Test/Arma3ToolsHelperTest.cs diff --git a/GameRealisticMap.Arma3.Test/Arma3ToolsHelperTest.cs b/GameRealisticMap.Arma3.Test/Arma3ToolsHelperTest.cs new file mode 100644 index 00000000..f96987e8 --- /dev/null +++ b/GameRealisticMap.Arma3.Test/Arma3ToolsHelperTest.cs @@ -0,0 +1,51 @@ +using System.IO; +using GameRealisticMap.Arma3; +using GameRealisticMap.Arma3.IO; +using Xunit; + +namespace GameRealisticMap.Arma3.Test +{ + public class Arma3ToolsHelperTest + { + [Fact] + public void GetProjectDrivePath_Returns_Custom_If_Set_And_Exists() + { + // Arrange + var customPath = Path.Combine(Path.GetTempPath(), "Arma3CustomProjectDrive"); + Directory.CreateDirectory(customPath); + var settings = new WorkspaceSettings { ProjectDriveBasePath = customPath }; + + // Act + var result = Arma3ToolsHelper.GetProjectDrivePath(settings); + + // Assert + Assert.Equal(customPath, result); + } + + [Fact] + public void GetProjectDrivePath_Returns_Default_If_Custom_Set_And_Not_Exists() + { + // Arrange + var settings = new WorkspaceSettings { ProjectDriveBasePath = "NoSuchFileOrDirectory" }; + + // Act + var result = Arma3ToolsHelper.GetProjectDrivePath(settings); + + // Assert + Assert.Contains("Arma 3 Projects", result); + } + + [Fact] + public void GetProjectDrivePath_Returns_Default_If_No_P_And_No_Custom() + { + // Arrange + var settings = new WorkspaceSettings { ProjectDriveBasePath = null }; + + // Act + var result = Arma3ToolsHelper.GetProjectDrivePath(settings); + + // Assert + Assert.Contains("Arma 3 Projects", result); + } + } +} diff --git a/GameRealisticMap.Studio.Test/Mocks/Arma3DataModuleMock.cs b/GameRealisticMap.Studio.Test/Mocks/Arma3DataModuleMock.cs index abd582a4..e6f962dc 100644 --- a/GameRealisticMap.Studio.Test/Mocks/Arma3DataModuleMock.cs +++ b/GameRealisticMap.Studio.Test/Mocks/Arma3DataModuleMock.cs @@ -16,6 +16,7 @@ internal class Arma3DataModuleMock : IArma3DataModule public IEnumerable ActiveMods { get; set; } = new string[0]; public bool UsePboProject { get; set; } + public string ProjectDriveBasePath { get => throw new NotImplementedException(); } public event EventHandler? Reloaded; @@ -40,5 +41,10 @@ public Task SaveLibraryCache() { return Task.CompletedTask; } + + public Task SetProjectDriveBasePath(string projectDriveBasePath) + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/GameRealisticMap.Studio/Modules/Arma3Data/Arma3DataModule.cs b/GameRealisticMap.Studio/Modules/Arma3Data/Arma3DataModule.cs index 6be8d477..4e78b176 100644 --- a/GameRealisticMap.Studio/Modules/Arma3Data/Arma3DataModule.cs +++ b/GameRealisticMap.Studio/Modules/Arma3Data/Arma3DataModule.cs @@ -51,6 +51,11 @@ public bool UsePboProject } } + public string ProjectDriveBasePath + { + get { return Settings?.ProjectDriveBasePath ?? Arma3ToolsHelper.GetDefaultProjectDriveMappedPath(); } + } + private void CommitSettings(WorkspaceSettings settings) { lock (this) @@ -125,5 +130,21 @@ public IPboCompilerFactory CreatePboCompilerFactory() } return new PboCompilerFactory(Library, ProjectDrive); } + + public async Task SetProjectDriveBasePath(string projectDriveBasePath) + { + string? actualValue = projectDriveBasePath; + if (string.IsNullOrEmpty(projectDriveBasePath) || string.Equals(projectDriveBasePath, Arma3ToolsHelper.GetDefaultProjectDriveMappedPath(), StringComparison.OrdinalIgnoreCase)) + { + actualValue = null; + } + var settings = Settings ?? new WorkspaceSettings(); + if (actualValue != settings.ProjectDriveBasePath) + { + settings.ProjectDriveBasePath = actualValue; + CommitSettings(settings); + await Reload(); + } + } } } diff --git a/GameRealisticMap.Studio/Modules/Arma3Data/IArma3DataModule.cs b/GameRealisticMap.Studio/Modules/Arma3Data/IArma3DataModule.cs index a52ac2fa..94267626 100644 --- a/GameRealisticMap.Studio/Modules/Arma3Data/IArma3DataModule.cs +++ b/GameRealisticMap.Studio/Modules/Arma3Data/IArma3DataModule.cs @@ -25,9 +25,12 @@ internal interface IArma3DataModule IPboCompilerFactory CreatePboCompilerFactory(); + Task SetProjectDriveBasePath(string projectDriveBasePath); + event EventHandler Reloaded; bool UsePboProject { get; set; } + string ProjectDriveBasePath { get; } } } diff --git a/GameRealisticMap.Studio/Modules/Arma3Data/ViewModels/Arma3DataSettingsViewModel.cs b/GameRealisticMap.Studio/Modules/Arma3Data/ViewModels/Arma3DataSettingsViewModel.cs index 269b5f25..fcf35638 100644 --- a/GameRealisticMap.Studio/Modules/Arma3Data/ViewModels/Arma3DataSettingsViewModel.cs +++ b/GameRealisticMap.Studio/Modules/Arma3Data/ViewModels/Arma3DataSettingsViewModel.cs @@ -1,5 +1,6 @@ using System.ComponentModel.Composition; using System.IO; +using System.Threading.Tasks; using Caliburn.Micro; using GameRealisticMap.Arma3; using Gemini.Modules.Settings; @@ -7,8 +8,8 @@ namespace GameRealisticMap.Studio.Modules.Arma3Data.ViewModels { [PartCreationPolicy(CreationPolicy.NonShared)] - [Export(typeof(ISettingsEditor))] - internal class Arma3DataSettingsViewModel : PropertyChangedBase, ISettingsEditor + [Export(typeof(ISettingsEditorAsync))] + internal class Arma3DataSettingsViewModel : PropertyChangedBase, ISettingsEditorAsync { private readonly IArma3DataModule _arma3; @@ -17,13 +18,16 @@ public Arma3DataSettingsViewModel(IArma3DataModule arma3) { _arma3 = arma3; UsePboProject = _arma3.UsePboProject; + ProjectDriveBasePath = _arma3.ProjectDriveBasePath; } public string SettingsPageName => GameRealisticMap.Studio.Labels.GeneralSettings; public string SettingsPagePath => "Arma 3"; - public string ProjectDriveBasePath => _arma3.ProjectDrive.MountPath; + public string ProjectDriveBasePath { get; set; } + + public string ProjectDriveMountPath => _arma3.ProjectDrive.MountPath; public string Arma3Path => Arma3ToolsHelper.GetArma3Path(); @@ -32,13 +36,16 @@ public Arma3DataSettingsViewModel(IArma3DataModule arma3) public string Arma3WorkshopPath => Arma3ToolsHelper.GetArma3WorkshopPath(); public bool UsePboProject { get; set; } + public bool UseBuiltinTool { get { return !UsePboProject; } set { UsePboProject = !value; } } public bool IsPboProjectInstalled => File.Exists(Arma3ToolsHelper.GetPboProjectPath()); - public void ApplyChanges() + public async Task ApplyChangesAsync() { _arma3.UsePboProject = UsePboProject; + await _arma3.SetProjectDriveBasePath(ProjectDriveBasePath); + NotifyOfPropertyChange(nameof(ProjectDriveMountPath)); } } } diff --git a/GameRealisticMap.Studio/Modules/Arma3Data/Views/Arma3DataSettingsView.xaml b/GameRealisticMap.Studio/Modules/Arma3Data/Views/Arma3DataSettingsView.xaml index 831cf004..d6be9019 100644 --- a/GameRealisticMap.Studio/Modules/Arma3Data/Views/Arma3DataSettingsView.xaml +++ b/GameRealisticMap.Studio/Modules/Arma3Data/Views/Arma3DataSettingsView.xaml @@ -11,59 +11,58 @@ - + + + + + + - - - + + - + + + + + + + + + + + + + + + + + - + - - - + - - - + - - - - - - - - - - - - - - - - - From 1178f114f3c22ecd364499cbbe3c21e1384bd2fc Mon Sep 17 00:00:00 2001 From: Julien Date: Sat, 17 May 2025 11:24:13 +0200 Subject: [PATCH 4/4] Provides implementation for Arma3DataModuleMock --- GameRealisticMap.Studio.Test/Mocks/Arma3DataModuleMock.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/GameRealisticMap.Studio.Test/Mocks/Arma3DataModuleMock.cs b/GameRealisticMap.Studio.Test/Mocks/Arma3DataModuleMock.cs index e6f962dc..fc0471b3 100644 --- a/GameRealisticMap.Studio.Test/Mocks/Arma3DataModuleMock.cs +++ b/GameRealisticMap.Studio.Test/Mocks/Arma3DataModuleMock.cs @@ -16,7 +16,7 @@ internal class Arma3DataModuleMock : IArma3DataModule public IEnumerable ActiveMods { get; set; } = new string[0]; public bool UsePboProject { get; set; } - public string ProjectDriveBasePath { get => throw new NotImplementedException(); } + public string ProjectDriveBasePath { get; set; } = "DefaultProjectDriveBasePath"; public event EventHandler? Reloaded; @@ -44,7 +44,8 @@ public Task SaveLibraryCache() public Task SetProjectDriveBasePath(string projectDriveBasePath) { - throw new NotImplementedException(); + ProjectDriveBasePath = projectDriveBasePath; + return Task.CompletedTask; } } } \ No newline at end of file