From 3a7d2111be33204332961a105ad49239befda6f1 Mon Sep 17 00:00:00 2001 From: morkt Date: Fri, 25 Jul 2014 07:05:55 +0400 Subject: [PATCH] implemented archive creation frontend. --- CreateArchive.xaml.cs | 30 +++- GARbro.GUI.csproj | 8 +- GameRes/GameRes.cs | 39 ++++- GameRes/Strings/garStrings.Designer.cs | 18 +++ GameRes/Strings/garStrings.resx | 6 + GameRes/Strings/garStrings.ru-RU.resx | 6 + GarCreate.cs | 140 ++++++++++++++++ MainWindow.xaml.cs | 215 ------------------------- Strings/guiStrings.Designer.cs | 21 +++ Strings/guiStrings.resx | 9 ++ Strings/guiStrings.ru-RU.resx | 9 ++ 11 files changed, 278 insertions(+), 223 deletions(-) create mode 100644 GarCreate.cs diff --git a/CreateArchive.xaml.cs b/CreateArchive.xaml.cs index b31cd94a..98d25107 100644 --- a/CreateArchive.xaml.cs +++ b/CreateArchive.xaml.cs @@ -1,12 +1,12 @@ using System.IO; using System.Windows; +using System.Windows.Controls; using System.Windows.Input; using System.Text; using System.Linq; using Microsoft.Win32; using GARbro.GUI.Strings; using GameRes; -using System.Windows.Controls; namespace GARbro.GUI { @@ -15,17 +15,31 @@ namespace GARbro.GUI /// public partial class CreateArchiveDialog : Window { - public CreateArchiveDialog () + public CreateArchiveDialog (string initial_name = "") { InitializeComponent (); - this.ArchiveFormat.ItemsSource = FormatCatalog.Instance.ArcFormats; + if (!string.IsNullOrEmpty (initial_name)) + { + var format = this.ArchiveFormat.SelectedItem as ArchiveFormat; + if (null != format) + ArchiveName.Text = Path.ChangeExtension (initial_name, format.Extensions.First()); + } } public ResourceOptions ArchiveOptions { get; private set; } void Button_Click (object sender, RoutedEventArgs e) { + string arc_name = Path.GetFullPath (ArchiveName.Text); + if (File.Exists (arc_name)) + { + string text = string.Format (guiStrings.MsgOverwrite, arc_name); + var rc = MessageBox.Show (this, text, guiStrings.TextConfirmOverwrite, MessageBoxButton.YesNo, + MessageBoxImage.Question); + if (MessageBoxResult.Yes != rc) + return; + } DialogResult = true; } @@ -92,11 +106,21 @@ namespace GARbro.GUI } OptionsWidget.Content = widget; OptionsWidget.Visibility = null != widget ? Visibility.Visible : Visibility.Hidden; + + if (!string.IsNullOrEmpty (ArchiveName.Text)) + { + ArchiveName.Text = Path.ChangeExtension (ArchiveName.Text, format.Extensions.First()); + } } void CanExecuteAlways (object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = true; } + + private void ArchiveName_TextChanged (object sender, RoutedEventArgs e) + { + this.ButtonOk.IsEnabled = ArchiveName.Text.Length > 0; + } } } diff --git a/GARbro.GUI.csproj b/GARbro.GUI.csproj index c443c9c8..d69a7023 100644 --- a/GARbro.GUI.csproj +++ b/GARbro.GUI.csproj @@ -69,7 +69,7 @@ LocalIntranet - Properties\app.manifest + app.manifest true @@ -130,11 +130,14 @@ ExtractFile.xaml + + - + + True True @@ -198,6 +201,7 @@ guiStrings.Designer.cs + diff --git a/GameRes/GameRes.cs b/GameRes/GameRes.cs index e5ffe475..c5bd4335 100644 --- a/GameRes/GameRes.cs +++ b/GameRes/GameRes.cs @@ -144,10 +144,10 @@ namespace GameRes } /// - /// Create resource archive named containing entries from the + /// Create resource wihin stream containing entries from the /// supplied and applying necessary . /// - public virtual void Create (string filename, IEnumerable list, ResourceOptions options = null) + public virtual void Create (Stream file, IEnumerable list, ResourceOptions options = null) { throw new NotImplementedException ("ArchiveFormat.Create is not implemented"); } @@ -329,7 +329,7 @@ namespace GameRes } } - public class InvalidFormatException : Exception + public class InvalidFormatException : FileFormatException { public InvalidFormatException() : base(garStrings.MsgInvalidFormat) { } public InvalidFormatException (string msg) : base (msg) { } @@ -346,4 +346,37 @@ namespace GameRes public InvalidEncryptionScheme() : base(garStrings.MsgInvalidEncryption) { } public InvalidEncryptionScheme (string msg) : base (msg) { } } + + public class FileSizeException : Exception + { + public FileSizeException () : base (garStrings.MsgFileTooLarge) { } + public FileSizeException (string msg) : base (msg) { } + } + + public class InvalidFileName : Exception + { + public string FileName { get; set; } + + public InvalidFileName (string filename) + : this (filename, garStrings.MsgInvalidFileName) + { + } + + public InvalidFileName (string filename, string message) + : base (message) + { + FileName = filename; + } + + public InvalidFileName (string filename, Exception X) + : this (filename, garStrings.MsgInvalidFileName, X) + { + } + + public InvalidFileName (string filename, string message, Exception X) + : base (message, X) + { + FileName = filename; + } + } } diff --git a/GameRes/Strings/garStrings.Designer.cs b/GameRes/Strings/garStrings.Designer.cs index 7b972b7c..7cdce5cd 100644 --- a/GameRes/Strings/garStrings.Designer.cs +++ b/GameRes/Strings/garStrings.Designer.cs @@ -60,6 +60,15 @@ namespace GameRes.Strings { } } + /// + /// Looks up a localized string similar to File is too large. + /// + public static string MsgFileTooLarge { + get { + return ResourceManager.GetString("MsgFileTooLarge", resourceCulture); + } + } + /// /// Looks up a localized string similar to Inappropriate encryption scheme. /// @@ -69,6 +78,15 @@ namespace GameRes.Strings { } } + /// + /// Looks up a localized string similar to Invalid file name. + /// + public static string MsgInvalidFileName { + get { + return ResourceManager.GetString("MsgInvalidFileName", resourceCulture); + } + } + /// /// Looks up a localized string similar to Invalid file format. /// diff --git a/GameRes/Strings/garStrings.resx b/GameRes/Strings/garStrings.resx index 6314d2b3..480912c6 100644 --- a/GameRes/Strings/garStrings.resx +++ b/GameRes/Strings/garStrings.resx @@ -117,9 +117,15 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + File is too large + Inappropriate encryption scheme + + Invalid file name + Invalid file format diff --git a/GameRes/Strings/garStrings.ru-RU.resx b/GameRes/Strings/garStrings.ru-RU.resx index 6a772f39..c8040a17 100644 --- a/GameRes/Strings/garStrings.ru-RU.resx +++ b/GameRes/Strings/garStrings.ru-RU.resx @@ -117,9 +117,15 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Слишком большой размер файла + Неподходящий метод шифрования + + Недопустимое имя файла + Некорректный формат файла diff --git a/GarCreate.cs b/GarCreate.cs new file mode 100644 index 00000000..791ec931 --- /dev/null +++ b/GarCreate.cs @@ -0,0 +1,140 @@ +//! \file GarCreate.cs +//! \date Fri Jul 25 05:56:29 2014 +//! \brief Create archive frontend. +// +// Copyright (C) 2014 by morkt +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +using System; +using System.IO; +using System.Linq; +using System.Collections.Generic; +using System.Diagnostics; +using System.Windows; +using System.Windows.Input; +using GameRes; +using GARbro.GUI.Strings; + +namespace GARbro.GUI +{ + public partial class MainWindow : Window + { + private void CreateArchiveExec (object sender, ExecutedRoutedEventArgs e) + { + try + { + Directory.SetCurrentDirectory (CurrentPath); + var items = CurrentDirectory.SelectedItems.Cast(); + string arc_name = Path.GetFileName (CurrentPath); + if (!items.Skip (1).Any()) // items.Count() == 1 + { + var item = items.First(); + if (item.IsDirectory) + arc_name = Path.GetFileNameWithoutExtension (item.Name); + } + + var dialog = new CreateArchiveDialog (arc_name); + dialog.Owner = this; + if (!dialog.ShowDialog().Value) + return; + if (string.IsNullOrEmpty (dialog.ArchiveName.Text)) + { + SetStatusText ("Archive name is empty"); + return; + } + var format = dialog.ArchiveFormat.SelectedItem as ArchiveFormat; + if (null == format) + { + SetStatusText ("Format is not selected"); + return; + } + + IEnumerable file_list; + if (format.IsHierarchic) + file_list = BuildFileList (items, AddFilesRecursive); + else + file_list = BuildFileList (items, AddFilesFromDir); + + arc_name = Path.GetFullPath (dialog.ArchiveName.Text); + using (var tmp_file = new GARbro.Shell.TemporaryFile (Path.GetDirectoryName (arc_name), + Path.GetRandomFileName())) + { + using (var file = File.Create (tmp_file.Name)) + format.Create (file, file_list, dialog.ArchiveOptions); + GARbro.Shell.File.Rename (tmp_file.Name, arc_name); + } + } + catch (Exception X) + { + PopupError (X.Message, guiStrings.TextCreateArchiveError); + } + } + + delegate void AddFilesEnumerator (IList list, string path, DirectoryInfo path_info); + + IEnumerable BuildFileList (IEnumerable files, AddFilesEnumerator add_files) + { + var list = new List(); + foreach (var entry in files) + { + if (entry.IsDirectory) + { + if (".." != entry.Name) + { + var dir = new DirectoryInfo (entry.Name); + add_files (list, entry.Name, dir); + } + } + else if (entry.Size < uint.MaxValue) + { + list.Add (entry.Source); + } + } + return list; + } + + void AddFilesFromDir (IList list, string path, DirectoryInfo dir) + { + foreach (var file in dir.EnumerateFiles()) + { + if (0 != (file.Attributes & (FileAttributes.Hidden | FileAttributes.System))) + continue; + if (file.Length >= uint.MaxValue) + continue; + string name = Path.Combine (path, file.Name); + var e = FormatCatalog.Instance.CreateEntry (name); + e.Size = (uint)file.Length; + list.Add (e); + } + } + + void AddFilesRecursive (IList list, string path, DirectoryInfo info) + { + foreach (var dir in info.EnumerateDirectories()) + { + string subdir = Path.Combine (path, dir.Name); + var subdir_info = new DirectoryInfo (subdir); + AddFilesRecursive (list, subdir, subdir_info); + } + AddFilesFromDir (list, path, info); + } + } +} diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index 9395c5ed..40fbff63 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -36,7 +36,6 @@ using System.Windows.Media; using System.Windows.Threading; using System.Threading; using Microsoft.VisualBasic.FileIO; -using Ookii.Dialogs.Wpf; using GARbro.GUI.Properties; using GARbro.GUI.Strings; using GameRes; @@ -772,220 +771,6 @@ namespace GARbro.GUI */ } - /// - /// Handle "Extract item" command. - /// - private void ExtractItemExec (object sender, ExecutedRoutedEventArgs e) - { - var entry = CurrentDirectory.SelectedItem as EntryViewModel; - if (null == entry) - return; - try - { - if (!ViewModel.IsArchive) - { - if (!entry.IsDirectory) - { - var arc_dir = CurrentPath; - var source = Path.Combine (arc_dir, entry.Name); - string destination = arc_dir; - // extract into directory named after archive - if (!string.IsNullOrEmpty (Path.GetExtension (entry.Name))) - destination = Path.GetFileNameWithoutExtension (source); - ExtractArchive (source, destination); - } - } - else if (null != m_app.CurrentArchive) - { - var vm = ViewModel as ArchiveViewModel; - string destination = Path.GetDirectoryName (vm.Path); - string arc_name = Path.GetFileName (vm.Path); - if (entry.Name == ".." && vm.SubDir == "") // root entry - { - ExtractArchive (m_app.CurrentArchive, arc_name, destination); - } - else - { - ExtractFileFromArchive (entry, destination); - } - } - } - catch (Exception X) - { - PopupError (X.Message, guiStrings.MsgErrorExtracting); - } - } - - private void ExtractArchive (string path, string destination) - { - string arc_name = Path.GetFileName (path); - FormatCatalog.Instance.LastError = null; - var arc = ArcFile.TryOpen (path); - if (null != arc) - { - ExtractArchive (arc, arc_name, destination); - } - else - { - string error_message; - if (FormatCatalog.Instance.LastError != null) - error_message = FormatCatalog.Instance.LastError.Message; - else - error_message = guiStrings.MsgUnknownFormat; - SetStatusText (string.Format ("{1}: {0}", error_message, arc_name)); - } - } - - private void ExtractArchive (ArcFile arc, string arc_name, string destination) - { - if (0 == arc.Dir.Count) - { - SetStatusText (string.Format ("{1}: {0}", guiStrings.MsgEmptyArchive, arc_name)); - return; - } - var extractDialog = new ExtractArchiveDialog (arc_name, destination); - extractDialog.Owner = this; - var result = extractDialog.ShowDialog(); - if (!result.Value) - return; - - destination = extractDialog.DestinationDir.Text; - if (!string.IsNullOrEmpty (destination)) - { - destination = Path.GetFullPath (destination); - Trace.WriteLine (destination, "Extract destination"); - StopWatchDirectoryChanges(); - try - { - Directory.CreateDirectory (destination); - Directory.SetCurrentDirectory (destination); - } - finally - { - m_watcher.EnableRaisingEvents = true; - } - } - IEnumerable file_list = arc.Dir; - bool skip_images = !extractDialog.ExtractImages.IsChecked.Value; - bool skip_script = !extractDialog.ExtractText.IsChecked.Value; - if (skip_images || skip_script) - file_list = file_list.Where (f => !(skip_images && f.Type == "image") && !(skip_script && f.Type == "script")); - - if (!file_list.Any()) - { - SetStatusText (string.Format ("{1}: {0}", guiStrings.MsgNoFiles, arc_name)); - return; - } - ImageFormat image_format = null; - if (!skip_images) - image_format = extractDialog.GetImageFormat (extractDialog.ImageConversionFormat); - - SetStatusText (string.Format(guiStrings.MsgExtractingTo, arc_name, destination)); - ExtractFilesFromArchive (string.Format (guiStrings.MsgExtractingArchive, arc_name), - arc, file_list, image_format); - } - - private void ExtractFileFromArchive (EntryViewModel entry, string destination) - { - var extractDialog = new ExtractFile (entry, destination); - extractDialog.Owner = this; - var result = extractDialog.ShowDialog(); - if (!result.Value) - return; - - var file_list = (ViewModel as ArchiveViewModel).GetFiles (entry); - - destination = extractDialog.DestinationDir.Text; - if (!string.IsNullOrEmpty (destination)) - { - destination = Path.GetFullPath (destination); - Directory.CreateDirectory (destination); - Directory.SetCurrentDirectory (destination); - } - ImageFormat format = null; - if (entry.Type == "image") - format = extractDialog.GetImageFormat (extractDialog.ImageConversionFormat); - - string arc_name = Path.GetFileName (CurrentPath); - ExtractFilesFromArchive (string.Format (guiStrings.MsgExtractingFile, arc_name), - m_app.CurrentArchive, file_list, format); - } - - private void ExtractFilesFromArchive (string text, ArcFile arc, IEnumerable file_list, - ImageFormat image_format = null) - { - file_list = file_list.OrderBy (e => e.Offset); - var extractProgressDialog = new ProgressDialog () - { - WindowTitle = guiStrings.TextTitle, - Text = text, - Description = "", - MinimizeBox = true, - }; - if (!file_list.Skip (1).Any()) // 1 == file_list.Count() - { - extractProgressDialog.Description = file_list.First().Name; - extractProgressDialog.ProgressBarStyle = ProgressBarStyle.MarqueeProgressBar; - } - extractProgressDialog.DoWork += (s, e) => - { - try - { - int total = file_list.Count(); - int i = 0; - foreach (var entry in file_list) - { - if (extractProgressDialog.CancellationPending) - break; - if (total > 1) - extractProgressDialog.ReportProgress (i*100/total, null, entry.Name); - if (null != image_format && entry.Type == "image") - ExtractImage (arc, entry, image_format); - else - arc.Extract (entry); - ++i; - } - SetStatusText (string.Format (guiStrings.MsgExtractCompletePlural, i, - Localization.Plural (i, "file"))); - } - catch (Exception X) - { - SetStatusText (X.Message); - } - }; - extractProgressDialog.RunWorkerCompleted += (s, e) => { - extractProgressDialog.Dispose(); - if (!ViewModel.IsArchive) - { - arc.Dispose(); - Dispatcher.Invoke (RefreshView); - } - }; - extractProgressDialog.ShowDialog (this); - } - - private void CreateArchiveExec (object sender, ExecutedRoutedEventArgs e) - { - try - { - var dialog = new CreateArchiveDialog(); - dialog.Owner = this; - if (!dialog.ShowDialog().Value || string.IsNullOrEmpty (dialog.ArchiveName.Text)) - return; - var format = dialog.ArchiveFormat.SelectedItem as ArchiveFormat; - if (null == format) - return; - - string arc_name = Path.GetFullPath (dialog.ArchiveName.Text); - var items = CurrentDirectory.SelectedItems.Cast(); - format.Create (arc_name, items.Select (entry => entry.Source), dialog.ArchiveOptions); - } - catch (Exception X) - { - PopupError (X.Message, guiStrings.TextCreateArchiveError); - } - } - /// /// Handle "Exit" command. /// diff --git a/Strings/guiStrings.Designer.cs b/Strings/guiStrings.Designer.cs index c09c3516..6aada000 100644 --- a/Strings/guiStrings.Designer.cs +++ b/Strings/guiStrings.Designer.cs @@ -438,6 +438,18 @@ namespace GARbro.GUI.Strings { } } + /// + /// Looks up a localized string similar to File {0} + ///already exists. + /// + ///Overwrite?. + /// + public static string MsgOverwrite { + get { + return ResourceManager.GetString("MsgOverwrite", resourceCulture); + } + } + /// /// Looks up a localized string similar to Ready. /// @@ -546,6 +558,15 @@ namespace GARbro.GUI.Strings { } } + /// + /// Looks up a localized string similar to Confirm overwrite. + /// + public static string TextConfirmOverwrite { + get { + return ResourceManager.GetString("TextConfirmOverwrite", resourceCulture); + } + } + /// /// Looks up a localized string similar to Create archive. /// diff --git a/Strings/guiStrings.resx b/Strings/guiStrings.resx index 493ce39c..0d73d843 100644 --- a/Strings/guiStrings.resx +++ b/Strings/guiStrings.resx @@ -318,4 +318,13 @@ Forward + + File {0} +already exists. + +Overwrite? + + + Confirm overwrite + \ No newline at end of file diff --git a/Strings/guiStrings.ru-RU.resx b/Strings/guiStrings.ru-RU.resx index 6fafdeb5..46572e07 100644 --- a/Strings/guiStrings.ru-RU.resx +++ b/Strings/guiStrings.ru-RU.resx @@ -321,4 +321,13 @@ Вперёд + + Файл под именем '{0}' +уже существует. + +Перезаписать? + + + Подтвердите перезапись + \ No newline at end of file