diff --git a/GameRealisticMap.Arma3/IO/ProjectDrive.cs b/GameRealisticMap.Arma3/IO/ProjectDrive.cs index 5c313ef1..09b188bb 100644 --- a/GameRealisticMap.Arma3/IO/ProjectDrive.cs +++ b/GameRealisticMap.Arma3/IO/ProjectDrive.cs @@ -148,7 +148,7 @@ public IEnumerable FindAll(string pattern) { var physical = Directory.GetFiles(mountPath, pattern, SearchOption.AllDirectories) .Select(file => file.Substring(mountPath.Length).TrimStart('\\')) - .Where(file => !file.StartsWith("temp\\", StringComparison.OrdinalIgnoreCase)); + .Where(file => !file.StartsWith("temp\\", StringComparison.OrdinalIgnoreCase) && !file.StartsWith("grm-temp\\", StringComparison.OrdinalIgnoreCase)); if (secondarySource != null) { return secondarySource.FindAll(pattern).Concat(physical).Distinct(StringComparer.OrdinalIgnoreCase); diff --git a/GameRealisticMap.Arma3/TerrainBuilder/AmbiguousModelName.cs b/GameRealisticMap.Arma3/TerrainBuilder/AmbiguousModelName.cs new file mode 100644 index 00000000..8e39ffa2 --- /dev/null +++ b/GameRealisticMap.Arma3/TerrainBuilder/AmbiguousModelName.cs @@ -0,0 +1,16 @@ +namespace GameRealisticMap.Arma3.TerrainBuilder +{ + public sealed class AmbiguousModelName : ApplicationException + { + public AmbiguousModelName(string name, IReadOnlyCollection candidates) + : base($"Name '{name}' matches multiples files : '{string.Join("', '", candidates)}'") + { + Name = name; + Candidates = candidates; + } + + public string Name { get; } + + public IReadOnlyCollection Candidates { get; } + } +} \ No newline at end of file diff --git a/GameRealisticMap.Arma3/TerrainBuilder/IModelInfoLibrary.cs b/GameRealisticMap.Arma3/TerrainBuilder/IModelInfoLibrary.cs index 1b94eac7..00d89490 100644 --- a/GameRealisticMap.Arma3/TerrainBuilder/IModelInfoLibrary.cs +++ b/GameRealisticMap.Arma3/TerrainBuilder/IModelInfoLibrary.cs @@ -6,8 +6,12 @@ public interface IModelInfoLibrary { ModelInfo ResolveByName(string name); + bool TryResolveByName(string name, [MaybeNullWhen(false)] out ModelInfo model); + ModelInfo ResolveByPath(string path); bool TryResolveByPath(string path, [MaybeNullWhen(false)] out ModelInfo model); + + bool TryRegister(string name, string path); } } diff --git a/GameRealisticMap.Arma3/TerrainBuilder/ModelInfoLibrary.cs b/GameRealisticMap.Arma3/TerrainBuilder/ModelInfoLibrary.cs index 0302a9e4..d576f1d5 100644 --- a/GameRealisticMap.Arma3/TerrainBuilder/ModelInfoLibrary.cs +++ b/GameRealisticMap.Arma3/TerrainBuilder/ModelInfoLibrary.cs @@ -24,6 +24,11 @@ public ModelInfoLibrary(IGameFileSystem fileSystem) this.fileSystem = fileSystem; } + public bool TryResolveByName(string name, [MaybeNullWhen(false)] out ModelInfo model) + { + return indexByName.TryGetValue(name, out model); + } + public ModelInfo ResolveByName(string name) { if (!indexByName.TryGetValue(name, out var modelInfo)) @@ -35,7 +40,7 @@ public ModelInfo ResolveByName(string name) } if (candidates.Count > 1) { - throw new ApplicationException($"Name '{name}' matches multiples files : '{string.Join("', '", candidates)}'"); + throw new AmbiguousModelName(name, candidates); } throw new ApplicationException($"Unknown model '{name}'"); } @@ -215,5 +220,18 @@ public Task Save() Directory.CreateDirectory(Path.GetDirectoryName(DefaultCachePath)!); return SaveTo(DefaultCachePath); } + + public bool TryRegister(string name, string path) + { + var odol = ReadModelInfoOnly(path); + if (odol == null) + { + return false; + } + + var model = new ModelInfo(name, path, odol.BoundingCenter.Vector3); + indexByName.Add(name, model); + return true; + } } } diff --git a/GameRealisticMap.Studio/Labels.Designer.cs b/GameRealisticMap.Studio/Labels.Designer.cs index c05f4d4a..1b83c503 100644 --- a/GameRealisticMap.Studio/Labels.Designer.cs +++ b/GameRealisticMap.Studio/Labels.Designer.cs @@ -69,6 +69,15 @@ public static string About { } } + /// + /// Recherche une chaîne localisée semblable à Absolute elevation. + /// + public static string AbsoluteElevation { + get { + return ResourceManager.GetString("AbsoluteElevation", resourceCulture); + } + } + /// /// Recherche une chaîne localisée semblable à Active mods. /// @@ -1393,6 +1402,15 @@ public static string EditWithEdenEditorHint { } } + /// + /// Recherche une chaîne localisée semblable à Elevation mode. + /// + public static string ElevationMode { + get { + return ResourceManager.GetString("ElevationMode", resourceCulture); + } + } + /// /// Recherche une chaîne localisée semblable à Explorer. /// @@ -1942,6 +1960,24 @@ public static string ImportNextPartPrompt { } } + /// + /// Recherche une chaîne localisée semblable à Import objects from file. + /// + public static string ImportObjectsFromFile { + get { + return ResourceManager.GetString("ImportObjectsFromFile", resourceCulture); + } + } + + /// + /// Recherche une chaîne localisée semblable à Import objects. + /// + public static string ImportsObjects { + get { + return ResourceManager.GetString("ImportsObjects", resourceCulture); + } + } + /// /// Recherche une chaîne localisée semblable à ✔ Installed. /// @@ -2295,6 +2331,24 @@ public static string NotInstalled { } } + /// + /// Recherche une chaîne localisée semblable à No valid object definition found in file.. + /// + public static string NoValidObjectDefinitionFoundInFile { + get { + return ResourceManager.GetString("NoValidObjectDefinitionFoundInFile", resourceCulture); + } + } + + /// + /// Recherche une chaîne localisée semblable à {0} objects to import. + /// + public static string NumObjectsToImport { + get { + return ResourceManager.GetString("NumObjectsToImport", resourceCulture); + } + } + /// /// Recherche une chaîne localisée semblable à objects/m². /// @@ -2503,6 +2557,15 @@ public static string Refresh { } } + /// + /// Recherche une chaîne localisée semblable à Relative elevation (to ground). + /// + public static string RelativeElevation { + get { + return ResourceManager.GetString("RelativeElevation", resourceCulture); + } + } + /// /// Recherche une chaîne localisée semblable à Remove. /// diff --git a/GameRealisticMap.Studio/Labels.fr.resx b/GameRealisticMap.Studio/Labels.fr.resx index 2dd7a448..1f5a2a1f 100644 --- a/GameRealisticMap.Studio/Labels.fr.resx +++ b/GameRealisticMap.Studio/Labels.fr.resx @@ -1108,4 +1108,25 @@ Dernière génération le {1} La partie #{0} a déjà été importée. + + Positon relative (au sol) + + + Aucune définition valide d'objet n'a été trouvé dans le fichier. + + + {0} objets à importer + + + Importer des objets depuis un fichier + + + Importer les objets + + + Mode d'altitude + + + Position absolue + \ No newline at end of file diff --git a/GameRealisticMap.Studio/Labels.resx b/GameRealisticMap.Studio/Labels.resx index 2a2bbdb7..760f29bc 100644 --- a/GameRealisticMap.Studio/Labels.resx +++ b/GameRealisticMap.Studio/Labels.resx @@ -1108,4 +1108,25 @@ Last generated on {1} Click on Refresh button when part #{0} is copied to continue import. + + Import objects from file + + + Import objects + + + Relative elevation (to ground) + + + Absolute elevation + + + Elevation mode + + + No valid object definition found in file. + + + {0} objects to import + \ No newline at end of file diff --git a/GameRealisticMap.Studio/Modules/Arma3WorldEditor/ViewModels/AmbiguousItem.cs b/GameRealisticMap.Studio/Modules/Arma3WorldEditor/ViewModels/AmbiguousItem.cs new file mode 100644 index 00000000..4cc445b8 --- /dev/null +++ b/GameRealisticMap.Studio/Modules/Arma3WorldEditor/ViewModels/AmbiguousItem.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading.Tasks; + +namespace GameRealisticMap.Studio.Modules.Arma3WorldEditor.ViewModels +{ + internal class AmbiguousItem + { + private readonly FileImporterViewModel parent; + + public AmbiguousItem(FileImporterViewModel parent, string name, string path, Uri preview) + { + this.parent = parent; + Name = name; + Path = path; + Preview = preview; + } + + public string Name { get; } + + public string Path { get; } + + public Uri Preview { get; } + + public Task Resolve() + { + return parent.Resolve(Name,Path); + } + } +} \ No newline at end of file diff --git a/GameRealisticMap.Studio/Modules/Arma3WorldEditor/ViewModels/Arma3WorldEditorViewModel.cs b/GameRealisticMap.Studio/Modules/Arma3WorldEditor/ViewModels/Arma3WorldEditorViewModel.cs index eac708c7..45febfea 100644 --- a/GameRealisticMap.Studio/Modules/Arma3WorldEditor/ViewModels/Arma3WorldEditorViewModel.cs +++ b/GameRealisticMap.Studio/Modules/Arma3WorldEditor/ViewModels/Arma3WorldEditorViewModel.cs @@ -13,11 +13,15 @@ using GameRealisticMap.Arma3.GameEngine; using GameRealisticMap.Arma3.GameLauncher; using GameRealisticMap.Arma3.IO; +using GameRealisticMap.Arma3.TerrainBuilder; +using GameRealisticMap.ElevationModel; +using GameRealisticMap.Reporting; using GameRealisticMap.Studio.Modules.Arma3Data; using GameRealisticMap.Studio.Modules.Arma3Data.Services; using GameRealisticMap.Studio.Modules.Reporting; using GameRealisticMap.Studio.Toolkit; using Gemini.Framework; +using Microsoft.Win32; namespace GameRealisticMap.Studio.Modules.Arma3WorldEditor.ViewModels { @@ -178,6 +182,8 @@ public float? SizeInMeters public IGameFileSystem GameFileSystem => arma3Data.ProjectDrive; + public IModelInfoLibrary Library => arma3Data.Library; + public List Dependencies { get; set; } = new List(); public List Backups @@ -268,16 +274,61 @@ public Task ImportEden() return windowManager.ShowDialogAsync(new EdenImporterViewModel(this)); } + public Task ImportFile() + { + var dialog = new OpenFileDialog(); + dialog.Filter = "Text File|*.txt"; + if (dialog.ShowDialog() == true) + { + return windowManager.ShowDialogAsync(new FileImporterViewModel(this, dialog.FileName)); + } + return Task.CompletedTask; + } + internal void Apply(WrpEditBatch batch) + { + IoC.Get().RunTask("Import", task => { + Apply(batch, task); + return Task.CompletedTask; + }, false); + } + + internal void Apply(List list) + { + IoC.Get().RunTask("Import", async task => + { + if (World == null) + { + return; + } + await arma3Data.SaveLibraryCache(); + var size = World.TerrainRangeX; + var grid = new ElevationGrid(size, CellSize!.Value); + for (int x = 0; x < size; x++) + { + for (int y = 0; y < size; y++) + { + grid[x, y] = World.Elevation[x + (y * size)]; + } + } + var batch = new WrpEditBatch(); + batch.Add.AddRange(list + .ProgressStep(task, "ToWrpObject") + .Select(l => l.ToWrpObject(grid)) + .Select(o => new WrpAddObject(o.Transform.Matrix, o.Model))); + Apply(batch, task); + }, false); + } + + private void Apply(WrpEditBatch batch, IProgressTaskUI task) { if (World == null) { return; } - using var task = IoC.Get().StartTask("Import"); var processor = new WrpEditProcessor(task); processor.Process(World, batch); - if ( ConfigFile != null) + if (ConfigFile != null) { if (Backups.Count > 0) { @@ -309,5 +360,7 @@ public void ClearActive() entry.IsActive = false; } } + + } } diff --git a/GameRealisticMap.Studio/Modules/Arma3WorldEditor/ViewModels/EdenImporterViewModel.cs b/GameRealisticMap.Studio/Modules/Arma3WorldEditor/ViewModels/EdenImporterViewModel.cs index 7de61bb1..86f00d5b 100644 --- a/GameRealisticMap.Studio/Modules/Arma3WorldEditor/ViewModels/EdenImporterViewModel.cs +++ b/GameRealisticMap.Studio/Modules/Arma3WorldEditor/ViewModels/EdenImporterViewModel.cs @@ -172,7 +172,7 @@ public Task ClipboardImport() { if (Batch != null) { - _ = Task.Run(() => parent.Apply(Batch)); + parent.Apply(Batch); } return TryCloseAsync(true); } diff --git a/GameRealisticMap.Studio/Modules/Arma3WorldEditor/ViewModels/FileImporterViewModel.cs b/GameRealisticMap.Studio/Modules/Arma3WorldEditor/ViewModels/FileImporterViewModel.cs new file mode 100644 index 00000000..5a33b95c --- /dev/null +++ b/GameRealisticMap.Studio/Modules/Arma3WorldEditor/ViewModels/FileImporterViewModel.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Caliburn.Micro; +using GameRealisticMap.Arma3.TerrainBuilder; +using GameRealisticMap.Reporting; +using GameRealisticMap.Studio.Modules.Arma3Data; +using GameRealisticMap.Studio.Modules.AssetBrowser.Services; +using Gemini.Framework; + +namespace GameRealisticMap.Studio.Modules.Arma3WorldEditor.ViewModels +{ + internal class FileImporterViewModel : WindowBase, IProgress + { + private static readonly NLog.Logger logger = NLog.LogManager.GetLogger("FileImporter"); + + private readonly Arma3WorldEditorViewModel parent; + private readonly List list = new List(); + private readonly string fileName; + private bool _isWorking; + private double _workingPercent; + private bool isAbsolute; + private List ambigousModels = new List(); + + public FileImporterViewModel(Arma3WorldEditorViewModel parent, string fileName) + { + this.parent = parent; + this.fileName = fileName; + } + + public Task Cancel() => TryCloseAsync(false); + + public bool IsWorking + { + get { return _isWorking; } + set { _isWorking = value; NotifyOfPropertyChange(); } + } + + public double WorkingPercent + { + get { return _workingPercent; } + set { _workingPercent = value; NotifyOfPropertyChange(); } + } + + public string Error { get; set; } = string.Empty; + + public string Message { get; set; } = string.Empty; + + public bool IsNotValid => !IsWorking && !string.IsNullOrEmpty(Error); + + public bool IsValid => !IsWorking && string.IsNullOrEmpty(Error); + + public string FileName => fileName; + + public bool IsAbsolute + { + get { return isAbsolute; } + set { if (isAbsolute != value) { isAbsolute = value; NotifyOfPropertyChange(); NotifyOfPropertyChange(nameof(IsRelative)); } } + } + + public bool IsRelative { get { return !IsAbsolute; } set { IsAbsolute = !value; } } + + public bool HasAmbigousModels => AmbigousModels.Count > 0; + + public List AmbigousModels + { + get { return ambigousModels; } + set + { + ambigousModels = value; NotifyOfPropertyChange(); NotifyOfPropertyChange(nameof(HasAmbigousModels)); + } + } + + protected override Task OnActivateAsync(CancellationToken cancellationToken) + { + IsWorking = true; + NotifyOfPropertyChange(nameof(IsNotValid)); + NotifyOfPropertyChange(nameof(IsValid)); + AmbigousModels = new List(); + + _ = Task.Run(() => DoReadFile()); + + return Task.CompletedTask; + } + + private async Task DoReadFile() + { + + try + { + var library = new ImportLibrary(await IoC.Get().Load(), parent.Library); + + list.Clear(); + using (var task = new BasicProgressSystem(this, logger)) + { + var lines = await File.ReadAllLinesAsync(fileName); + foreach (var line in lines.ProgressStep(task, "Lines")) + { + var csvLine = line.Trim().Split(';'); + if (csvLine.Length > 7) + { + list.Add(new TerrainBuilderObject(ElevationMode.Absolute, csvLine, library)); + } + } + } + if (list.Count == 0) + { + Error = GameRealisticMap.Studio.Labels.NoValidObjectDefinitionFoundInFile; + } + else + { + Message = string.Format(GameRealisticMap.Studio.Labels.NumObjectsToImport, list.Count); + } + } + catch (Exception ex) + { + logger.Error(ex); + Error = ex.Message; + if (ex is AmbiguousModelName amn) + { + await ShowAmbiguousItems(amn); + } + } + + IsWorking = false; + + NotifyOfPropertyChange(nameof(Error)); + NotifyOfPropertyChange(nameof(IsNotValid)); + NotifyOfPropertyChange(nameof(IsValid)); + NotifyOfPropertyChange(nameof(Message)); + + if (list.All(o => o.Elevation == 0)) + { + IsRelative = true; + } + else + { + IsAbsolute = true; + } + } + + private async Task ShowAmbiguousItems(AmbiguousModelName amn) + { + var preview = IoC.Get(); + var items = new List(); + foreach(var path in amn.Candidates) + { + items.Add(new AmbiguousItem(this, amn.Name, path, await preview.GetPreview(path))); + } + AmbigousModels = items; + } + + public void Report(double value) + { + WorkingPercent = value; + } + + public Task Import() + { + if (IsRelative) + { + parent.Apply(list.Select(i => new TerrainBuilderObject(i.Model, i.Point, i.Elevation, ElevationMode.Relative, i.Yaw, i.Pitch, i.Roll, i.Scale)).ToList()); + } + else + { + parent.Apply(list); + } + return TryCloseAsync(true); + } + + internal Task Resolve(string name, string path) + { + parent.Library.TryRegister(name, path); + + return OnActivateAsync(CancellationToken.None); + } + } +} \ No newline at end of file diff --git a/GameRealisticMap.Studio/Modules/Arma3WorldEditor/ViewModels/ImportLibrary.cs b/GameRealisticMap.Studio/Modules/Arma3WorldEditor/ViewModels/ImportLibrary.cs new file mode 100644 index 00000000..9f2dd29e --- /dev/null +++ b/GameRealisticMap.Studio/Modules/Arma3WorldEditor/ViewModels/ImportLibrary.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using GameRealisticMap.Arma3.TerrainBuilder; +using GameRealisticMap.Studio.Modules.AssetBrowser.Services; + +namespace GameRealisticMap.Studio.Modules.Arma3WorldEditor.ViewModels +{ + internal class ImportLibrary : IModelInfoLibrary + { + private readonly List assetCatalogItems; + private readonly IModelInfoLibrary library; + + public ImportLibrary(List assetCatalogItems, IModelInfoLibrary library) + { + this.assetCatalogItems = assetCatalogItems; + this.library = library; + } + + public bool TryRegister(string name, string path) + { + return library.TryRegister(name, path); + } + + public ModelInfo ResolveByName(string name) + { + if (TryResolveByName(name, out var model)) + { + return model; + } + return library.ResolveByName(name); + } + + public ModelInfo ResolveByPath(string path) + { + return library.ResolveByPath(path); + } + + public bool TryResolveByName(string name, [MaybeNullWhen(false)] out ModelInfo model) + { + if (library.TryResolveByName(name, out model)) + { + return true; + } + var searchName = name + ".p3d"; + var entry = assetCatalogItems.Where(i => string.Equals(i.Name, searchName, System.StringComparison.OrdinalIgnoreCase)).ToList(); + if (entry.Count == 1) + { + return TryResolveByPath(entry[0].Path, out model); + } + return false; + } + + public bool TryResolveByPath(string path, [MaybeNullWhen(false)] out ModelInfo model) + { + return library.TryResolveByPath(path, out model); + } + } +} \ No newline at end of file diff --git a/GameRealisticMap.Studio/Modules/Arma3WorldEditor/Views/Arma3WorldEditorView.xaml b/GameRealisticMap.Studio/Modules/Arma3WorldEditor/Views/Arma3WorldEditorView.xaml index 19a2b914..fb51185d 100644 --- a/GameRealisticMap.Studio/Modules/Arma3WorldEditor/Views/Arma3WorldEditorView.xaml +++ b/GameRealisticMap.Studio/Modules/Arma3WorldEditor/Views/Arma3WorldEditorView.xaml @@ -28,6 +28,9 @@ + diff --git a/GameRealisticMap.Studio/Modules/Arma3WorldEditor/Views/FileImporterView.xaml b/GameRealisticMap.Studio/Modules/Arma3WorldEditor/Views/FileImporterView.xaml new file mode 100644 index 00000000..c8dc6471 --- /dev/null +++ b/GameRealisticMap.Studio/Modules/Arma3WorldEditor/Views/FileImporterView.xaml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/GameRealisticMap.Studio/Modules/Arma3WorldEditor/Views/FileImporterView.xaml.cs b/GameRealisticMap.Studio/Modules/Arma3WorldEditor/Views/FileImporterView.xaml.cs new file mode 100644 index 00000000..ed4b237a --- /dev/null +++ b/GameRealisticMap.Studio/Modules/Arma3WorldEditor/Views/FileImporterView.xaml.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace GameRealisticMap.Studio.Modules.Arma3WorldEditor.Views +{ + /// + /// Logique d'interaction pour FileImporterView.xaml + /// + public partial class FileImporterView : Window + { + public FileImporterView() + { + InitializeComponent(); + } + } +} diff --git a/GameRealisticMap.Studio/Modules/AssetBrowser/Services/AssetsCatalogService.cs b/GameRealisticMap.Studio/Modules/AssetBrowser/Services/AssetsCatalogService.cs index 0502ab5c..487f66cc 100644 --- a/GameRealisticMap.Studio/Modules/AssetBrowser/Services/AssetsCatalogService.cs +++ b/GameRealisticMap.Studio/Modules/AssetBrowser/Services/AssetsCatalogService.cs @@ -19,6 +19,11 @@ internal class AssetsCatalogService : IAssetsCatalogService private static readonly Logger logger = NLog.LogManager.GetLogger("AssetsCatalogService"); private readonly IArma3DataModule arma3DataModule; + public string AssetsCatalogPath { get; set; } = System.IO.Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "GameRealisticMap", + "Arma3", + "assets.json"); [ImportingConstructor] public AssetsCatalogService(IArma3DataModule arma3DataModule) @@ -26,6 +31,11 @@ public AssetsCatalogService(IArma3DataModule arma3DataModule) this.arma3DataModule = arma3DataModule; } + public Task> Load() + { + return LoadFrom(AssetsCatalogPath); + } + public async Task> LoadFrom(string fileName) { if ( File.Exists(fileName)) @@ -36,6 +46,11 @@ public async Task> LoadFrom(string fileName) return new List(); } + public Task Save(List items) + { + return SaveTo(AssetsCatalogPath, items); + } + public async Task SaveTo(string fileName, List items) { using var stream = File.Create(fileName); diff --git a/GameRealisticMap.Studio/Modules/AssetBrowser/Services/IAssetsCatalogService.cs b/GameRealisticMap.Studio/Modules/AssetBrowser/Services/IAssetsCatalogService.cs index e5d1e17d..f1cb01e9 100644 --- a/GameRealisticMap.Studio/Modules/AssetBrowser/Services/IAssetsCatalogService.cs +++ b/GameRealisticMap.Studio/Modules/AssetBrowser/Services/IAssetsCatalogService.cs @@ -5,8 +5,12 @@ namespace GameRealisticMap.Studio.Modules.AssetBrowser.Services { internal interface IAssetsCatalogService { + Task> Load(); + Task> LoadFrom(string fileName); + Task Save(List items); + Task SaveTo(string fileName, List items); Task> ImportItems(IEnumerable paths, string modId = ""); diff --git a/GameRealisticMap.Studio/Modules/AssetBrowser/ViewModels/AssetBrowserViewModel.cs b/GameRealisticMap.Studio/Modules/AssetBrowser/ViewModels/AssetBrowserViewModel.cs index 5235ba69..527d0007 100644 --- a/GameRealisticMap.Studio/Modules/AssetBrowser/ViewModels/AssetBrowserViewModel.cs +++ b/GameRealisticMap.Studio/Modules/AssetBrowser/ViewModels/AssetBrowserViewModel.cs @@ -50,12 +50,6 @@ internal class AssetBrowserViewModel : Document new ModInfo("ARM", string.Empty, "2982306133") }; - public string AssetsCatalogPath { get; set; } = System.IO.Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), - "GameRealisticMap", - "Arma3", - "assets.json"); - [ImportingConstructor] public AssetBrowserViewModel(IArma3DataModule arma3DataModule, IArma3Previews arma3Previews, IAssetsCatalogService catalogService, IArma3ModsService arma3ModsService) { @@ -165,7 +159,7 @@ private async Task DoImport(List paths, string modId) var imported = await _catalogService.ImportItems(needImport, modId).ConfigureAwait(false); var models = imported.Select(m => new AssetViewModel(m, _arma3Previews.GetPreviewFast(m.Path))).ToList(); target.AddRange(models); - await _catalogService.SaveTo(AssetsCatalogPath, target.Select(i => i.Item).ToList()); + await _catalogService.Save(target.Select(i => i.Item).ToList()); UpdateModsList(models); _ = Task.Run(() => LoadPreviews(models.Where(a => !a.Preview.IsFile).ToList())); @@ -181,14 +175,14 @@ private async Task DoImport(List paths, string modId) protected override async Task OnInitializeAsync(CancellationToken cancellationToken) { - var items = await _catalogService.LoadFrom(AssetsCatalogPath); + var items = await _catalogService.Load(); var known = new HashSet(items.Select(i => i.Path), StringComparer.OrdinalIgnoreCase); var missing = await _catalogService.ImportItems(_arma3DataModule.Library.Models.Where(m => !known.Contains(m.Path)).Select(m => m.Path)); items.AddRange(missing); if (missing.Count > 0) { - await _catalogService.SaveTo(AssetsCatalogPath, items); + await _catalogService.Save(items); } AllAssets = new BindableCollection(items.Select(m => new AssetViewModel(m, _arma3Previews.GetPreviewFast(m.Path)))); @@ -267,7 +261,7 @@ internal async Task ChangeAssetsCategoryAsync(IEnumerable enumer if (target != null) { Assets?.Refresh(); - await _catalogService.SaveTo(AssetsCatalogPath, target.Select(i => i.Item).ToList()); + await _catalogService.Save(target.Select(i => i.Item).ToList()); } } @@ -277,7 +271,7 @@ internal async Task RemoveAssetsAsync(IEnumerable enumerable) if (target != null) { AllAssets?.RemoveRange(enumerable); - await _catalogService.SaveTo(AssetsCatalogPath, target.Select(i => i.Item).ToList()); + await _catalogService.Save(target.Select(i => i.Item).ToList()); } } diff --git a/GameRealisticMap.Studio/Modules/Reporting/IProgressTool.cs b/GameRealisticMap.Studio/Modules/Reporting/IProgressTool.cs index 07579ec1..5e7cf757 100644 --- a/GameRealisticMap.Studio/Modules/Reporting/IProgressTool.cs +++ b/GameRealisticMap.Studio/Modules/Reporting/IProgressTool.cs @@ -10,6 +10,6 @@ internal interface IProgressTool : ITool IProgressTaskUI StartTask(string name); - void RunTask(string name, Func run); + void RunTask(string name, Func run, bool prompt = true); } } diff --git a/GameRealisticMap.Studio/Modules/Reporting/ViewModels/ProgressToolViewModel.cs b/GameRealisticMap.Studio/Modules/Reporting/ViewModels/ProgressToolViewModel.cs index e1602689..9c5de2f0 100644 --- a/GameRealisticMap.Studio/Modules/Reporting/ViewModels/ProgressToolViewModel.cs +++ b/GameRealisticMap.Studio/Modules/Reporting/ViewModels/ProgressToolViewModel.cs @@ -167,16 +167,16 @@ internal void WriteLine(string message) OnUIThread(() => output.AppendLine(message ?? string.Empty)); } - public void RunTask(string name, Func run) + public void RunTask(string name, Func run, bool prompt = true) { if (!IsRunning) { shell.ShowTool(this); - _ = Task.Run(() => DoRunTask(name, run)); + _ = Task.Run(() => DoRunTask(name, run, prompt)); } } - private async Task DoRunTask(string name, Func run) + private async Task DoRunTask(string name, Func run, bool prompt) { using var task = (ProgressTask)StartTask(name); try @@ -189,10 +189,11 @@ private async Task DoRunTask(string name, Func run) } task.WriteLine(FormattableString.Invariant($"Memory: Peak: {memoryPeak:0.000} G, System: {totalMemGb:0.000} G")); task.Dispose(); - if ( task.Error == null && !task.CancellationToken.IsCancellationRequested) + if (prompt && task.Error == null && !task.CancellationToken.IsCancellationRequested) { new System.Action(() => windowManager.ShowDialogAsync(new SuccessViewModel(task))).BeginOnUIThread(); } } + } }