diff --git a/GarExtract.cs b/GarExtract.cs new file mode 100644 index 00000000..7f4f7258 --- /dev/null +++ b/GarExtract.cs @@ -0,0 +1,233 @@ +//! \file GarExtract.cs +//! \date Fri Jul 25 05:52:27 2014 +//! \brief Extract 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 Ookii.Dialogs.Wpf; +using GameRes; +using GARbro.GUI.Strings; + +namespace GARbro.GUI +{ + public partial class MainWindow : Window + { + /// + /// 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); + } + } +}