commit e208029dd329178a81924334b9f2331bb8578391 Author: Poddav Date: Mon Jul 21 23:26:28 2014 +0400 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..df26e2db --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin +obj +tags +*.exe +*.suo +*;* diff --git a/AboutBox.xaml b/AboutBox.xaml new file mode 100644 index 00000000..0becb3d6 --- /dev/null +++ b/AboutBox.xaml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs new file mode 100644 index 00000000..1b0cc98f --- /dev/null +++ b/MainWindow.xaml.cs @@ -0,0 +1,1094 @@ +// Game Resource Browser +// +// 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.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Diagnostics; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Input; +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; +using Rnd.Windows; + +namespace GARbro.GUI +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + private App m_app; + + const StringComparison StringIgnoreCase = StringComparison.CurrentCultureIgnoreCase; + + public MainWindow() + { + m_app = Application.Current as App; + InitializeComponent(); + InitDirectoryChangesWatcher(); + InitPreviewPane(); + + FormatCatalog.Instance.ParametersRequest += OnParametersRequest; + + CurrentDirectory.SizeChanged += (s, e) => { + if (e.WidthChanged) + { + pathLine.MinWidth = e.NewSize.Width-79; + this.MinWidth = e.NewSize.Width+65; + } + }; + pathLine.EnterKeyDown += acb_OnKeyDown; + } + + void WindowLoaded (object sender, RoutedEventArgs e) + { + lv_SetSortMode (Settings.Default.lvSortColumn, Settings.Default.lvSortDirection); + Dispatcher.InvokeAsync (WindowRendered, DispatcherPriority.ContextIdle); + } + + void WindowRendered () + { + ViewModel = CreateViewModel (m_app.InitPath); + lv_SelectItem (0); + SetStatusText (guiStrings.MsgReady); + } + + /// + /// Save settings when main window is about to close + /// + protected override void OnClosing (CancelEventArgs e) + { + SaveSettings(); + base.OnClosing (e); + } + + /// + /// Manually save settings that are not automatically saved by bindings. + /// + private void SaveSettings() + { + if (null != m_lvSortByColumn) + { + Settings.Default.lvSortColumn = m_lvSortByColumn.Tag.ToString(); + Settings.Default.lvSortDirection = m_lvSortDirection; + } + else + Settings.Default.lvSortColumn = ""; + } + + /// + /// Set status line text. Could be called from any thread. + /// + public void SetStatusText (string text) + { + Dispatcher.Invoke (() => { appStatusText.Text = text; }); + } + + /// + /// Popup error message box. Could be called from any thread. + /// + public void PopupError (string message, string title) + { + Dispatcher.Invoke (() => MessageBox.Show (this, message, title, MessageBoxButton.OK, MessageBoxImage.Error)); + } + + /// + /// Set data context of the ListView. + /// + + private DirectoryViewModel ViewModel + { + get + { + var source = CurrentDirectory.ItemsSource as CollectionView; + if (null == source) + return null; + return source.SourceCollection as DirectoryViewModel; + } + set + { + StopWatchDirectoryChanges(); + var cvs = this.Resources["ListViewSource"] as CollectionViewSource; + cvs.Source = value; + pathLine.Text = value.Path; + if (m_lvSortByColumn != null) + lv_Sort (m_lvSortByColumn.Tag.ToString(), m_lvSortDirection); + else + lv_Sort (null, m_lvSortDirection); + if (!value.IsArchive && !string.IsNullOrEmpty (value.Path)) + { + Directory.SetCurrentDirectory (value.Path); + WatchDirectoryChanges (value.Path); + } + CurrentDirectory.UpdateLayout(); + } + } + + DirectoryViewModel GetNewViewModel (string path) + { + SetBusyState(); + path = Path.GetFullPath (path); + if (Directory.Exists (path)) + return new DirectoryViewModel (path, m_app.GetDirectoryList (path)); + else + return new ArchiveViewModel (path, m_app.GetArchive (path)); + } + + public void SetBusyState() + { + Mouse.OverrideCursor = Cursors.Wait; + Dispatcher.InvokeAsync (() => { + Mouse.OverrideCursor = null; + }, DispatcherPriority.ApplicationIdle); + } + + /// + /// Create view model corresponding to . Returns null on error. + /// + DirectoryViewModel TryCreateViewModel (string path) + { + try + { + return GetNewViewModel (path); + } + catch (Exception X) + { + SetStatusText (string.Format ("{0}: {1}", Path.GetFileName (path), X.Message)); + return null; + } + } + + /// + /// Create view model corresponding to or empty view model if there was + /// an error accessing path. + /// + DirectoryViewModel CreateViewModel (string path) + { + try + { + return GetNewViewModel (path); + } + catch (Exception X) + { + PopupError (X.Message, guiStrings.MsgErrorOpening); + return new DirectoryViewModel ("", new Entry[0]); + } + } + + #region Refresh view on filesystem changes + + private FileSystemWatcher m_watcher = new FileSystemWatcher(); + + void InitDirectoryChangesWatcher () + { + m_watcher.NotifyFilter = NotifyFilters.Size | NotifyFilters.FileName | NotifyFilters.DirectoryName; + m_watcher.Changed += InvokeRefreshView; + m_watcher.Created += InvokeRefreshView; + m_watcher.Deleted += InvokeRefreshView; + m_watcher.Renamed += InvokeRefreshView; + } + + void WatchDirectoryChanges (string path) + { + m_watcher.Path = path; + m_watcher.EnableRaisingEvents = true; + } + + void StopWatchDirectoryChanges() + { + m_watcher.EnableRaisingEvents = false; + } + + private void InvokeRefreshView (object source, FileSystemEventArgs e) + { + var watcher = source as FileSystemWatcher; + if (watcher.Path == ViewModel.Path) + { + watcher.EnableRaisingEvents = false; + Dispatcher.Invoke (RefreshView, DispatcherPriority.Send, CancellationToken.None, + TimeSpan.FromMilliseconds (100)); + } + } + #endregion + + /// + /// Select specified item within CurrentDirectory and bring it into a view. + /// + + void lv_SelectItem (EntryViewModel item) + { + if (item != null) + { + CurrentDirectory.SelectedItem = item; + CurrentDirectory.ScrollIntoView (item); + var lvi = (ListViewItem)CurrentDirectory.ItemContainerGenerator.ContainerFromItem (item); + if (lvi != null) + lvi.Focus(); + } + } + + void lv_SelectItem (int index) + { + CurrentDirectory.SelectedIndex = index; + CurrentDirectory.ScrollIntoView (CurrentDirectory.SelectedItem); + var lvi = (ListViewItem)CurrentDirectory.ItemContainerGenerator.ContainerFromIndex (index); + if (lvi != null) + lvi.Focus(); + } + + void lv_SelectItem (string name) + { + if (!string.IsNullOrEmpty (name)) + lv_SelectItem (ViewModel.Find (name)); + } + + private void lv_Focus () + { + if (CurrentDirectory.SelectedIndex != -1) + { + var item = CurrentDirectory.SelectedItem; + var lvi = CurrentDirectory.ItemContainerGenerator.ContainerFromItem (item) as ListViewItem; + if (lvi != null) + { + lvi.Focus(); + return; + } + } + CurrentDirectory.Focus(); + } + + void lvi_Selected (object sender, RoutedEventArgs args) + { + var lvi = sender as ListViewItem; + if (lvi == null) + return; + var entry = lvi.Content as EntryViewModel; + if (entry == null) + return; + PreviewEntry (entry.Source); + } + + void lvi_DoubleClick (object sender, MouseButtonEventArgs args) + { + var lvi = sender as ListViewItem; + if (Commands.OpenItem.CanExecute (null, lvi)) + { + Commands.OpenItem.Execute (null, lvi); + args.Handled = true; + } + } + + /// + /// Get currently selected item from ListView widget. + /// + private ListViewItem lv_GetCurrentContainer () + { + int current = CurrentDirectory.SelectedIndex; + if (-1 == current) + return null; + + return CurrentDirectory.ItemContainerGenerator.ContainerFromIndex (current) as ListViewItem; + } + + GridViewColumnHeader m_lvSortByColumn = null; + ListSortDirection m_lvSortDirection = ListSortDirection.Ascending; + + public bool IsSortByName { + get { return m_lvSortByColumn != null && "Name".Equals (m_lvSortByColumn.Tag); } + } + public bool IsSortByType { + get { return m_lvSortByColumn != null && "Type".Equals (m_lvSortByColumn.Tag); } + } + public bool IsSortBySize { + get { return m_lvSortByColumn != null && "Size".Equals (m_lvSortByColumn.Tag); } + } + public bool IsUnsorted { get { return m_lvSortByColumn == null; } } + + void lv_SetSortMode (string sortBy, ListSortDirection direction) + { + m_lvSortByColumn = null; + GridView view = CurrentDirectory.View as GridView; + foreach (var column in view.Columns) + { + var header = column.Header as GridViewColumnHeader; + if (null != header && !string.IsNullOrEmpty (sortBy) && sortBy.Equals (header.Tag)) + { + if (ListSortDirection.Ascending == direction) + column.HeaderTemplate = Resources["SortArrowUp"] as DataTemplate; + else + column.HeaderTemplate = Resources["SortArrowDown"] as DataTemplate; + m_lvSortByColumn = header; + m_lvSortDirection = direction; + } + else + { + column.HeaderTemplate = Resources["SortArrowNone"] as DataTemplate; + } + } + } + + private void lv_Sort (string sortBy, ListSortDirection direction) + { + var dataView = CollectionViewSource.GetDefaultView (CurrentDirectory.ItemsSource) as ListCollectionView; + dataView.CustomSort = new FileSystemComparer (sortBy, direction); + /* + using (dataView.DeferRefresh()) + { + dataView.SortDescriptions.Clear(); + dataView.SortDescriptions.Add (new SortDescription ("Priority", ListSortDirection.Ascending)); + if (!string.IsNullOrEmpty (sortBy)) + { + dataView.SortDescriptions.Add (new SortDescription (sortBy, direction)); + if (sortBy != "Name") + dataView.SortDescriptions.Add (new SortDescription ("Name", ListSortDirection.Ascending)); + } + } + */ + } + + /// + /// Sort Listview by columns + /// + void lv_ColumnHeaderClicked (object sender, RoutedEventArgs e) + { + var headerClicked = e.OriginalSource as GridViewColumnHeader; + + if (null == headerClicked) + return; + if (headerClicked.Role == GridViewColumnHeaderRole.Padding) + return; + + ListSortDirection direction; + if (headerClicked != m_lvSortByColumn) + direction = ListSortDirection.Ascending; + else if (m_lvSortDirection == ListSortDirection.Ascending) + direction = ListSortDirection.Descending; + else + direction = ListSortDirection.Ascending; + + string sortBy = headerClicked.Tag.ToString(); + lv_Sort (sortBy, direction); + + // Remove arrow from previously sorted header + if (m_lvSortByColumn != null && m_lvSortByColumn != headerClicked) + { + m_lvSortByColumn.Column.HeaderTemplate = Resources["SortArrowNone"] as DataTemplate; + } + + if (ListSortDirection.Ascending == direction) + { + headerClicked.Column.HeaderTemplate = Resources["SortArrowUp"] as DataTemplate; + } + else + { + headerClicked.Column.HeaderTemplate = Resources["SortArrowDown"] as DataTemplate; + } + m_lvSortByColumn = headerClicked; + m_lvSortDirection = direction; + } + + /// + /// Handle "Sort By" commands. + /// + + private void SortByExec (object sender, ExecutedRoutedEventArgs e) + { + string sort_by = e.Parameter as string; + lv_Sort (sort_by, ListSortDirection.Ascending); + lv_SetSortMode (sort_by, ListSortDirection.Ascending); + } + + /// + /// Event handler for keys pressed in the right pane + /// + + private void lv_TextInput (object sender, TextCompositionEventArgs e) + { + LookupItem (e.Text); + e.Handled = true; + } + + /// + /// Lookup item in listview pane by first letter. + /// + + private void LookupItem (string key) + { + if (string.IsNullOrEmpty (key)) + return; + var source = CurrentDirectory.ItemsSource as CollectionView; + if (source == null) + return; + + var current = CurrentDirectory.SelectedItem as EntryViewModel; + int index = 0; + if (current != null && current.Name.StartsWith (key, StringIgnoreCase)) + index = CurrentDirectory.SelectedIndex+1; + + for (int i = index, count = source.Count; i < count; ++i) + { + var entry = source.GetItemAt (i) as EntryViewModel; + if (entry != null && entry.Name.StartsWith (key, StringIgnoreCase)) + { + lv_SelectItem (entry); + break; + } + } + } + + private void acb_OnKeyDown (object sender, KeyEventArgs e) + { + if (e.Key != Key.Return) + return; + string path = (sender as AutoCompleteBox).Text; + if (string.IsNullOrEmpty (path)) + return; + try + { + ViewModel = GetNewViewModel (path); + lv_Focus(); + } + catch (Exception X) + { + PopupError (X.Message, guiStrings.MsgErrorOpening); + } + } + + #region Navigation history implementation + + private string CurrentPath { get { return ViewModel.Path; } } + + HistoryStack m_history = new HistoryStack(); + + DirectoryPosition GetCurrentPosition () + { + var evm = CurrentDirectory.SelectedItem as EntryViewModel; + return new DirectoryPosition (ViewModel, evm); + } + + bool SetCurrentPosition (DirectoryPosition pos) + { + var vm = TryCreateViewModel (pos.Path); + if (null == vm) + return false; + try + { + vm.SetPosition (pos); + ViewModel = vm; + if (null != pos.Item) + lv_SelectItem (pos.Item.Name); + return true; + } + catch (Exception X) + { + SetStatusText (X.Message); + return false; + } + } + + private void SaveCurrentPosition () + { + m_history.Push (GetCurrentPosition()); + } + + private void GoBackExec (object sender, ExecutedRoutedEventArgs e) + { + DirectoryPosition current = m_history.Undo (GetCurrentPosition()); + if (current != null) + SetCurrentPosition (current); + } + + private void GoForwardExec (object sender, ExecutedRoutedEventArgs e) + { + DirectoryPosition current = m_history.Redo (GetCurrentPosition()); + if (current != null) + SetCurrentPosition (current); + } + + private void CanExecuteGoBack (object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = m_history.CanUndo(); + } + + private void CanExecuteGoForward (object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = m_history.CanRedo(); + } + #endregion + + /// + /// Open file/directory. + /// + private void OpenItemExec (object control, ExecutedRoutedEventArgs e) + { + EntryViewModel entry = CurrentDirectory.SelectedItem as EntryViewModel; + if (null == entry) + { + var lvi = e.OriginalSource as ListViewItem; + if (lvi != null) + entry = lvi.Content as EntryViewModel; + } + if (null == entry) + return; + + var vm = ViewModel; + if (null == vm) + return; + if (vm.IsArchive) // tried to open file inside archive + { + var arc_vm = vm as ArchiveViewModel; + if (!("" == arc_vm.SubDir && ".." == entry.Name)) + { + OpenArchiveEntry (arc_vm, entry); + return; + } + } + OpenDirectoryEntry (vm, entry); + } + + private void OpenDirectoryEntry (DirectoryViewModel vm, EntryViewModel entry) + { + string old_dir = vm.Path; + string new_dir = Path.Combine (old_dir, entry.Name); + Trace.WriteLine (new_dir, "OpenDirectoryEntry"); + vm = TryCreateViewModel (new_dir); + if (null == vm) + { + if (entry.Type != "archive") + SystemOpen (new_dir); + return; + } + SaveCurrentPosition(); + ViewModel = vm; + if (vm.IsArchive && null != m_app.CurrentArchive) + SetStatusText (m_app.CurrentArchive.Description); + else + SetStatusText (""); + var old_parent = Directory.GetParent (old_dir); + if (null != old_parent && vm.Path == old_parent.FullName) + { + lv_SelectItem (Path.GetFileName (old_dir)); + } + else + { + lv_SelectItem (0); + } + } + + private void OpenArchiveEntry (ArchiveViewModel vm, EntryViewModel entry) + { + if (entry.IsDirectory) + { + SaveCurrentPosition(); + var old_dir = vm.SubDir; + try + { + vm.ChDir (entry.Name); + if (".." == entry.Name) + lv_SelectItem (Path.GetFileName (old_dir)); + else + lv_SelectItem (0); + SetStatusText (""); + } + catch (Exception X) + { + SetStatusText (X.Message); + } + } + } + + /// + /// Launch specified file. + /// + private void SystemOpen (string file) + { + try + { + Process.Start (file); + } + catch (Exception X) + { + SetStatusText (X.Message); + } + } + + /// + /// Refresh current view. + /// + private void RefreshExec (object sender, ExecutedRoutedEventArgs e) + { + RefreshView(); + } + + private void RefreshView () + { + m_app.ResetCache(); + var pos = GetCurrentPosition(); + SetCurrentPosition (pos); + } + + /// + /// Open current file in Explorer. + /// + + private void ExploreItemExec (object sender, ExecutedRoutedEventArgs e) + { + var entry = CurrentDirectory.SelectedItem as EntryViewModel; + if (entry != null && !ViewModel.IsArchive) + { + try + { + string name = Path.Combine (CurrentPath, entry.Name); + Process.Start ("explorer.exe", "/select,"+name); + } + catch (Exception X) + { + // ignore + Trace.WriteLine (X.Message, "explorer.exe"); + } + } + } + + /// + /// Delete item from both media library and disk drive. + /// + private void DeleteItemExec (object sender, ExecutedRoutedEventArgs e) + { + var entry = CurrentDirectory.SelectedItem as EntryViewModel; + if (entry == null) + return; + + this.IsEnabled = false; + try + { + m_app.ResetCache(); + string item_name = Path.Combine (CurrentPath, entry.Name); + Trace.WriteLine (item_name, "DeleteItemExec"); + FileSystem.DeleteFile (item_name, UIOption.AllDialogs, RecycleOption.SendToRecycleBin); + DeleteItem (lv_GetCurrentContainer()); + SetStatusText (string.Format(guiStrings.MsgDeletedItem, item_name)); + } + catch (OperationCanceledException) + { + } + catch (Exception X) + { + SetStatusText (X.Message); + } + finally + { + this.IsEnabled = true; + } + } + + /// + /// Delete item at the specified position within ListView, correctly adjusting current + /// position. + /// + private void DeleteItem (ListViewItem item) + { + int i = CurrentDirectory.SelectedIndex; + int next = -1; + if (i+1 < CurrentDirectory.Items.Count) + next = i + 1; + else if (i > 0) + next = i - 1; + + if (next != -1) + CurrentDirectory.SelectedIndex = next; + + var entry = item.Content as EntryViewModel; + if (entry != null) + { + ViewModel.Remove (entry); + } + } + + /// + /// Rename selected item. + /// + private void RenameItemExec(object sender, ExecutedRoutedEventArgs e) + { + RenameElement (lv_GetCurrentContainer()); + } + + /// + /// Rename item contained within specified framework control. + /// + void RenameElement (ListViewItem item) + { + if (item == null) + return; +/* + TextBlock block = FindByName (item, "item_Text") as TextBlock; + TextBox box = FindSibling (block, "item_Input") as TextBox; + + if (block == null || box == null) + return; + + IsRenameActive = true; + + block.Visibility = Visibility.Collapsed; + box.Text = block.Text; + box.Visibility = Visibility.Visible; + box.Select (0, box.Text.Length); + box.Focus(); +*/ + } + + /// + /// 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 (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); + } + + /// + /// Handle "Exit" command. + /// + void ExitExec (object sender, ExecutedRoutedEventArgs e) + { + Application.Current.Shutdown(); + } + + private void MenuAbout_Click (object sender, RoutedEventArgs e) + { + var about = new AboutBox(); + about.Owner = this; + about.ShowDialog(); + } + + private void CanExecuteAlways (object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = true; + } + + private void CanExecuteControlCommand (object sender, CanExecuteRoutedEventArgs e) + { + Control target = e.Source as Control; + e.CanExecute = target != null; + } + + private void CanExecuteOnSelected (object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = CurrentDirectory.SelectedIndex != -1; + } + + private void CanExecuteInArchive (object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = ViewModel.IsArchive && CurrentDirectory.SelectedIndex != -1; + } + + private void CanExecuteInDirectory (object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = !ViewModel.IsArchive; + } + + private void CanExecuteDelete (object sender, CanExecuteRoutedEventArgs e) + { + if (!ViewModel.IsArchive && CurrentDirectory.SelectedIndex != -1) + { + var entry = CurrentDirectory.SelectedItem as EntryViewModel; + if (entry != null && !entry.IsDirectory) + { + e.CanExecute = true; + return; + } + } + e.CanExecute = false; + } + + private void OnParametersRequest (object sender, ParametersRequestEventArgs e) + { + var control = e.InputWidget as UIElement; + if (null != control) + { + var param_dialog = new ArcParametersDialog (control, e.Notice); + param_dialog.Owner = this; + e.InputResult = param_dialog.ShowDialog() ?? false; + } + } + } + + /// + /// TextBox that uses filesystem as source for autocomplete. + /// + public class ExtAutoCompleteBox : AutoCompleteBox + { + public delegate void EnterKeyDownEvent (object sender, KeyEventArgs e); + public event EnterKeyDownEvent EnterKeyDown; + /* + public event EnterKeyDownEvent EnterKeyDown + { + add { AddHandler (KeyEvent, value); } + remove { RemoveHandler (KeyEvent, value); } + } + public static readonly RoutedEvent EnterKeyEvent = EventManager.RegisterRoutedEvent( + "EnterKeyDown", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ExtAutoCompleteBox)); + */ + + protected override void OnKeyDown (KeyEventArgs e) + { + base.OnKeyDown (e); + if (e.Key == Key.Enter) + RaiseEnterKeyDownEvent (e); + } + + private void RaiseEnterKeyDownEvent (KeyEventArgs e) + { + if (EnterKeyDown != null) + EnterKeyDown (this, e); + +// var event_args = new RoutedEventArgs (ExtCompleteBox.EnterKeyEvent); +// RaiseEvent (event_args); + } + + protected override void OnPopulating (PopulatingEventArgs e) + { + var candidates = new List(); + string dirname = Path.GetDirectoryName (this.Text); + if (!string.IsNullOrEmpty (this.Text) && Directory.Exists (dirname)) + { + foreach (var dir in Directory.GetDirectories (dirname)) + { + if (dir.StartsWith (dirname, StringComparison.CurrentCultureIgnoreCase)) + candidates.Add (dir); + } + } + this.ItemsSource = candidates; + base.OnPopulating (e); + } + } + + public static class Commands + { + public static readonly RoutedCommand OpenItem = new RoutedCommand(); + public static readonly RoutedCommand ExtractItem = new RoutedCommand(); + public static readonly RoutedCommand SortBy = new RoutedCommand(); + public static readonly RoutedCommand Exit = new RoutedCommand(); + public static readonly RoutedCommand GoBack = new RoutedCommand(); + public static readonly RoutedCommand GoForward = new RoutedCommand(); + public static readonly RoutedCommand DeleteItem = new RoutedCommand(); + public static readonly RoutedCommand RenameItem = new RoutedCommand(); + public static readonly RoutedCommand ExploreItem = new RoutedCommand(); + public static readonly RoutedCommand Refresh = new RoutedCommand(); + public static readonly RoutedCommand Browse = new RoutedCommand(); + } +} diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..15ee9a22 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +MSCS = D:/WINDOWS/Microsoft.NET/Framework/v4.0.30319/csc //nologo + +.SUFFIXES: .cs .exe + +all: GARbro + +adler32: adler32.cs + $(MSCS) $(MSCSFLAGS) //out:$@.exe $^ + +inflate: inflate.cs + $(MSCS) $(MSCSFLAGS) //out:$@.exe $^ //r:zlib\\zlibnet.dll + +deflate: deflate.cs + $(MSCS) $(MSCSFLAGS) //out:$@.exe $^ //r:zlib\\zlibnet.dll + +GARbro: Program.cs GameRes.cs ArcXFL.cs + $(MSCS) $(MSCSFLAGS) //out:$@.exe $^ //r:System.ComponentModel.Composition.dll //r:System.ComponentModel.DataAnnotations.dll + +tags: + ctags *.cs diff --git a/ModalWindow.cs b/ModalWindow.cs new file mode 100644 index 00000000..6b1aa5cc --- /dev/null +++ b/ModalWindow.cs @@ -0,0 +1,88 @@ +//! \file ModalWindow.cs +//! \date Tue Aug 02 10:20:50 2011 +//! \brief Window without an icon. +// +// Copyright (C) 2011 by poddav +// +// 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.Windows; +using System.Windows.Interop; +using System.Runtime.InteropServices; + +namespace Rnd.Windows +{ + /// + /// Window without an icon. + /// + public class ModalWindow : Window + { + protected override void OnSourceInitialized(EventArgs e) + { + base.OnSourceInitialized(e); + + HideIcon (this); + } + + internal class NativeMethods + { + [DllImport ("user32.dll")] + internal static extern int GetWindowLong (IntPtr hwnd, int index); + + [DllImport ("user32.dll")] + internal static extern int SetWindowLong (IntPtr hwnd, int index, int newStyle); + + [DllImport ("user32.dll")] + internal static extern bool SetWindowPos (IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int width, int height, uint flags); + + [DllImport ("user32.dll")] + internal static extern IntPtr SendMessage (IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam); + } + + const int GWL_EXSTYLE = -20; + const int WS_EX_DLGMODALFRAME = 0x0001; + + const int SWP_NOSIZE = 0x0001; + const int SWP_NOMOVE = 0x0002; + const int SWP_NOZORDER = 0x0004; + const int SWP_FRAMECHANGED = 0x0020; + const uint WM_SETICON = 0x0080; + + /// + /// Win32 mumbo-jumbo to hide window icon and its menu. + /// + + public static void HideIcon (Window window) + { + // Get this window's handle + IntPtr hwnd = new WindowInteropHelper (window).Handle; + + // Change the extended window style to not show a window icon + int extendedStyle = NativeMethods.GetWindowLong (hwnd, GWL_EXSTYLE); + NativeMethods.SetWindowLong (hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME); + NativeMethods.SendMessage (hwnd, WM_SETICON, IntPtr.Zero, IntPtr.Zero); + NativeMethods.SendMessage (hwnd, WM_SETICON, new IntPtr (1), IntPtr.Zero); + + // Update the window's non-client area to reflect the changes + NativeMethods.SetWindowPos (hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); + } + } +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..18975dae --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Game Resource browser")] +[assembly: AssemblyDescription("Game Resource browser")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("GARbro.GUI")] +[assembly: AssemblyCopyright("Copyright © 2014 mørkt")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs new file mode 100644 index 00000000..836235e8 --- /dev/null +++ b/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.18444 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace GARbro.GUI.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GARbro.GUI.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/Properties/Resources.resx b/Properties/Resources.resx new file mode 100644 index 00000000..ffecec85 --- /dev/null +++ b/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs new file mode 100644 index 00000000..6b052f5d --- /dev/null +++ b/Properties/Settings.Designer.cs @@ -0,0 +1,230 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.18444 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace GARbro.GUI.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool UpgradeRequired { + get { + return ((bool)(this["UpgradeRequired"])); + } + set { + this["UpgradeRequired"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("NaN")] + public double winTop { + get { + return ((double)(this["winTop"])); + } + set { + this["winTop"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("NaN")] + public double winLeft { + get { + return ((double)(this["winLeft"])); + } + set { + this["winLeft"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1024")] + public double winWidth { + get { + return ((double)(this["winWidth"])); + } + set { + this["winWidth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("560")] + public double winHeight { + get { + return ((double)(this["winHeight"])); + } + set { + this["winHeight"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Normal")] + public global::System.Windows.WindowState winState { + get { + return ((global::System.Windows.WindowState)(this["winState"])); + } + set { + this["winState"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("265")] + public double lvNameColumnWidth { + get { + return ((double)(this["lvNameColumnWidth"])); + } + set { + this["lvNameColumnWidth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("75")] + public double lvTypeColumnWidth { + get { + return ((double)(this["lvTypeColumnWidth"])); + } + set { + this["lvTypeColumnWidth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("75")] + public double lvSizeColumnWidth { + get { + return ((double)(this["lvSizeColumnWidth"])); + } + set { + this["lvSizeColumnWidth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Name")] + public string lvSortColumn { + get { + return ((string)(this["lvSortColumn"])); + } + set { + this["lvSortColumn"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Ascending")] + public global::System.ComponentModel.ListSortDirection lvSortDirection { + get { + return ((global::System.ComponentModel.ListSortDirection)(this["lvSortDirection"])); + } + set { + this["lvSortDirection"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("448")] + public global::System.Windows.GridLength lvPanelWidth { + get { + return ((global::System.Windows.GridLength)(this["lvPanelWidth"])); + } + set { + this["lvPanelWidth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool appExtractImages { + get { + return ((bool)(this["appExtractImages"])); + } + set { + this["appExtractImages"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool appExtractText { + get { + return ((bool)(this["appExtractText"])); + } + set { + this["appExtractText"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string appImageFormat { + get { + return ((string)(this["appImageFormat"])); + } + set { + this["appImageFormat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("UTF-8")] + public string appTextEncoding { + get { + return ((string)(this["appTextEncoding"])); + } + set { + this["appTextEncoding"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string appLastDirectory { + get { + return ((string)(this["appLastDirectory"])); + } + set { + this["appLastDirectory"] = value; + } + } + } +} diff --git a/Properties/Settings.settings b/Properties/Settings.settings new file mode 100644 index 00000000..e3fcc508 --- /dev/null +++ b/Properties/Settings.settings @@ -0,0 +1,57 @@ + + + + + + True + + + NaN + + + NaN + + + 1024 + + + 560 + + + Normal + + + 265 + + + 75 + + + 75 + + + Name + + + Ascending + + + 448 + + + True + + + True + + + + + + UTF-8 + + + + + + \ No newline at end of file diff --git a/Properties/app.manifest b/Properties/app.manifest new file mode 100644 index 00000000..14d77941 --- /dev/null +++ b/Properties/app.manifest @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Settings.cs b/Settings.cs new file mode 100644 index 00000000..e755fd00 --- /dev/null +++ b/Settings.cs @@ -0,0 +1,43 @@ +namespace GARbro.GUI.Properties { + + + // This class allows you to handle specific events on the settings class: + // The SettingChanging event is raised before a setting's value is changed. + // The PropertyChanged event is raised after a setting's value is changed. + // The SettingsLoaded event is raised after the setting values are loaded. + // The SettingsSaving event is raised before the setting values are saved. + internal sealed partial class Settings { + + public Settings() { + // // To add event handlers for saving and changing settings, uncomment the lines below: + // + // this.SettingChanging += this.SettingChangingEventHandler; + // + // this.SettingsSaving += this.SettingsSavingEventHandler; + // + this.SettingsLoaded += OnSettingsLoadedHandler; + } + + void OnSettingsLoadedHandler (object sender, System.Configuration.SettingsLoadedEventArgs e) + { + if (Settings.Default.UpgradeRequired) + { + Settings.Default.Upgrade(); + Settings.Default.UpgradeRequired = false; + Settings.Default.Save(); + + // do not restore in minimized state + if (Settings.Default.winState == System.Windows.WindowState.Minimized) + Settings.Default.winState = System.Windows.WindowState.Normal; + } + } + + private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) { + // Add code to handle the SettingChangingEvent event here. + } + + private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) { + // Add code to handle the SettingsSaving event here. + } + } +} diff --git a/Shell.cs b/Shell.cs new file mode 100644 index 00000000..e50c6dc2 --- /dev/null +++ b/Shell.cs @@ -0,0 +1,182 @@ +//! \file Shell.cs +//! \date Tue Aug 02 13:48:55 2011 +//! \brief Win32 shell functions. +// +// Copyright (C) 2011 by poddav +// +// 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.Text; +using System.Runtime.InteropServices; + +namespace Rnd.Shell +{ + /// + /// Wrapper around PathCreateFromUrl WINAPI call. + /// + class PathConverter + { + [DllImport("shlwapi.dll", EntryPoint="PathCreateFromUrlW", CharSet=CharSet.Unicode, SetLastError=true)] + private static extern Int32 PathCreateFromUrl( + string url, StringBuilder path, + ref Int32 size, UInt32 flags); + + private StringBuilder buf; + private Int32 size; + + public PathConverter (int capacity = 260) + { + buf = new StringBuilder (capacity); + } + public string UrlToPath (string url) + { + size = buf.Capacity; + Int32 rc = PathCreateFromUrl (url, buf, ref size, 0); + return rc == 0 ? buf.ToString (0, size) : ""; + } + } + + /// + /// Wrapper around SHGetFileInfo WINAPI call. + /// + class FileInfo + { + [DllImport("shell32.dll", CharSet=CharSet.Auto)] + public static extern IntPtr SHGetFileInfo( + string pszPath, Int32 dwFileAttributes, + ref SHFILEINFO psfi, int cbFileInfo, int uFlags); + + [DllImport("User32.dll")] + public static extern int DestroyIcon(IntPtr hIcon); + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + public struct SHFILEINFO + { + public IntPtr hIcon; + public int iIcon; + public uint dwAttributes; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string szDisplayName; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)] + public string szTypeName; + + public SHFILEINFO(bool b) + { + hIcon = IntPtr.Zero; + iIcon = 0; + dwAttributes = 0; + szDisplayName = ""; + szTypeName = ""; + } + }; + + [Flags] + public enum SHGFI : uint + { + /// get icon + Icon = 0x000000100, + /// get display name + DisplayName = 0x000000200, + /// get type name + TypeName = 0x000000400, + /// get attributes + Attributes = 0x000000800, + /// get icon location + IconLocation = 0x000001000, + /// return exe type + ExeType = 0x000002000, + /// get system icon index + SysIconIndex = 0x000004000, + /// put a link overlay on icon + LinkOverlay = 0x000008000, + /// show icon in selected state + Selected = 0x000010000, + /// get only specified attributes + Attr_Specified = 0x000020000, + /// get large icon + LargeIcon = 0x000000000, + /// get small icon + SmallIcon = 0x000000001, + /// get open icon + OpenIcon = 0x000000002, + /// get shell size icon + ShellIconSize = 0x000000004, + /// pszPath is a pidl + PIDL = 0x000000008, + /// use passed dwFileAttribute + UseFileAttributes= 0x000000010, + /// apply the appropriate overlays + AddOverlays = 0x000000020, + /// Get the index of the overlay in the upper 8 bits of the iIcon + OverlayIndex = 0x000000040, + } + + public static string GetTypeName (string filename) + { + SHFILEINFO info = new SHFILEINFO(true); + int szInfo = Marshal.SizeOf (info); + int result = (int)SHGetFileInfo (filename, 0, ref info, szInfo, (int)SHGFI.TypeName); + + // If uFlags does not contain SHGFI_EXETYPE or SHGFI_SYSICONINDEX, + // the return value is nonzero if successful, or zero otherwise. + if (result != 0) + return info.szTypeName; + else + return string.Empty; + } + + public static SHFILEINFO? GetInfo (string filename, SHGFI flags) + { + SHFILEINFO info = new SHFILEINFO(true); + int szInfo = Marshal.SizeOf (info); + int result = (int)SHGetFileInfo (filename, 0, ref info, szInfo, (int)flags); + + return result != 0? new Nullable (info): null; + } + } + + /// + /// Wrapper around MoveFileEx WINAPI call. + /// + class File + { + [DllImport("kernel32.dll", EntryPoint="MoveFileExW", SetLastError=true, CharSet=CharSet.Unicode)] + public static extern bool MoveFileEx (string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags); + + [Flags] + public enum MoveFileFlags : uint + { + ReplaceExisting = 0x00000001, + CopyAllowed = 0x00000002, + DelayUntilReboot = 0x00000004, + WriteThrough = 0x00000008, + CreateHardlink = 0x00000010, + FailIfNotTrackable = 0x00000020 + } + + public static bool Rename (string szFrom, string szTo) + { + return MoveFileEx (szFrom, szTo, MoveFileFlags.ReplaceExisting); + } + } +} diff --git a/Strings/guiStrings.Designer.cs b/Strings/guiStrings.Designer.cs new file mode 100644 index 00000000..8b79e6b7 --- /dev/null +++ b/Strings/guiStrings.Designer.cs @@ -0,0 +1,594 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.18444 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace GARbro.GUI.Strings { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class guiStrings { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal guiStrings() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GARbro.GUI.Strings.guiStrings", typeof(guiStrings).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Cancel. + /// + public static string ButtonCancel { + get { + return ResourceManager.GetString("ButtonCancel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Extract. + /// + public static string ButtonExtract { + get { + return ResourceManager.GetString("ButtonExtract", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to OK. + /// + public static string ButtonOK { + get { + return ResourceManager.GetString("ButtonOK", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to _Close. + /// + public static string CtxMenuClose { + get { + return ResourceManager.GetString("CtxMenuClose", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copy. + /// + public static string CtxMenuCopy { + get { + return ResourceManager.GetString("CtxMenuCopy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cut. + /// + public static string CtxMenuCut { + get { + return ResourceManager.GetString("CtxMenuCut", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to _Delete. + /// + public static string CtxMenuDelete { + get { + return ResourceManager.GetString("CtxMenuDelete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Browse in _Explorer. + /// + public static string CtxMenuExplorer { + get { + return ResourceManager.GetString("CtxMenuExplorer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Extract. + /// + public static string CtxMenuExtract { + get { + return ResourceManager.GetString("CtxMenuExtract", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open. + /// + public static string CtxMenuOpen { + get { + return ResourceManager.GetString("CtxMenuOpen", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Paste. + /// + public static string CtxMenuPaste { + get { + return ResourceManager.GetString("CtxMenuPaste", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Refresh. + /// + public static string CtxMenuRefresh { + get { + return ResourceManager.GetString("CtxMenuRefresh", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to _Rename. + /// + public static string CtxMenuRename { + get { + return ResourceManager.GetString("CtxMenuRename", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sort by. + /// + public static string CtxMenuSortBy { + get { + return ResourceManager.GetString("CtxMenuSortBy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Name. + /// + public static string CtxMenuSortByName { + get { + return ResourceManager.GetString("CtxMenuSortByName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Size. + /// + public static string CtxMenuSortBySize { + get { + return ResourceManager.GetString("CtxMenuSortBySize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type. + /// + public static string CtxMenuSortByType { + get { + return ResourceManager.GetString("CtxMenuSortByType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unsorted. + /// + public static string CtxMenuUnsorted { + get { + return ResourceManager.GetString("CtxMenuUnsorted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Name. + /// + public static string HeaderName { + get { + return ResourceManager.GetString("HeaderName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Size. + /// + public static string HeaderSize { + get { + return ResourceManager.GetString("HeaderSize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type. + /// + public static string HeaderType { + get { + return ResourceManager.GetString("HeaderType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to file. + /// + public static string LPfile1 { + get { + return ResourceManager.GetString("LPfile1", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to files. + /// + public static string LPfile2 { + get { + return ResourceManager.GetString("LPfile2", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to About. + /// + public static string MenuAbout { + get { + return ResourceManager.GetString("MenuAbout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deleted {0}. + /// + public static string MsgDeletedItem { + get { + return ResourceManager.GetString("MsgDeletedItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to directory not found. + /// + public static string MsgDirectoryNotFound { + get { + return ResourceManager.GetString("MsgDirectoryNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to archive is empty. + /// + public static string MsgEmptyArchive { + get { + return ResourceManager.GetString("MsgEmptyArchive", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error extracting file. + /// + public static string MsgErrorExtracting { + get { + return ResourceManager.GetString("MsgErrorExtracting", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error opening file. + /// + public static string MsgErrorOpening { + get { + return ResourceManager.GetString("MsgErrorOpening", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Extracted {0} into {1}. + /// + public static string MsgExtractComplete { + get { + return ResourceManager.GetString("MsgExtractComplete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Extracted {0} {1}. + /// + public static string MsgExtractCompletePlural { + get { + return ResourceManager.GetString("MsgExtractCompletePlural", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Extracting files from {0}. + /// + public static string MsgExtractingArchive { + get { + return ResourceManager.GetString("MsgExtractingArchive", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Extracting file from {0}. + /// + public static string MsgExtractingFile { + get { + return ResourceManager.GetString("MsgExtractingFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Extracting files from {0} to {1}. + /// + public static string MsgExtractingTo { + get { + return ResourceManager.GetString("MsgExtractingTo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Image {0} x {1} pixels. + /// + public static string MsgImageSize { + get { + return ResourceManager.GetString("MsgImageSize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to no files to extract. + /// + public static string MsgNoFiles { + get { + return ResourceManager.GetString("MsgNoFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ready. + /// + public static string MsgReady { + get { + return ResourceManager.GetString("MsgReady", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to unable to interpret image format. + /// + public static string MsgUnableInterpret { + get { + return ResourceManager.GetString("MsgUnableInterpret", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to file could not be opened as resource archive. + /// + public static string MsgUnknownFormat { + get { + return ResourceManager.GetString("MsgUnknownFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Version {0}. + /// + public static string MsgVersion { + get { + return ResourceManager.GetString("MsgVersion", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to [builtin]. + /// + public static string TextAboutBuiltin { + get { + return ResourceManager.GetString("TextAboutBuiltin", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Supported archives:. + /// + public static string TextAboutSupportedArchives { + get { + return ResourceManager.GetString("TextAboutSupportedArchives", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Supported image formats:. + /// + public static string TextAboutSupportedImages { + get { + return ResourceManager.GetString("TextAboutSupportedImages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to About Game Resource browser. + /// + public static string TextAboutTitle { + get { + return ResourceManager.GetString("TextAboutTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to as is. + /// + public static string TextAsIs { + get { + return ResourceManager.GetString("TextAsIs", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Choose destination directory. + /// + public static string TextChooseDestDir { + get { + return ResourceManager.GetString("TextChooseDestDir", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <DIR>. + /// + public static string TextDirType { + get { + return ResourceManager.GetString("TextDirType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Text encoding. + /// + public static string TextEncoding { + get { + return ResourceManager.GetString("TextEncoding", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Extract all files from {0} to. + /// + public static string TextExtractAllTo { + get { + return ResourceManager.GetString("TextExtractAllTo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Extract {0} to. + /// + public static string TextExtractFileTo { + get { + return ResourceManager.GetString("TextExtractFileTo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Extract images. + /// + public static string TextExtractImages { + get { + return ResourceManager.GetString("TextExtractImages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Extract text. + /// + public static string TextExtractText { + get { + return ResourceManager.GetString("TextExtractText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Extract from archive. + /// + public static string TextExtractTitle { + get { + return ResourceManager.GetString("TextExtractTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Archive parameters. + /// + public static string TextParametersTitle { + get { + return ResourceManager.GetString("TextParametersTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save as. + /// + public static string TextSaveAs { + get { + return ResourceManager.GetString("TextSaveAs", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save images as. + /// + public static string TextSaveImagesAs { + get { + return ResourceManager.GetString("TextSaveImagesAs", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Game Resource browser. + /// + public static string TextTitle { + get { + return ResourceManager.GetString("TextTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Back. + /// + public static string TooltipBack { + get { + return ResourceManager.GetString("TooltipBack", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Forward. + /// + public static string TooltipForward { + get { + return ResourceManager.GetString("TooltipForward", resourceCulture); + } + } + } +} diff --git a/Strings/guiStrings.resx b/Strings/guiStrings.resx new file mode 100644 index 00000000..db4ca19e --- /dev/null +++ b/Strings/guiStrings.resx @@ -0,0 +1,297 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Cancel + + + Extract + + + OK + + + _Close + + + Copy + + + Cut + + + _Delete + + + Browse in _Explorer + + + Extract + + + Open + + + Paste + + + Refresh + + + _Rename + + + Sort by + + + Name + + + Size + + + Type + + + Unsorted + + + Name + + + Size + + + Type + + + file + + + files + + + About + + + Deleted {0} + + + directory not found + + + archive is empty + + + Error extracting file + + + Error opening file + + + Extracted {0} into {1} + + + Extracted {0} {1} + + + Extracting files from {0} + + + Extracting file from {0} + + + Extracting files from {0} to {1} + + + Image {0} x {1} pixels + + + no files to extract + + + Ready + + + unable to interpret image format + + + file could not be opened as resource archive + + + Version {0} + + + [builtin] + + + Supported archives: + + + Supported image formats: + + + About Game Resource browser + + + as is + + + Choose destination directory + + + <DIR> + + + Text encoding + + + Extract all files from {0} to + + + Extract {0} to + + + Extract images + + + Extract text + + + Extract from archive + + + Archive parameters + + + Save as + + + Save images as + + + Game Resource browser + + + Back + + + Forward + + \ No newline at end of file diff --git a/Strings/guiStrings.ru-RU.resx b/Strings/guiStrings.ru-RU.resx new file mode 100644 index 00000000..76f309c4 --- /dev/null +++ b/Strings/guiStrings.ru-RU.resx @@ -0,0 +1,300 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Отмена + + + Извлечь + + + _Закрыть + + + Копировать + + + Вырезать + + + Удалить + + + Просмотр в Explorer + + + Извлечь + + + Открыть + + + Вставить + + + Обновить + + + Переименовать + + + Сортировка + + + по имени + + + по размеру + + + по типу + + + не сортировать + + + Имя + + + Размер + + + Тип + + + файл + + + файла + + + файлов + + + О программе + + + Удалён файл {0} + + + каталог не найден + + + архив пуст + + + Ошибка извлечения файла + + + Ошибка открытия файла + + + {0} извлечён в {1} + + + Извлечено {0} {1} + + + Извлекаются файлы из {0} + + + Файл извлекается из {0} + + + Извлекаются файлы из {0} в {1} + + + Изображение {0} x {1} пикселей + + + отсутствуют файлы, удовлетворяющие выбранным критериям + + + Готов + + + не удалось интерпретировать формат изображения + + + файл не может быть открыт как архив ресурсов + + + Версия {0} + + + [встроен] + + + Поддерживаемые архивы: + + + Поддерживаемые форматы изображений: + + + Об обозревателе игровых ресурсов + + + исходном + + + Выберите место извлечения + + + <DIR> + + + Кодировка текста + + + Извлечь все файлы из {0} в + + + Извлечь {0} в + + + Извлекать изображения + + + Извлекать текст + + + Извлечь из архива + + + OK + + + Параметры архива + + + Сохранить в формате + + + Сохранить в формате + + + Обозреватель игровых ресурсов + + + Назад + + + Вперёд + + \ No newline at end of file diff --git a/Utility.cs b/Utility.cs new file mode 100644 index 00000000..a9272457 --- /dev/null +++ b/Utility.cs @@ -0,0 +1,117 @@ +//! \file Utility.cs +//! \date Sun Jul 06 07:40:34 2014 +//! \brief utility classes. +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Windows.Input; +using GARbro.GUI.Strings; + +namespace GARbro.GUI +{ + internal class NativeMethods + { + [DllImport ("shlwapi.dll", CharSet = CharSet.Unicode)] + internal static extern int StrCmpLogicalW (string psz1, string psz2); + + [DllImport ("gdi32.dll")] + internal static extern int GetDeviceCaps (IntPtr hDc, int nIndex); + + [DllImport ("user32.dll")] + internal static extern IntPtr GetDC (IntPtr hWnd); + + [DllImport ("user32.dll")] + internal static extern int ReleaseDC (IntPtr hWnd, IntPtr hDc); + } + + public static class Desktop + { + public static int DpiX { get { return dpi_x; } } + public static int DpiY { get { return dpi_y; } } + + public const int LOGPIXELSX = 88; + public const int LOGPIXELSY = 90; + + private static int dpi_x = GetCaps (LOGPIXELSX); + private static int dpi_y = GetCaps (LOGPIXELSY); + + public static int GetCaps (int cap) + { + IntPtr hdc = NativeMethods.GetDC (IntPtr.Zero); + if (hdc == IntPtr.Zero) + return 96; + int dpi = NativeMethods.GetDeviceCaps (hdc, cap); + NativeMethods.ReleaseDC (IntPtr.Zero, hdc); + return dpi; + } + } + + public sealed class NumericStringComparer : IComparer + { + public int Compare (string a, string b) + { + return NativeMethods.StrCmpLogicalW (a, b); + } + } + + public class WaitCursor : IDisposable + { + private Cursor m_previousCursor; + + public WaitCursor() + { + m_previousCursor = Mouse.OverrideCursor; + Mouse.OverrideCursor = Cursors.Wait; + } + + #region IDisposable Members + bool disposed = false; + public void Dispose() + { + Dispose (true); + GC.SuppressFinalize (this); + } + + protected virtual void Dispose (bool disposing) + { + if (!disposed) + { + Mouse.OverrideCursor = m_previousCursor; + disposed = true; + } + } + #endregion + } + + public static class Localization + { + public static string Plural (int n, string en_singular) + { + string suffix; + if (CultureInfo.CurrentUICulture.Name == "ru-RU") + { + suffix = (n%10==1 && n%100!=11 ? "1" : n%10>=2 && n% 10<=4 && (n%100<10 || n%100>=20) ? "2" : "3"); + } + else // assume en-EN + { + suffix = 1 == n ? "1" : "2"; + } + try + { + var res = guiStrings.ResourceManager.GetString ("LP"+en_singular+suffix); + return res ?? en_singular; + } + catch + { + return en_singular; + } + } + + // Localization.Format ("{0:file:files} copied", count); +// public static string Format (string format, params object[] args); + } +} diff --git a/ViewModel.cs b/ViewModel.cs new file mode 100644 index 00000000..6ec735d3 --- /dev/null +++ b/ViewModel.cs @@ -0,0 +1,296 @@ +//! \file ViewModel.cs +//! \date Wed Jul 02 07:29:11 2014 +//! \brief GARbro directory list. +// + +using System; +using System.IO; +using System.Linq; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Text.RegularExpressions; +using System.Globalization; +using GameRes; +using GARbro.GUI.Strings; + +namespace GARbro.GUI +{ + public class SubDirEntry : GameRes.Entry + { + public override string Type { get { return guiStrings.TextDirType; } } + + public SubDirEntry (string name) + { + Name = name; + Size = 0; + } + } + + public class DirectoryViewModel : ObservableCollection + { + public string Path { get; private set; } + public ICollection Source { get; private set; } + public virtual bool IsArchive { get { return false; } } + + public DirectoryViewModel (string path, ICollection filelist) + { + Path = path; + Source = filelist; + ImportFromSource(); + } + + protected virtual void ImportFromSource () + { + if (!string.IsNullOrEmpty (Path) && null != Directory.GetParent (Path)) + { + Add (new EntryViewModel (new SubDirEntry (".."), -2)); + } + foreach (var entry in Source) + { + int prio = null == entry as SubDirEntry ? 0 : -1; + Add (new EntryViewModel (entry, prio)); + } + } + + public EntryViewModel Find (string name) + { + return this.FirstOrDefault (e => e.Name.Equals (name, System.StringComparison.OrdinalIgnoreCase)); + } + + public virtual void SetPosition (DirectoryPosition pos) + { + } + } + + public class ArchiveViewModel : DirectoryViewModel + { + public override bool IsArchive { get { return true; } } + public string SubDir { get; protected set; } + + public ArchiveViewModel (string path, ArcFile arc) + : base (path, arc.Dir) + { + } + + protected override void ImportFromSource () + { + UpdateModel (""); + } + + private string m_delimiter = "/"; + private static readonly char[] m_path_delimiters = { '/', '\\' }; + + public void ChDir (string subdir) + { + string new_path; + if (".." == subdir) + { + if (0 == SubDir.Length) + return; + var path = SubDir.Split (m_path_delimiters); + if (path.Length > 1) + new_path = string.Join (m_delimiter, path, 0, path.Length-1); + else + new_path = ""; + } + else + { + var entry = this.FirstOrDefault (e => e.Name.Equals (subdir, StringComparison.OrdinalIgnoreCase)); + if (null == entry) + throw new DirectoryNotFoundException (string.Format ("{1}: {0}", guiStrings.MsgDirectoryNotFound, subdir)); + if (SubDir.Length > 0) + new_path = SubDir + m_delimiter + entry.Name; + else + new_path = entry.Name; + } + UpdateModel (new_path); + } + + static readonly Regex path_re = new Regex (@"\G[/\\]?([^/\\]+)([/\\])"); + + private void UpdateModel (string root_path) + { + IEnumerable dir = Source; + if (!string.IsNullOrEmpty (root_path)) + dir = from entry in dir + where entry.Name.StartsWith (root_path+m_delimiter) + select entry; + if (!dir.Any()) + { + throw new DirectoryNotFoundException (string.Format ("{1}: {0}", guiStrings.MsgDirectoryNotFound, root_path)); + } + m_suppress_notification = true; + try + { + this.Clear(); + SubDir = root_path; + Add (new EntryViewModel (new SubDirEntry (".."), -2)); + var subdirs = new HashSet(); + foreach (var entry in dir) + { + var match = path_re.Match (entry.Name, root_path.Length); + if (match.Success) + { + string name = match.Groups[1].Value; + if (subdirs.Add (name)) + { + m_delimiter = match.Groups[2].Value; + Add (new EntryViewModel (new SubDirEntry (name), -1)); + } + } + else + { + Add (new EntryViewModel (entry, 0)); + } + } + } + finally + { + m_suppress_notification = false; + OnCollectionChanged (new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + } + + public override void SetPosition (DirectoryPosition pos) + { + UpdateModel (pos.ArchivePath); + } + + public IEnumerable GetFiles (EntryViewModel entry) + { + if (!entry.IsDirectory) + return new Entry[] { entry.Source }; + + string path = GetPath (entry.Name); + return from file in Source + where file.Name.StartsWith (path) + select file; + } + + string GetPath (string dir) + { + if (SubDir.Length > 0) + return SubDir + m_delimiter + dir + m_delimiter; + else + return dir + m_delimiter; + } + + private bool m_suppress_notification = false; + + protected override void OnCollectionChanged (NotifyCollectionChangedEventArgs e) + { + if (!m_suppress_notification) + base.OnCollectionChanged(e); + } + } + + public class EntryViewModel + { + public EntryViewModel (Entry entry, int priority) + { + Source = entry; + Name = Path.GetFileName (entry.Name); + Priority = priority; + } + + public Entry Source { get; private set; } + + public string Name { get; private set; } + public string Type { get { return Source.Type; } } + public uint Size { get { return Source.Size; } } + public int Priority { get; private set; } + public bool IsDirectory { get { return Priority < 0; } } + } + + public sealed class FileSystemComparer : IComparer + { + private string m_property; + private int m_direction; + private static Comparer s_default_comparer = new Comparer (CultureInfo.CurrentUICulture); + + public FileSystemComparer (string property, ListSortDirection direction) + { + m_property = property; + m_direction = direction == ListSortDirection.Ascending ? 1 : -1; + } + + public int Compare (object a, object b) + { + var v_a = a as EntryViewModel; + var v_b = b as EntryViewModel; + if (null == v_a || null == v_b) + return s_default_comparer.Compare (a, b) * m_direction; + + if (v_a.Priority < v_b.Priority) + return -1; + if (v_a.Priority > v_b.Priority) + return 1; + if (string.IsNullOrEmpty (m_property)) + return 0; + int order; + if (m_property != "Name") + { + if ("Type" == m_property) + { + // empty strings placed in the end + if (string.IsNullOrEmpty (v_a.Type)) + order = string.IsNullOrEmpty (v_b.Type) ? 0 : m_direction; + else if (string.IsNullOrEmpty (v_b.Type)) + order = -m_direction; + else + order = string.Compare (v_a.Type, v_b.Type, true) * m_direction; + } + else + { + var prop_a = a.GetType ().GetProperty (m_property).GetValue (a); + var prop_b = b.GetType ().GetProperty (m_property).GetValue (b); + order = s_default_comparer.Compare (prop_a, prop_b) * m_direction; + } + if (0 == order) + order = NativeMethods.StrCmpLogicalW (v_a.Name, v_b.Name); + } + else + order = NativeMethods.StrCmpLogicalW (v_a.Name, v_b.Name) * m_direction; + return order; + } + } + + /// + /// Image format model for formats drop-down list widgets. + /// + public class ImageFormatModel + { + public ImageFormat Source { get; private set; } + public string Tag { + get { return null != Source ? Source.Tag : guiStrings.TextAsIs; } + } + + public ImageFormatModel (ImageFormat impl = null) + { + Source = impl; + } + } + + /// + /// Stores current position within directory view model. + /// + public class DirectoryPosition + { + public string Path { get; set; } + public string ArchivePath { get; set; } + public EntryViewModel Item { get; set; } + + public DirectoryPosition (DirectoryViewModel vm, EntryViewModel item) + { + Path = vm.Path; + Item = item; + if (vm.IsArchive) + ArchivePath = (vm as ArchiveViewModel).SubDir; + else + ArchivePath = ""; + } + } +} diff --git a/packages.config b/packages.config new file mode 100644 index 00000000..316816f4 --- /dev/null +++ b/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/zlib/changes.txt b/zlib/changes.txt new file mode 100644 index 00000000..2a9c0c11 --- /dev/null +++ b/zlib/changes.txt @@ -0,0 +1,25 @@ +v1.3 june 2013 +Upgrade zlib to 1.2.8 +Make custom build v1.2.8.1 where 1 bugs fixed: Incorrect move method was sent to SetFilePointer. +Fix bug where utf8 flag was not set correctly. +Change UTF8Encoding to default true. +Support unicode in filename of zip itself. + +v1.2 6.may 2012 +-Upgrade zlib to 1.2.7 +-Change Zip64 option to enum (Yes,No,Auto) where Auto now is default (was No) + +v1.1 6.jan 2011 +Fixed bug: If Unzipper.ItemList had more than one entry, unzip would not work (exctracted files would try to overwrite themself) + +Update zlib dlls to custom build v1.2.5.1 where 2 bugs fixed: +-Adding over 64k number of entries in zip with total size below 4GB created invalid zip +-Writing entries over 4GB with zip64=false did not fail but created invalid zip + +Misc: +Convert unsafe code to safe code. +Wrap CloseCurrentEntry in try/finally block: we must always CloseFile, even if CloseCurrentEntry fails. +Zip64 zipping works now. + +v1.0 +Initial release diff --git a/zlib/zlib32.dll b/zlib/zlib32.dll new file mode 100644 index 00000000..ae69a87a Binary files /dev/null and b/zlib/zlib32.dll differ diff --git a/zlib/zlib64.dll b/zlib/zlib64.dll new file mode 100644 index 00000000..20233f2f Binary files /dev/null and b/zlib/zlib64.dll differ diff --git a/zlib/zlibnet.chm b/zlib/zlibnet.chm new file mode 100644 index 00000000..153b5075 Binary files /dev/null and b/zlib/zlibnet.chm differ diff --git a/zlib/zlibnet.dll b/zlib/zlibnet.dll new file mode 100644 index 00000000..84a100fc Binary files /dev/null and b/zlib/zlibnet.dll differ