//! \file GarConvert.cs //! \date Fri Aug 22 08:22:47 2014 //! \brief Game resources conversion methods. // // Copyright (C) 2014-2015 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.Windows; using System.Windows.Input; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using GameRes; using GARbro.GUI.Strings; using GARbro.GUI.Properties; using Ookii.Dialogs.Wpf; namespace GARbro.GUI { public partial class MainWindow : Window { /// /// Convert selected images to another format. /// void ConvertMediaExec (object sender, ExecutedRoutedEventArgs e) { if (ViewModel.IsArchive) return; var source = from entry in CurrentDirectory.SelectedItems.Cast() where entry.Type == "image" || entry.Type == "audio" select entry.Source; if (!source.Any()) { PopupError (guiStrings.MsgNoMediaFiles, guiStrings.TextMediaConvertError); return; } var convert_dialog = new ConvertMedia(); convert_dialog.Owner = this; var result = convert_dialog.ShowDialog() ?? false; if (!result) return; var format = convert_dialog.ImageConversionFormat.SelectedItem as ImageFormat; if (null == format) { Trace.WriteLine ("Format is not selected", "ConvertMediaExec"); return; } try { Directory.SetCurrentDirectory (ViewModel.Path); var converter = new GarConvertMedia (this); converter.IgnoreErrors = convert_dialog.IgnoreErrors.IsChecked ?? false; converter.Convert (source, format); } catch (Exception X) { PopupError (X.Message, guiStrings.TextMediaConvertError); } } } internal class GarConvertMedia { private MainWindow m_main; private ProgressDialog m_progress_dialog; private IEnumerable m_source; private ImageFormat m_image_format; private Exception m_pending_error; private List> m_failed = new List>(); public bool IgnoreErrors { get; set; } public IEnumerable> FailedFiles { get { return m_failed; } } public GarConvertMedia (MainWindow parent) { m_main = parent; } public void Convert (IEnumerable images, ImageFormat format) { m_main.StopWatchDirectoryChanges(); m_source = images; m_image_format = format; m_progress_dialog = new ProgressDialog () { WindowTitle = guiStrings.TextTitle, Text = "Converting image", Description = "", MinimizeBox = true, }; m_progress_dialog.DoWork += ConvertWorker; m_progress_dialog.RunWorkerCompleted += OnConvertComplete; m_progress_dialog.ShowDialog (m_main); } void ConvertWorker (object sender, DoWorkEventArgs e) { m_pending_error = null; try { int total = m_source.Count(); int i = 0; foreach (var entry in m_source) { if (m_progress_dialog.CancellationPending) throw new OperationCanceledException(); var filename = entry.Name; int progress = i++*100/total; m_progress_dialog.ReportProgress (progress, string.Format (guiStrings.MsgConvertingFile, Path.GetFileName (filename)), null); try { if ("image" == entry.Type) ConvertImage (filename); else if ("audio" == entry.Type) ConvertAudio (filename); } catch (NotImplementedException X) { // target format creation not implemented m_pending_error = X; break; } catch (Exception X) { if (!IgnoreErrors) throw; m_failed.Add (Tuple.Create (Path.GetFileName (filename), X.Message)); } } } catch (Exception X) { m_pending_error = X; } } public static readonly HashSet CommonAudioFormats = new HashSet { "wav", "mp3", "ogg" }; void ConvertAudio (string filename) { using (var file = File.OpenRead (filename)) using (var input = AudioFormat.Read (file)) { if (null == input) return; var source_ext = Path.GetExtension (filename).TrimStart ('.').ToLowerInvariant(); string source_format = input.SourceFormat; if (CommonAudioFormats.Contains (source_format)) { if (source_ext == source_format) return; string output_name = Path.ChangeExtension (filename, source_format); using (var output = CreateNewFile (output_name)) { input.Source.Position = 0; input.Source.CopyTo (output); } } else { if (source_ext == "wav") return; string output_name = Path.ChangeExtension (filename, "wav"); using (var output = CreateNewFile (output_name)) AudioFormat.Wav.Write (input, output); } } } void ConvertImage (string filename) { string source_ext = Path.GetExtension (filename).TrimStart ('.').ToLowerInvariant(); if (m_image_format.Extensions.Any (ext => ext == source_ext)) return; string target_ext = m_image_format.Extensions.First(); string target_name = Path.ChangeExtension (filename, target_ext); using (var file = File.OpenRead (filename)) { var image = ImageFormat.Read (filename, file); if (null == image) return; try { using (var output = CreateNewFile (target_name)) m_image_format.Write (output, image); } catch // delete destination file on conversion failure { File.Delete (target_name); throw; } } } /// /// Creates new file with specified filename, or, if it's already exists, tries to open /// files named "FILENAME.1.EXT", "FILENAME.2.EXT" and so on. /// Throws exception after 100th failed attempt. /// public static Stream CreateNewFile (string filename) { string name = filename; var ext = new Lazy (() => Path.GetExtension (filename)); for (int attempt = 1; ; ++attempt) { try { return File.Open (name, FileMode.CreateNew); } catch (IOException) // file already exists { if (100 == attempt) // limit number of attempts throw; } name = Path.ChangeExtension (filename, attempt.ToString()+ext.Value); } } void OnConvertComplete (object sender, RunWorkerCompletedEventArgs e) { m_main.ResumeWatchDirectoryChanges(); m_progress_dialog.Dispose(); if (null != m_pending_error) { if (m_pending_error is OperationCanceledException) m_main.SetStatusText (m_pending_error.Message); else m_main.PopupError (m_pending_error.Message, guiStrings.TextMediaConvertError); } m_main.Activate(); m_main.RefreshView(); } } }