diff --git a/Knossos.NET/Classes/KnUtils.cs b/Knossos.NET/Classes/KnUtils.cs index a21d798d..a97a435a 100644 --- a/Knossos.NET/Classes/KnUtils.cs +++ b/Knossos.NET/Classes/KnUtils.cs @@ -411,50 +411,62 @@ public static void OpenFolder(string path) /// /// /// - public static async Task CopyDirectoryAsync(string sourceDir, string destinationDir, bool recursive, CancellationTokenSource cancelSource, Action? progressCallback = null) + public static async Task CopyDirectoryAsync(string sourceDir, string destinationDir, bool recursive, CancellationTokenSource cancelSource, + Action? progressCallback = null, string[]? ignoreExtensions = null) { - await Task.Run(async () => + return await Task.Run(async () => { - var dir = new DirectoryInfo(sourceDir); + try + { + var dir = new DirectoryInfo(sourceDir); - if (!dir.Exists) - throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}"); + if (!dir.Exists) + throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}"); - var dirs = dir.GetDirectories(); + var dirs = dir.GetDirectories(); - Directory.CreateDirectory(destinationDir); + Directory.CreateDirectory(destinationDir); - foreach (var file in dir.GetFiles()) - { - if (cancelSource.IsCancellationRequested) - { - throw new TaskCanceledException(); - } - var targetFilePath = Path.Combine(destinationDir, file.Name); - if (progressCallback != null) - { - progressCallback(file.Name); - } - file.CopyTo(targetFilePath); - } - - if (recursive) - { - foreach (var subDir in dirs) + foreach (var file in dir.GetFiles()) { if (cancelSource.IsCancellationRequested) { throw new TaskCanceledException(); } + if (ignoreExtensions == null || !ignoreExtensions.Contains(file.Extension.ToLower())) + { + var targetFilePath = Path.Combine(destinationDir, file.Name); + if (progressCallback != null) + { + progressCallback(file.Name); + } + file.CopyTo(targetFilePath); + } + } - var newDestinationDir = Path.Combine(destinationDir, subDir.Name); - await CopyDirectoryAsync(subDir.FullName, newDestinationDir, true, cancelSource, progressCallback); + if (recursive) + { + foreach (var subDir in dirs) + { + if (cancelSource.IsCancellationRequested) + { + throw new TaskCanceledException(); + } + + var newDestinationDir = Path.Combine(destinationDir, subDir.Name); + await CopyDirectoryAsync(subDir.FullName, newDestinationDir, true, cancelSource, progressCallback); + } } - } - Directory.SetCreationTime(destinationDir, Directory.GetCreationTime(sourceDir)); - Directory.SetLastAccessTime(destinationDir, Directory.GetLastAccessTime(sourceDir)); - Directory.SetLastWriteTime(destinationDir, Directory.GetLastWriteTime(sourceDir)); + Directory.SetCreationTime(destinationDir, Directory.GetCreationTime(sourceDir)); + Directory.SetLastAccessTime(destinationDir, Directory.GetLastAccessTime(sourceDir)); + Directory.SetLastWriteTime(destinationDir, Directory.GetLastWriteTime(sourceDir)); + }catch(Exception ex) + { + Log.Add(Log.LogSeverity.Error, "KnUtils.CopyDirectoryAsync()", ex); + return false; + } + return true; }); } diff --git a/Knossos.NET/ViewModels/FsoBuildsViewModel.cs b/Knossos.NET/ViewModels/FsoBuildsViewModel.cs index da660005..6ea34934 100644 --- a/Knossos.NET/ViewModels/FsoBuildsViewModel.cs +++ b/Knossos.NET/ViewModels/FsoBuildsViewModel.cs @@ -342,7 +342,7 @@ internal async void CommandAddUserBuild() if (MainWindow.instance != null) { var dialog = new AddUserBuildView(); - dialog.DataContext = new AddUserBuildViewModel(); + dialog.DataContext = new AddUserBuildViewModel(dialog); await dialog.ShowDialog(MainWindow.instance); } diff --git a/Knossos.NET/ViewModels/Windows/AddUserBuildViewModel.cs b/Knossos.NET/ViewModels/Windows/AddUserBuildViewModel.cs index 1bbf5d74..6a3cb814 100644 --- a/Knossos.NET/ViewModels/Windows/AddUserBuildViewModel.cs +++ b/Knossos.NET/ViewModels/Windows/AddUserBuildViewModel.cs @@ -1,4 +1,5 @@ -using Avalonia.Platform.Storage; +using Avalonia.Controls; +using Avalonia.Platform.Storage; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; using Knossos.NET.Classes; @@ -8,6 +9,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; using System.Threading.Tasks; namespace Knossos.NET.ViewModels @@ -55,8 +57,6 @@ public partial class AddUserBuildViewModel : ViewModelBase [ObservableProperty] internal bool riscv64 = false; [ObservableProperty] - internal bool modCreated = false; - [ObservableProperty] internal int copyProgress = 0; [ObservableProperty] internal int maxFiles = 100; @@ -64,14 +64,26 @@ public partial class AddUserBuildViewModel : ViewModelBase [ObservableProperty] internal string buildNewPath = string.Empty; + [ObservableProperty] + internal bool collapseData = false; + [ObservableProperty] + internal bool collapseArch = false; + [ObservableProperty] + internal bool collapseExecs = false; + private string buildId = string.Empty; private string? folderPath = null; - + private Window? window = null; public AddUserBuildViewModel() { } + public AddUserBuildViewModel(Window window) + { + this.window = window; + } + internal async void OpenFolderCommand() { try @@ -83,15 +95,15 @@ internal async void OpenFolderCommand() string[] execs; if (KnUtils.IsWindows) { - execs=Directory.GetFiles(folderPath, "*.exe"); + execs=Directory.GetFiles(folderPath, "*.exe", SearchOption.AllDirectories); } else if (KnUtils.IsMacOS) { - execs=Directory.GetDirectories(folderPath, "*.app"); + execs=Directory.GetDirectories(folderPath, "*.app", SearchOption.AllDirectories); } else { - execs=Directory.GetFiles(folderPath, "*.AppImage"); + execs=Directory.GetFiles(folderPath, "*.AppImage", SearchOption.AllDirectories); } ScanResults += "\nDetected Executables: " + execs.Count(); foreach (string exe in execs) @@ -103,7 +115,7 @@ internal async void OpenFolderCommand() { if (file.Name.ToLower().Contains("fs2_open")) { - DebugFile = file.Name; + DebugFile = Path.GetRelativePath(folderPath, exe); if (BuildVersion == string.Empty) { ParseVersion(file.Name); @@ -114,7 +126,7 @@ internal async void OpenFolderCommand() { if (file.Name.ToLower().Contains("fred2_open")) { - Fred2Debug = file.Name; + Fred2Debug = Path.GetRelativePath(folderPath, exe); if (BuildVersion == string.Empty) { ParseVersion(file.Name); @@ -125,7 +137,7 @@ internal async void OpenFolderCommand() { if (file.Name.ToLower().Contains("qtfred")) { - QtFredDebug = file.Name; + QtFredDebug = Path.GetRelativePath(folderPath, exe); } } } @@ -134,7 +146,7 @@ internal async void OpenFolderCommand() { if (file.Name.ToLower().Contains("fs2_open")) { - Release = file.Name; + Release = Path.GetRelativePath(folderPath, exe); if (BuildVersion == string.Empty) { ParseVersion(file.Name); @@ -145,7 +157,7 @@ internal async void OpenFolderCommand() { if (file.Name.ToLower().Contains("fred2_open")) { - Fred2 = file.Name; + Fred2 = Path.GetRelativePath(folderPath, exe); if (BuildVersion == string.Empty) { ParseVersion(file.Name); @@ -156,7 +168,7 @@ internal async void OpenFolderCommand() { if (file.Name.ToLower().Contains("qtfred")) { - QtFred = file.Name; + QtFred = Path.GetRelativePath(folderPath, exe); } } } @@ -224,7 +236,10 @@ private void ParseVersion(string filename) FolderPickerOpenOptions options = new FolderPickerOpenOptions(); options.AllowMultiple = false; options.Title = "Select the folder containing the FSO execs files"; - var result = await MainWindow.instance.StorageProvider.OpenFolderPickerAsync(options); + + var topmostWindow = window == null ? MainWindow.instance : window; + + var result = await topmostWindow.StorageProvider.OpenFolderPickerAsync(options); if (result != null && result.Count > 0) return result[0].Path.LocalPath.ToString(); else @@ -304,22 +319,20 @@ internal async void ChangeQtFredDebugCommand() public async Task GetPath(string? folderRoot) { - if(MainWindow.instance != null && folderPath != null) + if(MainWindow.instance != null && folderRoot != null) { FilePickerOpenOptions options = new FilePickerOpenOptions(); options.AllowMultiple = false; options.Title = "Select the executable file"; - if (folderRoot != null) - { - options.SuggestedStartLocation = await MainWindow.instance.StorageProvider.TryGetFolderFromPathAsync(folderRoot); - } + options.SuggestedStartLocation = await MainWindow.instance.StorageProvider.TryGetFolderFromPathAsync(folderRoot); + + var topmostWindow = window == null ? MainWindow.instance : window; - var result = await MainWindow.instance.StorageProvider.OpenFilePickerAsync(options); + var result = await topmostWindow.StorageProvider.OpenFilePickerAsync(options); if (result != null && result.Count > 0) { - var file = new FileInfo(result[0].Path.LocalPath.ToString()); - return file.Name; + return Path.GetRelativePath(folderRoot, result[0].Path.LocalPath.ToString()); } } return null; @@ -331,10 +344,14 @@ internal async void AddCommand() { return; } - + if (folderPath != null) { - if(await CopyFilesRecursively(folderPath, BuildNewPath)) + CollapseData = true; + CollapseExecs = true; + CollapseArch = true; + string[] ignoreList = { ".pdb", ".lib", ".exp", ".a", ".map" }; + if(await KnUtils.CopyDirectoryAsync(folderPath, BuildNewPath, true, new CancellationTokenSource(), copyCallback, ignoreList)) { Mod mod = new Mod(); mod.fullPath = BuildNewPath + Path.DirectorySeparatorChar; @@ -485,7 +502,14 @@ internal async void AddCommand() Knossos.AddBuild(userBuild); FsoBuildsViewModel.Instance?.AddBuildToUi(userBuild); - ModCreated = true; + Dispatcher.UIThread.Invoke(() => { window?.Close(); }); + } + else + { + CollapseData = false; + CollapseExecs = false; + CollapseArch = false; + await MessageBox.Show(window, "An error has ocurred while copying files", "Filecopy error", MessageBox.MessageBoxButtons.OK); } } } @@ -531,37 +555,10 @@ private bool Verify() return true; } - private async Task CopyFilesRecursively(string sourcePath, string targetPath) + private async void copyCallback(string filename) { - return await Task.Factory.StartNew(() => { - try - { - Directory.CreateDirectory(targetPath); - foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories)) - { - Directory.CreateDirectory(dirPath.Replace(sourcePath, targetPath)); - } - var allFiles = Directory.GetFiles(sourcePath, "*.*", SearchOption.AllDirectories); - Dispatcher.UIThread.Invoke(()=> MaxFiles = allFiles.Length ); - foreach (string newPath in Directory.GetFiles(sourcePath, "*.*", SearchOption.AllDirectories)) - { - if (!newPath.ToLower().Contains(".pdb") && !newPath.ToLower().Contains(".lib") && !newPath.ToLower().Contains(".exp") && !newPath.ToLower().EndsWith(".a")) - { - System.IO.File.Copy(newPath, newPath.Replace(sourcePath, targetPath), true); - } - Dispatcher.UIThread.Invoke(() => CopyProgress++); - } - return true; - } - catch (Exception ex) - { - Log.Add(Log.LogSeverity.Error, "AddUserBuildViewModel.CopyFilesRecursively()", ex); - if (MainWindow.instance != null) - { - MessageBox.Show(MainWindow.instance, "Error while copying files:\n"+ex.Message.ToString(), "Filecopy error", MessageBox.MessageBoxButtons.OK); - } - return false; - } + await Dispatcher.UIThread.InvokeAsync(() => { + CopyProgress++; }); } } diff --git a/Knossos.NET/Views/Windows/AddUserBuildView.axaml b/Knossos.NET/Views/Windows/AddUserBuildView.axaml index c3a3d367..8bd14451 100644 --- a/Knossos.NET/Views/Windows/AddUserBuildView.axaml +++ b/Knossos.NET/Views/Windows/AddUserBuildView.axaml @@ -2,15 +2,15 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" + mc:Ignorable="d" d:DesignWidth="550" d:DesignHeight="600" x:Class="Knossos.NET.Views.Windows.AddUserBuildView" xmlns:v="using:Knossos.NET.Views" xmlns:vm="using:Knossos.NET.ViewModels" x:DataType="vm:AddUserBuildViewModel" Icon="/Assets/knossos-icon.ico" WindowStartupLocation="CenterScreen" - Title="Add Custom FSO Build" - SizeToContent="Width" + Title="Add Custom FSO Build" + Width="550" Height="600" CanResize="True"> @@ -19,49 +19,71 @@ - + - - X86 - X64 - AVX - AVX2 - ARM32 - ARM64 - RISCV32 - RISCV64 - - - - - - - - - - - - - - - - - - - - - - - - - + + + Build Data + + + + + + + + + + + + + Build CPU Arch + + + + X86 + X64 + AVX + AVX2 + ARM64 + RISCV64 + RISCV32 + ARM32 + + + + + + + Build Executables + + + + + + + + + + + + + + + + + + + + + + - + @@ -69,7 +91,6 @@ -