diff --git a/RomRepoMgr.Core/Workers/Compression.cs b/RomRepoMgr.Core/Workers/Compression.cs index c660bb9..3df8c39 100644 --- a/RomRepoMgr.Core/Workers/Compression.cs +++ b/RomRepoMgr.Core/Workers/Compression.cs @@ -1,8 +1,35 @@ +/****************************************************************************** +// RomRepoMgr - ROM repository manager +// ---------------------------------------------------------------------------- +// +// Author(s) : Natalia Portillo +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2017-2020 Natalia Portillo +*******************************************************************************/ + using System; +using System.Diagnostics; using System.IO; using RomRepoMgr.Core.EventArgs; using SharpCompress.Compressors; using SharpCompress.Compressors.LZMA; +using ErrorEventArgs = RomRepoMgr.Core.EventArgs.ErrorEventArgs; namespace RomRepoMgr.Core.Workers { @@ -12,6 +39,8 @@ namespace RomRepoMgr.Core.Workers public event EventHandler SetProgressBounds; public event EventHandler SetProgress; + public event EventHandler FinishedWithText; + public event EventHandler FailedWithText; public void CompressFile(string source, string destination) { @@ -53,5 +82,141 @@ namespace RomRepoMgr.Core.Workers zStream.Close(); outFs.Dispose(); } + + public void CheckUnar(string unArPath) + { + if(string.IsNullOrWhiteSpace(unArPath)) + { + FailedWithText?.Invoke(this, new ErrorEventArgs + { + Message = "unar path is not set." + }); + + return; + } + + string unarFolder = Path.GetDirectoryName(unArPath); + string extension = Path.GetExtension(unArPath); + string unarfilename = Path.GetFileNameWithoutExtension(unArPath); + string lsarfilename = unarfilename.Replace("unar", "lsar"); + string unarPath = Path.Combine(unarFolder, unarfilename + extension); + string lsarPath = Path.Combine(unarFolder, lsarfilename + extension); + + if(!File.Exists(unarPath)) + { + FailedWithText?.Invoke(this, new ErrorEventArgs + { + Message = $"Cannot find unar executable at {unarPath}." + }); + + return; + } + + if(!File.Exists(lsarPath)) + { + FailedWithText?.Invoke(this, new ErrorEventArgs + { + Message = "Cannot find lsar executable." + }); + + return; + } + + string unarOut, lsarOut; + + try + { + var unarProcess = new Process + { + StartInfo = + { + FileName = unarPath, + CreateNoWindow = true, + RedirectStandardOutput = true, + UseShellExecute = false + } + }; + + unarProcess.Start(); + unarProcess.WaitForExit(); + unarOut = unarProcess.StandardOutput.ReadToEnd(); + } + catch + { + FailedWithText?.Invoke(this, new ErrorEventArgs + { + Message = "Cannot run unar." + }); + + return; + } + + try + { + var lsarProcess = new Process + { + StartInfo = + { + FileName = lsarPath, + CreateNoWindow = true, + RedirectStandardOutput = true, + UseShellExecute = false + } + }; + + lsarProcess.Start(); + lsarProcess.WaitForExit(); + lsarOut = lsarProcess.StandardOutput.ReadToEnd(); + } + catch + { + FailedWithText?.Invoke(this, new ErrorEventArgs + { + Message = "Cannot run lsar." + }); + + return; + } + + if(!unarOut.StartsWith("unar ", StringComparison.CurrentCulture)) + { + FailedWithText?.Invoke(this, new ErrorEventArgs + { + Message = "Not the correct unar executable" + }); + + return; + } + + if(!lsarOut.StartsWith("lsar ", StringComparison.CurrentCulture)) + { + FailedWithText?.Invoke(this, new ErrorEventArgs + { + Message = "Not the correct lsar executable" + }); + + return; + } + + var versionProcess = new Process + { + StartInfo = + { + FileName = unarPath, + CreateNoWindow = true, + RedirectStandardOutput = true, + UseShellExecute = false, + Arguments = "-v" + } + }; + + versionProcess.Start(); + versionProcess.WaitForExit(); + + FinishedWithText?.Invoke(this, new MessageEventArgs + { + Message = versionProcess.StandardOutput.ReadToEnd().TrimEnd('\n') + }); + } } } \ No newline at end of file diff --git a/RomRepoMgr.Database/Context.cs b/RomRepoMgr.Database/Context.cs index 7470111..5e1f1a4 100644 --- a/RomRepoMgr.Database/Context.cs +++ b/RomRepoMgr.Database/Context.cs @@ -57,6 +57,8 @@ namespace RomRepoMgr.Database public DbSet Machines { get; set; } public DbSet FilesByMachines { get; set; } + public static void ReplaceSingleton(string dbPath) => _singleton = Create(dbPath); + public static Context Create(string dbPath) { var optionsBuilder = new DbContextOptionsBuilder(); diff --git a/RomRepoMgr/RomRepoMgr.csproj b/RomRepoMgr/RomRepoMgr.csproj index a06c871..42f5720 100644 --- a/RomRepoMgr/RomRepoMgr.csproj +++ b/RomRepoMgr/RomRepoMgr.csproj @@ -18,6 +18,7 @@ + diff --git a/RomRepoMgr/ViewModels/MainWindowViewModel.cs b/RomRepoMgr/ViewModels/MainWindowViewModel.cs index 0c5aa09..d2f329a 100644 --- a/RomRepoMgr/ViewModels/MainWindowViewModel.cs +++ b/RomRepoMgr/ViewModels/MainWindowViewModel.cs @@ -75,9 +75,9 @@ namespace RomRepoMgr.ViewModels internal async void ExecuteSettingsCommand() { - /*var dialog = new SettingsDialog(); - dialog.DataContext = new SettingsViewModel(dialog, false); - await dialog.ShowDialog(_view);*/ + var dialog = new SettingsDialog(); + dialog.DataContext = new SettingsViewModel(dialog); + await dialog.ShowDialog(_view); } internal void ExecuteExitCommand() => diff --git a/RomRepoMgr/ViewModels/SettingsViewModel.cs b/RomRepoMgr/ViewModels/SettingsViewModel.cs new file mode 100644 index 0000000..45b1521 --- /dev/null +++ b/RomRepoMgr/ViewModels/SettingsViewModel.cs @@ -0,0 +1,334 @@ +/****************************************************************************** +// RomRepoMgr - ROM repository manager +// ---------------------------------------------------------------------------- +// +// Author(s) : Natalia Portillo +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2017-2020 Natalia Portillo +*******************************************************************************/ + +using System; +using System.IO; +using System.Reactive; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Threading; +using MessageBox.Avalonia; +using MessageBox.Avalonia.Enums; +using Microsoft.EntityFrameworkCore; +using ReactiveUI; +using RomRepoMgr.Core.EventArgs; +using RomRepoMgr.Core.Workers; +using RomRepoMgr.Database; +using RomRepoMgr.Views; +using ErrorEventArgs = RomRepoMgr.Core.EventArgs.ErrorEventArgs; + +namespace RomRepoMgr.ViewModels +{ + public sealed class SettingsViewModel : ViewModelBase + { + readonly SettingsDialog _view; + bool _databaseChanged; + string _databasePath; + bool _repositoryChanged; + string _repositoryPath; + bool _temporaryChanged; + string _temporaryPath; + bool _unArChanged; + string _unArPath; + string _unArVersion; + + public SettingsViewModel(SettingsDialog view) + { + _view = view; + _databaseChanged = false; + _repositoryChanged = false; + _temporaryChanged = false; + _unArChanged = false; + + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + UnArCommand = ReactiveCommand.Create(ExecuteUnArCommand); + TemporaryCommand = ReactiveCommand.Create(ExecuteTemporaryCommand); + RepositoryCommand = ReactiveCommand.Create(ExecuteRepositoryCommand); + DatabaseCommand = ReactiveCommand.Create(ExecuteDatabaseCommand); + SaveCommand = ReactiveCommand.Create(ExecuteSaveCommand); + + DatabasePath = Settings.Settings.Current.DatabasePath; + RepositoryPath = Settings.Settings.Current.RepositoryPath; + TemporaryPath = Settings.Settings.Current.TemporaryFolder; + UnArPath = Settings.Settings.Current.UnArchiverPath; + + if(!string.IsNullOrWhiteSpace(UnArPath)) + CheckUnar(); + } + + public string ChooseLabel => "Choose..."; + public string Title => "Settings"; + public string CloseLabel => "Close"; + public string DatabaseLabel => "Database file"; + public string RepositoryLabel => "Repository folder"; + public string TemporaryLabel => "Temporary folder"; + public string UnArPathLabel => "Path to UnAr"; + + public ReactiveCommand UnArCommand { get; } + public ReactiveCommand TemporaryCommand { get; } + public ReactiveCommand RepositoryCommand { get; } + public ReactiveCommand DatabaseCommand { get; } + public ReactiveCommand CloseCommand { get; } + public ReactiveCommand SaveCommand { get; } + + public string DatabasePath + { + get => _databasePath; + set + { + this.RaiseAndSetIfChanged(ref _databasePath, value); + _databaseChanged = true; + } + } + + public string RepositoryPath + { + get => _repositoryPath; + set + { + this.RaiseAndSetIfChanged(ref _repositoryPath, value); + + // TODO: Refresh repository existing files + _repositoryChanged = true; + } + } + + public string TemporaryPath + { + get => _temporaryPath; + set + { + this.RaiseAndSetIfChanged(ref _temporaryPath, value); + _temporaryChanged = true; + } + } + + public string UnArPath + { + get => _unArPath; + set => this.RaiseAndSetIfChanged(ref _unArPath, value); + } + + public string UnArVersion + { + get => _unArVersion; + set => this.RaiseAndSetIfChanged(ref _unArVersion, value); + } + + public string SaveLabel => "Save"; + + void CheckUnar() + { + var worker = new Compression(); + + worker.FinishedWithText += CheckUnarFinished; + worker.FailedWithText += CheckUnarFailed; + + Task.Run(() => worker.CheckUnar(UnArPath)); + } + + async void CheckUnarFailed(object sender, ErrorEventArgs args) + { + UnArVersion = ""; + UnArPath = ""; + + await MessageBoxManager.GetMessageBoxStandardWindow("Error", $"{args.Message}", ButtonEnum.Ok, Icon.Error). + ShowDialog(_view); + } + + void CheckUnarFinished(object? sender, MessageEventArgs args) => Dispatcher.UIThread.Post(() => + { + UnArVersion = string.Format("The Unarchiver version {0}", args.Message); + _unArChanged = true; + }); + + void ExecuteCloseCommand() => _view.Close(); + + async void ExecuteUnArCommand() + { + var dlgFile = new OpenFileDialog(); + dlgFile.Title = "Choose UnArchiver executable"; + dlgFile.AllowMultiple = false; + + if(!string.IsNullOrWhiteSpace(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles))) + dlgFile.Directory = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); + + string[] result = await dlgFile.ShowAsync(_view); + + if(result?.Length != 1) + return; + + UnArPath = result[0]; + CheckUnar(); + } + + async void ExecuteTemporaryCommand() + { + var dlgFolder = new OpenFolderDialog(); + dlgFolder.Title = "Choose temporary folder"; + + string result = await dlgFolder.ShowAsync(_view); + + if(result == null) + return; + + TemporaryPath = result; + } + + async void ExecuteRepositoryCommand() + { + var dlgFolder = new OpenFolderDialog(); + dlgFolder.Title = "Choose repository folder"; + + string result = await dlgFolder.ShowAsync(_view); + + if(result == null) + return; + + RepositoryPath = result; + } + + async void ExecuteDatabaseCommand() + { + var dlgFile = new SaveFileDialog(); + dlgFile.InitialFileName = "romrepo.db"; + dlgFile.Directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + dlgFile.Title = "Choose database to open /create"; + + string result = await dlgFile.ShowAsync(_view); + + if(result == null) + return; + + if(File.Exists(result)) + { + ButtonResult btnResult = await MessageBoxManager. + GetMessageBoxStandardWindow("File exists", + "Do you want to try to open the existing file as a database?", + ButtonEnum.YesNo, Icon.Database). + ShowDialog(_view); + + if(btnResult == ButtonResult.Yes) + { + try + { + var ctx = Context.Create(result); + await ctx.Database.MigrateAsync(); + } + catch(Exception) + { + btnResult = await MessageBoxManager. + GetMessageBoxStandardWindow("Could not use database", + "An error occurred trying to use the chosen file as a database.\nDo you want to delete the file?", + ButtonEnum.YesNo, Icon.Error).ShowDialog(_view); + + if(btnResult == ButtonResult.No) + return; + + try + { + File.Delete(result); + } + catch(Exception) + { + await MessageBoxManager. + GetMessageBoxStandardWindow("Could not delete file", + "An error occurred trying to delete the chosen.", + ButtonEnum.Ok, Icon.Error).ShowDialog(_view); + + return; + } + } + } + else + { + btnResult = await MessageBoxManager. + GetMessageBoxStandardWindow("File exists", "Do you want to delete the file?", + ButtonEnum.YesNo, Icon.Error).ShowDialog(_view); + + if(btnResult == ButtonResult.No) + return; + + try + { + File.Delete(result); + } + catch(Exception) + { + await MessageBoxManager. + GetMessageBoxStandardWindow("Could not delete file", + "An error occurred trying to delete the chosen.", + ButtonEnum.Ok, Icon.Error).ShowDialog(_view); + + return; + } + } + } + + try + { + var ctx = Context.Create(result); + await ctx.Database.MigrateAsync(); + } + catch(Exception) + { + await MessageBoxManager. + GetMessageBoxStandardWindow("Could not use database", + "An error occurred trying to use the chosen file as a database.", + ButtonEnum.Ok, Icon.Error).ShowDialog(_view); + + return; + } + + DatabasePath = result; + } + + void ExecuteSaveCommand() + { + if(_databaseChanged) + { + Settings.Settings.Current.DatabasePath = DatabasePath; + Context.ReplaceSingleton(DatabasePath); + } + + if(_repositoryChanged) + Settings.Settings.Current.RepositoryPath = RepositoryPath; + + if(_temporaryChanged) + Settings.Settings.Current.TemporaryFolder = TemporaryPath; + + if(_unArChanged) + Settings.Settings.Current.UnArchiverPath = UnArPath; + + if(_databaseChanged || + _repositoryChanged || + _temporaryChanged || + _unArChanged) + Settings.Settings.SaveSettings(); + + _view.Close(); + } + } +} \ No newline at end of file diff --git a/RomRepoMgr/Views/SettingsDialog.xaml b/RomRepoMgr/Views/SettingsDialog.xaml new file mode 100644 index 0000000..9e1553f --- /dev/null +++ b/RomRepoMgr/Views/SettingsDialog.xaml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/RomRepoMgr/Views/SettingsDialog.xaml.cs b/RomRepoMgr/Views/SettingsDialog.xaml.cs new file mode 100644 index 0000000..8267261 --- /dev/null +++ b/RomRepoMgr/Views/SettingsDialog.xaml.cs @@ -0,0 +1,37 @@ +/****************************************************************************** +// RomRepoMgr - ROM repository manager +// ---------------------------------------------------------------------------- +// +// Author(s) : Natalia Portillo +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2020 Natalia Portillo +*******************************************************************************/ + +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace RomRepoMgr.Views +{ + public sealed class SettingsDialog : Window + { + public SettingsDialog() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); + } +} \ No newline at end of file