From 9d1d320cd2b0ea92a2e19c1f5b54884e2558988b Mon Sep 17 00:00:00 2001 From: morkt Date: Mon, 31 Aug 2015 10:48:27 +0400 Subject: [PATCH] virtual file system preliminary implementation. --- App.xaml.cs | 105 ----------- ArcFormats/Properties/AssemblyInfo.cs | 4 +- GARbro.sln | 25 +-- GameRes/ArcFile.cs | 111 +---------- GameRes/FileSystem.cs | 249 ++++++++++++++++++++++++- GameRes/GameRes.cs | 6 + GameRes/Properties/AssemblyInfo.cs | 4 +- GameRes/Strings/garStrings.Designer.cs | 11 +- GameRes/Strings/garStrings.resx | 3 + GameRes/Strings/garStrings.ru-RU.resx | 3 + GarConvert.cs | 2 +- GarExtract.cs | 18 +- ImagePreview.cs | 21 +-- MainWindow.xaml.cs | 93 ++++----- Properties/AssemblyInfo.cs | 4 +- Strings/guiStrings.Designer.cs | 9 - Strings/guiStrings.resx | 3 - Strings/guiStrings.ru-RU.resx | 3 - ViewModel.cs | 28 ++- 19 files changed, 352 insertions(+), 350 deletions(-) diff --git a/App.xaml.cs b/App.xaml.cs index ba88a117..4a3e6144 100644 --- a/App.xaml.cs +++ b/App.xaml.cs @@ -49,16 +49,6 @@ namespace GARbro.GUI /// public string InitPath { get; private set; } - /// - /// Path to the currently open archive, or empty string if none. - /// - public string CurrentPath { get; private set; } - - /// - /// Archive file being browsed, or null. - /// - public ArcFile CurrentArchive { get; private set; } - void ApplicationStartup (object sender, StartupEventArgs e) { #if DEBUG @@ -90,106 +80,11 @@ namespace GARbro.GUI if (string.IsNullOrEmpty (InitPath)) InitPath = Directory.GetCurrentDirectory(); - - CurrentPath = ""; } void ApplicationExit (object sender, ExitEventArgs e) { Settings.Default.Save(); } - - public ICollection GetDirectoryList (string path) - { - var info = new DirectoryInfo (path); - var list = new List(); - foreach (var subdir in info.EnumerateDirectories()) - { - if (0 != (subdir.Attributes & (FileAttributes.Hidden | FileAttributes.System))) - continue; - list.Add (new SubDirEntry (subdir.Name)); - } - foreach (var file in info.EnumerateFiles()) - { - if (0 != (file.Attributes & (FileAttributes.System))) - continue; - var entry = FormatCatalog.Instance.Create (file.Name); - entry.Size = (uint)Math.Min (file.Length, uint.MaxValue); - list.Add (entry); - } - return list; - } - - public ArcFile GetArchive (string path) - { - if (path.Equals (CurrentPath, StringIgnoreCase)) - return CurrentArchive; - FormatCatalog.Instance.LastError = null; - var arc = ArcFile.TryOpen (path); - if (null == arc) - { - if (null != FormatCatalog.Instance.LastError) - throw FormatCatalog.Instance.LastError; - throw new UnknownFormatException(); - } - if (null != CurrentArchive) - CurrentArchive.Dispose(); - CurrentPath = path; - CurrentArchive = arc; - return CurrentArchive; - } - - public void ResetCache () - { - if (null != CurrentArchive) - CurrentArchive.Dispose(); - CurrentArchive = null; - CurrentPath = ""; - } - - // Update UI on demand. - - private static DispatcherOperationCallback exitFrameCallback = - new DispatcherOperationCallback(ExitFrame); - - /// - /// Processes all UI messages currently in the message queue. - /// - public static void DoEvents() - { - // Create new nested message pump. - DispatcherFrame nestedFrame = new DispatcherFrame(); - - // Dispatch a callback to the current message queue, when getting called, - // this callback will end the nested message loop. - // note that the priority of this callback should be lower than the that of UI event messages. - DispatcherOperation exitOperation = Dispatcher.CurrentDispatcher.BeginInvoke( - DispatcherPriority.Background, exitFrameCallback, nestedFrame); - - // pump the nested message loop, the nested message loop will - // immediately process the messages left inside the message queue. - Dispatcher.PushFrame(nestedFrame); - - // If the "exitFrame" callback doesn't get finished, Abort it. - if (exitOperation.Status != DispatcherOperationStatus.Completed) - exitOperation.Abort(); - } - - static Object ExitFrame(Object state) - { - DispatcherFrame frame = state as DispatcherFrame; - - // Exit the nested message loop. - frame.Continue = false; - return null; - } - } - - public class UnknownFormatException : Exception - { - public UnknownFormatException () : base (guiStrings.MsgUnknownFormat) { } - public UnknownFormatException (string path) - : base (string.Format ("{1}: {0}", guiStrings.MsgUnknownFormat, path)) - { } } } diff --git a/ArcFormats/Properties/AssemblyInfo.cs b/ArcFormats/Properties/AssemblyInfo.cs index 3809b2ec..2dfab110 100644 --- a/ArcFormats/Properties/AssemblyInfo.cs +++ b/ArcFormats/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // 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.1.10.422")] -[assembly: AssemblyFileVersion ("1.1.10.422")] +[assembly: AssemblyVersion ("1.1.12.423")] +[assembly: AssemblyFileVersion ("1.1.12.423")] diff --git a/GARbro.sln b/GARbro.sln index e2c79385..ea6b4b33 100644 --- a/GARbro.sln +++ b/GARbro.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 -VisualStudioVersion = 12.0.21005.1 +VisualStudioVersion = 12.0.31101.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GARbro.Console", "Console\GARbro.Console.csproj", "{B966F292-431A-4D8A-A1D3-1EB45048A1D2}" ProjectSection(ProjectDependencies) = postProject @@ -17,9 +17,6 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameRes", "GameRes\GameRes.csproj", "{453C087F-E416-4AE9-8C03-D8760DA0574B}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GARbro.GUI", "GARbro.GUI.csproj", "{2935BE57-C4E0-43E7-86DE-C1848C820B19}" - ProjectSection(ProjectDependencies) = postProject - {A8865685-27CC-427B-AC38-E48D2AD05DF4} = {A8865685-27CC-427B-AC38-E48D2AD05DF4} - EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Image.Convert", "Image.Convert\Image.Convert.csproj", "{757EB8B1-F62C-4690-AC3D-DAE4A5576B3E}" ProjectSection(ProjectDependencies) = postProject @@ -32,26 +29,22 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B966F292-431A-4D8A-A1D3-1EB45048A1D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B966F292-431A-4D8A-A1D3-1EB45048A1D2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B966F292-431A-4D8A-A1D3-1EB45048A1D2}.Debug|Any CPU.ActiveCfg = Release|Any CPU {B966F292-431A-4D8A-A1D3-1EB45048A1D2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B966F292-431A-4D8A-A1D3-1EB45048A1D2}.Release|Any CPU.Build.0 = Release|Any CPU - {A8865685-27CC-427B-AC38-E48D2AD05DF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A8865685-27CC-427B-AC38-E48D2AD05DF4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8865685-27CC-427B-AC38-E48D2AD05DF4}.Debug|Any CPU.ActiveCfg = Release|Any CPU + {A8865685-27CC-427B-AC38-E48D2AD05DF4}.Debug|Any CPU.Build.0 = Release|Any CPU {A8865685-27CC-427B-AC38-E48D2AD05DF4}.Release|Any CPU.ActiveCfg = Release|Any CPU {A8865685-27CC-427B-AC38-E48D2AD05DF4}.Release|Any CPU.Build.0 = Release|Any CPU - {453C087F-E416-4AE9-8C03-D8760DA0574B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {453C087F-E416-4AE9-8C03-D8760DA0574B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {453C087F-E416-4AE9-8C03-D8760DA0574B}.Debug|Any CPU.ActiveCfg = Release|Any CPU + {453C087F-E416-4AE9-8C03-D8760DA0574B}.Debug|Any CPU.Build.0 = Release|Any CPU {453C087F-E416-4AE9-8C03-D8760DA0574B}.Release|Any CPU.ActiveCfg = Release|Any CPU {453C087F-E416-4AE9-8C03-D8760DA0574B}.Release|Any CPU.Build.0 = Release|Any CPU - {2935BE57-C4E0-43E7-86DE-C1848C820B19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2935BE57-C4E0-43E7-86DE-C1848C820B19}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2935BE57-C4E0-43E7-86DE-C1848C820B19}.Debug|Any CPU.ActiveCfg = Release|Any CPU + {2935BE57-C4E0-43E7-86DE-C1848C820B19}.Debug|Any CPU.Build.0 = Release|Any CPU {2935BE57-C4E0-43E7-86DE-C1848C820B19}.Release|Any CPU.ActiveCfg = Release|Any CPU {2935BE57-C4E0-43E7-86DE-C1848C820B19}.Release|Any CPU.Build.0 = Release|Any CPU - {757EB8B1-F62C-4690-AC3D-DAE4A5576B3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {757EB8B1-F62C-4690-AC3D-DAE4A5576B3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {757EB8B1-F62C-4690-AC3D-DAE4A5576B3E}.Debug|Any CPU.ActiveCfg = Release|Any CPU {757EB8B1-F62C-4690-AC3D-DAE4A5576B3E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {757EB8B1-F62C-4690-AC3D-DAE4A5576B3E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/GameRes/ArcFile.cs b/GameRes/ArcFile.cs index a75100d3..550ec121 100644 --- a/GameRes/ArcFile.cs +++ b/GameRes/ArcFile.cs @@ -49,8 +49,6 @@ namespace GameRes /// Archive contents. public ICollection Dir { get { return m_dir; } } - public event EventHandler OverwriteNotify; - public ArcFile (ArcView arc, ArchiveFormat impl, ICollection dir) { m_arc = arc; @@ -66,11 +64,11 @@ namespace GameRes /// public static ArcFile TryOpen (string filename) { - var info = new FileInfo (filename); - if (info.Length < 4) + var entry = VFS.FindFile (filename); + if (entry.Size < 4) return null; var ext = new Lazy (() => Path.GetExtension (filename).TrimStart ('.').ToLowerInvariant()); - var file = new ArcView (filename); + var file = VFS.OpenView (entry); try { uint signature = file.View.ReadUInt32 (0); @@ -191,15 +189,6 @@ namespace GameRes } } - /// - /// Create file corresponding to within current directory and open - /// it for writing. - /// - public Stream CreateFile (Entry entry) - { - return ArchiveFormat.CreateFile (entry.Name); - } - public IFileSystem CreateFileSystem () { if (m_interface.IsHierarchic) @@ -229,98 +218,4 @@ namespace GameRes } #endregion } - - public class OverwriteEventArgs : EventArgs - { - public string Filename { get; set; } - public bool Overwrite { get; set; } - } - - public class AppendStream : System.IO.Stream - { - private Stream m_base; - private long m_start_pos; - - public override bool CanRead { get { return true; } } - public override bool CanSeek { get { return true; } } - public override bool CanWrite { get { return true; } } - public override long Length { get { return m_base.Length - m_start_pos; } } - public override long Position - { - get { return m_base.Position - m_start_pos; } - set { m_base.Position = Math.Max (m_start_pos+value, m_start_pos); } - } - - public AppendStream (System.IO.Stream file) - { - m_base = file; - m_start_pos = m_base.Seek (0, SeekOrigin.End); - } - - public AppendStream (System.IO.Stream file, long offset) - { - m_base = file; - m_start_pos = m_base.Seek (offset, SeekOrigin.Begin); - } - - public Stream BaseStream { get { return m_base; } } - - public override void Flush() - { - m_base.Flush(); - } - - public override long Seek (long offset, SeekOrigin origin) - { - if (SeekOrigin.Begin == origin) - { - offset = Math.Max (offset + m_start_pos, m_start_pos); - } - long position = m_base.Seek (offset, origin); - if (position < m_start_pos) - { - m_base.Seek (m_start_pos, SeekOrigin.Begin); - position = m_start_pos; - } - return position - m_start_pos; - } - - public override void SetLength (long length) - { - if (length < 0) - length = 0; - m_base.SetLength (length + m_start_pos); - } - - public override int Read (byte[] buffer, int offset, int count) - { - return m_base.Read (buffer, offset, count); - } - - public override int ReadByte () - { - return m_base.ReadByte(); - } - - public override void Write (byte[] buffer, int offset, int count) - { - m_base.Write (buffer, offset, count); - } - - public override void WriteByte (byte value) - { - m_base.WriteByte (value); - } - - bool disposed = false; - protected override void Dispose (bool disposing) - { - if (!disposed) - { - m_base = null; - disposed = true; - base.Dispose (disposing); - } - } - } } diff --git a/GameRes/FileSystem.cs b/GameRes/FileSystem.cs index e4ea47dd..32d2b85e 100644 --- a/GameRes/FileSystem.cs +++ b/GameRes/FileSystem.cs @@ -28,16 +28,28 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; +using GameRes.Strings; namespace GameRes { public interface IFileSystem : IDisposable { /// - /// Open file for reading. + /// Returns entry corresponding to the given filename within filesystem. + /// + /// File is not found. + Entry FindFile (string filename); + + /// + /// Open file for reading as stream. /// Stream OpenStream (Entry entry); + Stream OpenSeekableStream (Entry entry); + + /// + /// Open file for reading as memory-mapped view. + /// ArcView OpenView (Entry entry); /// @@ -47,7 +59,7 @@ namespace GameRes /// /// Recursively enumerates files in the current directory and its subdirectories. - /// Subdirectory entries are omitted. + /// Subdirectory entries are omitted from resulting set. /// IEnumerable GetFilesRecursive (); @@ -73,6 +85,15 @@ namespace GameRes set { Directory.SetCurrentDirectory (value); } } + public Entry FindFile (string filename) + { + var attr = File.GetAttributes (filename); + if ((attr & FileAttributes.Directory) == FileAttributes.Directory) + return new SubDirEntry (filename); + else + return EntryFromFileInfo (new FileInfo (filename)); + } + public IEnumerable GetFiles () { var info = new DirectoryInfo (CurrentDirectory); @@ -84,7 +105,7 @@ namespace GameRes } foreach (var file in info.EnumerateFiles()) { - if (0 != (file.Attributes & (FileAttributes.Hidden | FileAttributes.System))) + if (0 != (file.Attributes & FileAttributes.System)) continue; yield return EntryFromFileInfo (file); } @@ -113,6 +134,11 @@ namespace GameRes return File.OpenRead (entry.Name); } + public Stream OpenSeekableStream (Entry entry) + { + return OpenStream (entry); + } + public ArcView OpenView (Entry entry) { return new ArcView (entry.Name); @@ -126,7 +152,10 @@ namespace GameRes public class FlatArchiveFileSystem : IFileSystem { - protected ArcFile m_arc; + protected readonly ArcFile m_arc; + protected readonly Dictionary m_dir; + + public ArcFile Source { get { return m_arc; } } public virtual string CurrentDirectory { @@ -146,6 +175,11 @@ namespace GameRes public FlatArchiveFileSystem (ArcFile arc) { m_arc = arc; + m_dir = new Dictionary (arc.Dir.Count, StringComparer.InvariantCultureIgnoreCase); + foreach (var entry in arc.Dir) + { + m_dir.Add (entry.Name, entry); + } } public Stream OpenStream (Entry entry) @@ -153,11 +187,24 @@ namespace GameRes return m_arc.OpenEntry (entry); } + public Stream OpenSeekableStream (Entry entry) + { + return m_arc.OpenSeekableEntry (entry); + } + public ArcView OpenView (Entry entry) { return m_arc.OpenView (entry); } + public virtual Entry FindFile (string filename) + { + Entry entry = null; + if (!m_dir.TryGetValue (filename, out entry)) + throw new FileNotFoundException(); + return entry; + } + public virtual IEnumerable GetFiles () { return m_arc.Dir; @@ -211,6 +258,16 @@ namespace GameRes set { ChDir (value); } } + public override Entry FindFile (string filename) + { + Entry entry = null; + if (m_dir.TryGetValue (filename, out entry)) + return entry; + if (m_dir.Keys.Any (n => n.StartsWith (filename + PathDelimiter))) + return new SubDirEntry (filename); + throw new FileNotFoundException(); + } + static readonly Regex path_re = new Regex (@"\G[/\\]?([^/\\]+)([/\\])"); public override IEnumerable GetFiles () @@ -300,4 +357,188 @@ namespace GameRes m_cwd = new_path; } } + + public sealed class FileSystemStack : IDisposable + { + Stack m_fs_stack = new Stack(); + Stack m_arc_name_stack = new Stack(); + + public IEnumerable All { get { return m_fs_stack; } } + + public IFileSystem Top { get { return m_fs_stack.Peek(); } } + public int Count { get { return m_fs_stack.Count; } } + public IEnumerable ArcStack { get { return m_arc_name_stack; } } + + public ArcFile CurrentArchive { get; private set; } + private IFileSystem LastVisitedArc { get; set; } + private string LastVisitedPath { get; set; } + + public FileSystemStack () + { + m_fs_stack.Push (new PhysicalFileSystem()); + } + + public void ChDir (Entry entry) + { + if (entry is SubDirEntry) + { + if (1 == m_fs_stack.Count) + { + Top.CurrentDirectory = entry.Name; + return; + } + if (".." == entry.Name && string.IsNullOrEmpty (Top.CurrentDirectory)) + { + Pop(); + return; + } + } + if (entry.Name == LastVisitedPath && null != LastVisitedArc) + { + Push (LastVisitedPath, LastVisitedArc); + return; + } + Flush(); + var arc = ArcFile.TryOpen (entry.Name); + if (null == arc) + throw new UnknownFormatException(); + try + { + Push (entry.Name, arc.CreateFileSystem()); + CurrentArchive = arc; + } + catch + { + arc.Dispose(); + throw; + } + } + + private void Push (string path, IFileSystem fs) + { + m_fs_stack.Push (fs); + m_arc_name_stack.Push (path); + } + + private void Pop () + { + if (m_fs_stack.Count > 1) + { + Flush(); + LastVisitedArc = m_fs_stack.Pop(); + LastVisitedPath = m_arc_name_stack.Pop(); + if (m_fs_stack.Count > 1 && m_fs_stack.Peek() is FlatArchiveFileSystem) + CurrentArchive = (m_fs_stack.Peek() as FlatArchiveFileSystem).Source; + else + CurrentArchive = null; + } + } + + public void Flush () + { + if (LastVisitedArc != null) + { + LastVisitedArc.Dispose(); + LastVisitedArc = null; + LastVisitedPath = null; + } + } + + private bool _disposed = false; + public void Dispose () + { + if (!_disposed) + { + Flush(); + foreach (var fs in m_fs_stack.Reverse()) + fs.Dispose(); + _disposed = true; + } + GC.SuppressFinalize (this); + } + } + + public static class VFS + { + private static FileSystemStack m_vfs = new FileSystemStack(); + + public static IFileSystem Top { get { return m_vfs.Top; } } + public static bool IsVirtual { get { return m_vfs.Count > 1; } } + public static int Count { get { return m_vfs.Count; } } + + public static ArcFile CurrentArchive { get { return m_vfs.CurrentArchive; } } + + private static string[] m_top_path = new string[1]; + + public static IEnumerable FullPath + { + get + { + m_top_path[0] = Top.CurrentDirectory; + if (1 == Count) + return m_top_path; + else + return m_vfs.ArcStack.Concat (m_top_path); + } + set + { + if (!value.Any()) + return; + var new_vfs = new FileSystemStack(); + var desired = value.ToArray(); + for (int i = 0; i < desired.Length-1; ++i) + new_vfs.ChDir (new_vfs.Top.FindFile (desired[i])); + new_vfs.Top.CurrentDirectory = desired.Last(); + m_vfs.Dispose(); + m_vfs = new_vfs; + } + } + + public static Entry FindFile (string filename) + { + if (".." == filename) + return new SubDirEntry (".."); + return m_vfs.Top.FindFile (filename); + } + + public static Stream OpenStream (Entry entry) + { + return m_vfs.Top.OpenStream (entry); + } + + public static Stream OpenSeekableStream (Entry entry) + { + return m_vfs.Top.OpenSeekableStream (entry); + } + + public static ArcView OpenView (Entry entry) + { + return m_vfs.Top.OpenView (entry); + } + + public static void ChDir (Entry entry) + { + m_vfs.ChDir (entry); + } + + public static void ChDir (string path) + { + m_vfs.ChDir (FindFile (path)); + } + + public static void Flush () + { + m_vfs.Flush(); + } + + public static IEnumerable GetFiles () + { + return m_vfs.Top.GetFiles(); + } + } + + public class UnknownFormatException : FileFormatException + { + public UnknownFormatException () : base (garStrings.MsgUnknownFormat) { } + } } diff --git a/GameRes/GameRes.cs b/GameRes/GameRes.cs index 3a6f5ec9..373033d7 100644 --- a/GameRes/GameRes.cs +++ b/GameRes/GameRes.cs @@ -261,6 +261,12 @@ namespace GameRes public ResourceOptions Options { get; set; } } + public class OverwriteEventArgs : EventArgs + { + public string Filename { get; set; } + public bool Overwrite { get; set; } + } + public sealed class FormatCatalog { private static readonly FormatCatalog m_instance = new FormatCatalog(); diff --git a/GameRes/Properties/AssemblyInfo.cs b/GameRes/Properties/AssemblyInfo.cs index 1ce73221..9ed6d3e2 100644 --- a/GameRes/Properties/AssemblyInfo.cs +++ b/GameRes/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // 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.1.7.90")] -[assembly: AssemblyFileVersion ("1.1.7.90")] +[assembly: AssemblyVersion ("1.1.9.91")] +[assembly: AssemblyFileVersion ("1.1.9.91")] diff --git a/GameRes/Strings/garStrings.Designer.cs b/GameRes/Strings/garStrings.Designer.cs index 7cdce5cd..0baa50f2 100644 --- a/GameRes/Strings/garStrings.Designer.cs +++ b/GameRes/Strings/garStrings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.18444 +// Runtime Version:4.0.30319.34209 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -104,5 +104,14 @@ namespace GameRes.Strings { return ResourceManager.GetString("MsgUnknownEncryption", 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); + } + } } } diff --git a/GameRes/Strings/garStrings.resx b/GameRes/Strings/garStrings.resx index 480912c6..dcfe9a63 100644 --- a/GameRes/Strings/garStrings.resx +++ b/GameRes/Strings/garStrings.resx @@ -132,4 +132,7 @@ Unknown encryption scheme + + file could not be opened as resource archive + \ No newline at end of file diff --git a/GameRes/Strings/garStrings.ru-RU.resx b/GameRes/Strings/garStrings.ru-RU.resx index c8040a17..04b4d9e3 100644 --- a/GameRes/Strings/garStrings.ru-RU.resx +++ b/GameRes/Strings/garStrings.ru-RU.resx @@ -132,4 +132,7 @@ Неизвестный метод шифрования + + файл не может быть открыт как архив ресурсов + \ No newline at end of file diff --git a/GarConvert.cs b/GarConvert.cs index 72ab5cd4..cadbd6c5 100644 --- a/GarConvert.cs +++ b/GarConvert.cs @@ -68,7 +68,7 @@ namespace GARbro.GUI } try { - Directory.SetCurrentDirectory (ViewModel.Path); + Directory.SetCurrentDirectory (ViewModel.Path.First()); var converter = new GarConvertMedia (this); converter.IgnoreErrors = convert_dialog.IgnoreErrors.IsChecked ?? false; converter.Convert (source, format); diff --git a/GarExtract.cs b/GarExtract.cs index 49b16e72..8d619c65 100644 --- a/GarExtract.cs +++ b/GarExtract.cs @@ -34,6 +34,7 @@ using System.Windows.Input; using System.Windows.Media.Imaging; using Ookii.Dialogs.Wpf; using GameRes; +using GameRes.Strings; using GARbro.GUI.Strings; using GARbro.GUI.Properties; @@ -58,11 +59,12 @@ namespace GARbro.GUI string destination = Settings.Default.appLastDestination; if (!Directory.Exists (destination)) destination = ""; - if (!ViewModel.IsArchive) + var vm = ViewModel; + if (!vm.IsArchive) { if (!entry.IsDirectory) { - var arc_dir = CurrentPath; + var arc_dir = vm.Path.First(); var source = Path.Combine (arc_dir, entry.Name); if (string.IsNullOrEmpty (destination)) destination = arc_dir; @@ -73,13 +75,13 @@ namespace GARbro.GUI extractor.ExtractAll (destination); } } - else if (null != m_app.CurrentArchive) + else if (vm.Path.Skip (1).Any()) { - var vm = ViewModel as ArchiveViewModel; if (string.IsNullOrEmpty (destination)) - destination = Path.GetDirectoryName (vm.Path); - extractor = new GarExtract (this, vm.Path, m_app.CurrentArchive); - if (null == entry || (entry.Name == ".." && vm.SubDir == "")) // root entry + destination = Path.GetDirectoryName (vm.Path.First()); + var archive_name = vm.Path.Reverse().Skip (1).First(); + extractor = new GarExtract (this, archive_name, VFS.CurrentArchive); + if (null == entry || (entry.Name == ".." && string.IsNullOrEmpty (vm.Path.Last()))) // root entry extractor.ExtractAll (destination); else extractor.Extract (entry, destination); @@ -132,7 +134,7 @@ namespace GARbro.GUI if (FormatCatalog.Instance.LastError != null) error_message = FormatCatalog.Instance.LastError.Message; else - error_message = guiStrings.MsgUnknownFormat; + error_message = garStrings.MsgUnknownFormat; throw new OperationCanceledException (string.Format ("{1}: {0}", error_message, m_arc_name)); } m_should_dispose = true; diff --git a/ImagePreview.cs b/ImagePreview.cs index af776547..117ccaf9 100644 --- a/ImagePreview.cs +++ b/ImagePreview.cs @@ -75,14 +75,13 @@ namespace GARbro.GUI class PreviewFile { - public string Path { get; set; } + public IEnumerable Path { get; set; } public string Name { get; set; } - public Entry Entry { get; set; } - public ArcFile Archive { get; set; } + public Entry Entry { get; set; } - public bool IsEqual (string path, string name) + public bool IsEqual (IEnumerable path, string name) { - return path.Equals (Path) && name.Equals (Name); + return Path != null && path.SequenceEqual (Path) && name.Equals (Name); } } @@ -167,8 +166,6 @@ namespace GARbro.GUI ActiveViewer = ImageView; return; } - if (vm.IsArchive) - m_current_preview.Archive = m_app.CurrentArchive; if ("image" != entry.Type) LoadPreviewText (m_current_preview); else if (!m_preview_worker.IsBusy) @@ -193,15 +190,7 @@ namespace GARbro.GUI Stream OpenPreviewStream (PreviewFile preview) { - if (null == preview.Archive) - { - string filename = Path.Combine (preview.Path, preview.Name); - return File.OpenRead (filename); - } - else - { - return preview.Archive.OpenSeekableEntry (preview.Entry); - } + return VFS.OpenSeekableStream (preview.Entry); } void LoadPreviewText (PreviewFile preview) diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index 76348aa2..213bc461 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -223,16 +223,15 @@ namespace GARbro.GUI StopWatchDirectoryChanges(); var cvs = this.Resources["ListViewSource"] as CollectionViewSource; cvs.Source = value; - pathLine.Text = value.Path; + pathLine.Text = value.Path.Last(); - if (value.IsArchive) - PushRecentFile (value.Path); + if (value.IsArchive && !value.Path.Skip (2).Any()) + PushRecentFile (value.Path.First()); lv_Sort (SortMode, m_lvSortDirection); - if (!value.IsArchive && !string.IsNullOrEmpty (value.Path)) + if (!value.IsArchive && !string.IsNullOrEmpty (value.Path.First())) { - Directory.SetCurrentDirectory (value.Path); - WatchDirectoryChanges (value.Path); + WatchDirectoryChanges (value.Path.First()); } CurrentDirectory.UpdateLayout(); } @@ -240,16 +239,13 @@ namespace GARbro.GUI DirectoryViewModel GetNewViewModel (string path) { - path = Path.GetFullPath (path); - if (Directory.Exists (path)) - { - return new DirectoryViewModel (path, m_app.GetDirectoryList (path)); - } - else - { + if (!VFS.IsVirtual) + path = Path.GetFullPath (path); + var entry = VFS.FindFile (path); + if (!(entry is SubDirEntry)) SetBusyState(); - return new ArchiveViewModel (path, m_app.GetArchive (path)); - } + VFS.ChDir (entry); + return new DirectoryViewModel (VFS.FullPath, VFS.GetFiles(), VFS.IsVirtual); } private bool m_busy_state = false; @@ -293,7 +289,7 @@ namespace GARbro.GUI catch (Exception X) { PopupError (X.Message, guiStrings.MsgErrorOpening); - return new DirectoryViewModel ("", new Entry[0]); + return new DirectoryViewModel (new string[] { "" }, new Entry[0], false); } } @@ -323,13 +319,14 @@ namespace GARbro.GUI public void ResumeWatchDirectoryChanges () { - m_watcher.EnableRaisingEvents = true; + m_watcher.EnableRaisingEvents = !ViewModel.IsArchive; } private void InvokeRefreshView (object source, FileSystemEventArgs e) { var watcher = source as FileSystemWatcher; - if (watcher.Path == ViewModel.Path) + var vm = ViewModel; + if (!vm.IsArchive && vm.Path.First() == watcher.Path) { watcher.EnableRaisingEvents = false; Dispatcher.Invoke (RefreshView); @@ -632,7 +629,7 @@ namespace GARbro.GUI #region Navigation history implementation - internal string CurrentPath { get { return ViewModel.Path; } } + internal string CurrentPath { get { return ViewModel.Path.First(); } } HistoryStack m_history = new HistoryStack(); @@ -644,12 +641,12 @@ namespace GARbro.GUI public bool SetCurrentPosition (DirectoryPosition pos) { - var vm = TryCreateViewModel (pos.Path); - if (null == vm) - return false; try { - vm.SetPosition (pos); + VFS.FullPath = pos.Path; + var vm = TryCreateViewModel (pos.Path.Last()); + if (null == vm) + return false; ViewModel = vm; if (null != pos.Item) lv_SelectItem (pos.Item); @@ -670,7 +667,7 @@ namespace GARbro.GUI public void ChangePosition (DirectoryPosition new_pos) { var current = GetCurrentPosition(); - if (current.Path != new_pos.Path || current.ArchivePath != new_pos.ArchivePath) + if (!current.Path.SequenceEqual (new_pos.Path)) SaveCurrentPosition(); SetCurrentPosition (new_pos); } @@ -722,8 +719,8 @@ namespace GARbro.GUI var vm = GetNewViewModel (filename); SaveCurrentPosition(); ViewModel = vm; - if (null != m_app.CurrentArchive) - SetStatusText (m_app.CurrentArchive.Description); + if (null != VFS.CurrentArchive) + SetStatusText (VFS.CurrentArchive.Description); lv_SelectItem (0); } catch (OperationCanceledException X) @@ -766,48 +763,37 @@ namespace GARbro.GUI PlayFile (entry.Source); 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); + string old_dir = vm.Path.Last(); + string new_dir = entry.Source.Name; + if (!vm.IsArchive && ".." == new_dir) + new_dir = Path.Combine (old_dir, entry.Name); Trace.WriteLine (new_dir, "OpenDirectoryEntry"); + int old_fs_count = VFS.Count; 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 (string.Format ("{0}: {1}", m_app.CurrentArchive.Description, - Localization.Format ("MsgFiles", m_app.CurrentArchive.Dir.Count()))); + if (VFS.Count > old_fs_count && null != VFS.CurrentArchive) + SetStatusText (string.Format ("{0}: {1}", VFS.CurrentArchive.Description, + Localization.Format ("MsgFiles", VFS.CurrentArchive.Dir.Count()))); else SetStatusText (""); - var old_parent = Directory.GetParent (old_dir); - if (null != old_parent && vm.Path == old_parent.FullName) - { + + if (".." == entry.Name) lv_SelectItem (Path.GetFileName (old_dir)); - } else - { lv_SelectItem (0); - } } + /* private void OpenArchiveEntry (ArchiveViewModel vm, EntryViewModel entry) { if (entry.IsDirectory) @@ -829,14 +815,11 @@ namespace GARbro.GUI } } } + */ Stream OpenEntry (Entry entry) { - var vm = ViewModel; - if (vm.IsArchive) - return m_app.CurrentArchive.OpenEntry (entry); - else - return File.OpenRead (Path.Combine (vm.Path, entry.Name)); + return VFS.OpenStream (entry); } WaveOutEvent m_audio_device; @@ -935,7 +918,7 @@ namespace GARbro.GUI public void RefreshView () { - m_app.ResetCache(); + VFS.Flush(); var pos = GetCurrentPosition(); SetCurrentPosition (pos); } @@ -974,7 +957,7 @@ namespace GARbro.GUI this.IsEnabled = false; try { - m_app.ResetCache(); + VFS.Flush(); ResetPreviewPane(); if (!items.Skip (1).Any()) // items.Count() == 1 { diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index a6a123e1..cc51191d 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -51,5 +51,5 @@ using System.Windows; // 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.1.6.659")] -[assembly: AssemblyFileVersion ("1.1.6.659")] +[assembly: AssemblyVersion ("1.1.8.660")] +[assembly: AssemblyFileVersion ("1.1.8.660")] diff --git a/Strings/guiStrings.Designer.cs b/Strings/guiStrings.Designer.cs index d3b7b4d0..ae8586f9 100644 --- a/Strings/guiStrings.Designer.cs +++ b/Strings/guiStrings.Designer.cs @@ -729,15 +729,6 @@ namespace GARbro.GUI.Strings { } } - /// - /// 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}. /// diff --git a/Strings/guiStrings.resx b/Strings/guiStrings.resx index 279e7917..9ed61ce3 100644 --- a/Strings/guiStrings.resx +++ b/Strings/guiStrings.resx @@ -243,9 +243,6 @@ unable to interpret image format - - file could not be opened as resource archive - Version {0} diff --git a/Strings/guiStrings.ru-RU.resx b/Strings/guiStrings.ru-RU.resx index e616ec0d..2910a931 100644 --- a/Strings/guiStrings.ru-RU.resx +++ b/Strings/guiStrings.ru-RU.resx @@ -237,9 +237,6 @@ не удалось интерпретировать формат изображения - - файл не может быть открыт как архив ресурсов - Версия {0} diff --git a/ViewModel.cs b/ViewModel.cs index 96e3bc51..5e75a0ba 100644 --- a/ViewModel.cs +++ b/ViewModel.cs @@ -41,20 +41,22 @@ namespace GARbro.GUI { public class DirectoryViewModel : ObservableCollection { - public string Path { get; private set; } - public ICollection Source { get; private set; } - public virtual bool IsArchive { get { return false; } } + public IEnumerable Path { get; private set; } + public IEnumerable Source { get; private set; } + public bool IsArchive { get; private set; } - public DirectoryViewModel (string path, ICollection filelist) + public DirectoryViewModel (IEnumerable path, IEnumerable filelist, bool is_archive) { Path = path; Source = filelist; + IsArchive = is_archive; ImportFromSource(); } - protected virtual void ImportFromSource () + protected void ImportFromSource () { - if (!string.IsNullOrEmpty (Path) && null != Directory.GetParent (Path)) + var last_dir = Path.Last(); + if (IsArchive || !string.IsNullOrEmpty (last_dir) && null != Directory.GetParent (last_dir)) { Add (new EntryViewModel (new SubDirEntry (".."), -2)); } @@ -80,6 +82,7 @@ namespace GARbro.GUI } } + /* public class ArchiveViewModel : DirectoryViewModel { public override bool IsArchive { get { return true; } } @@ -212,6 +215,7 @@ namespace GARbro.GUI base.OnCollectionChanged(e); } } + */ public class EntryViewModel : INotifyPropertyChanged { @@ -332,24 +336,18 @@ namespace GARbro.GUI /// public class DirectoryPosition { - public string Path { get; set; } - public string ArchivePath { get; set; } - public string Item { get; set; } + public IEnumerable Path { get; set; } + public string Item { get; set; } public DirectoryPosition (DirectoryViewModel vm, EntryViewModel item) { Path = vm.Path; Item = null != item ? item.Name : null; - if (vm.IsArchive) - ArchivePath = (vm as ArchiveViewModel).SubDir; - else - ArchivePath = ""; } public DirectoryPosition (string filename) { - Path = System.IO.Path.GetDirectoryName (filename); - ArchivePath = ""; + Path = new string[] { System.IO.Path.GetDirectoryName (filename) }; Item = System.IO.Path.GetFileName (filename); } }