diff --git a/ArcFormats/ArcINT.cs b/ArcFormats/ArcINT.cs index 58b175df..194c9fd1 100644 --- a/ArcFormats/ArcINT.cs +++ b/ArcFormats/ArcINT.cs @@ -284,11 +284,9 @@ namespace GameRes.Formats return widget.GetKey(); } - public override ResourceOptions GetOptions () + public override object GetAccessWidget () { - return new ResourceOptions { - Widget = new GUI.WidgetINT (Settings.Default.INTEncryption ?? new IntEncryptionInfo()) - }; + return new GUI.WidgetINT (Settings.Default.INTEncryption ?? new IntEncryptionInfo()); } } } diff --git a/ArcFormats/ArcNPA.cs b/ArcFormats/ArcNPA.cs index d01c4020..17724855 100644 --- a/ArcFormats/ArcNPA.cs +++ b/ArcFormats/ArcNPA.cs @@ -273,11 +273,9 @@ namespace GameRes.Formats return table; } - public override ResourceOptions GetOptions () + public override object GetAccessWidget () { - return new ResourceOptions { - Widget = new GUI.WidgetNPA() - }; + return new GUI.WidgetNPA(); } NpaTitleId QueryGameEncryption () diff --git a/ArcFormats/ArcXFL.cs b/ArcFormats/ArcXFL.cs index 4757d7b4..d2cf1648 100644 --- a/ArcFormats/ArcXFL.cs +++ b/ArcFormats/ArcXFL.cs @@ -54,7 +54,8 @@ namespace GameRes.Formats return new ArcFile (file, this, dir); } - public override void Create (Stream output, IEnumerable list, ResourceOptions options) + public override void Create (Stream output, IEnumerable list, ResourceOptions options, + EntryCallback callback) { using (var writer = new BinaryWriter (output, Encoding.ASCII, true)) { @@ -68,6 +69,10 @@ namespace GameRes.Formats encoding.EncoderFallback = EncoderFallback.ExceptionFallback; byte[] name_buf = new byte[32]; + int callback_count = 0; + + if (null != callback) + callback (callback_count++, null, "Writing index..."); // first, write names only foreach (var entry in list) @@ -95,6 +100,9 @@ namespace GameRes.Formats uint current_offset = 0; foreach (var entry in list) { + if (null != callback) + callback (callback_count++, entry, "Adding file"); + entry.Offset = current_offset; using (var input = File.Open (entry.Name, FileMode.Open, FileAccess.Read)) { @@ -107,6 +115,9 @@ namespace GameRes.Formats } } + if (null != callback) + callback (callback_count++, null, "Updating index..."); + // at last, go back to directory and write offset/sizes long dir_offset = 12+32; foreach (var entry in list) diff --git a/ArcFormats/ArcXP3.cs b/ArcFormats/ArcXP3.cs index b706b42b..236082e2 100644 --- a/ArcFormats/ArcXP3.cs +++ b/ArcFormats/ArcXP3.cs @@ -39,6 +39,15 @@ namespace GameRes.Formats.KiriKiri public uint Hash { get; set; } } + public class Xp3Options : ResourceOptions + { + public int Version { get; set; } + public ICrypt Scheme { get; set; } + public bool CompressIndex { get; set; } + public bool CompressContents { get; set; } + public bool RetainDirs { get; set; } + } + // Archive version 1: encrypt file first, then calculate checksum // version 2: calculate checksum, then encrypt @@ -229,16 +238,41 @@ NextEntry: return new Xp3Stream (arc.File, xp3_entry); } - public override ResourceOptions GetOptions () + public override ResourceOptions GetDefaultOptions () { - return new ResourceOptions { - Widget = new GUI.CreateXP3Widget() + return new Xp3Options { + Version = Settings.Default.XP3Version, + Scheme = NoCryptAlgorithm, + CompressIndex = Settings.Default.XP3CompressHeader, + CompressContents = Settings.Default.XP3CompressContents, + RetainDirs = Settings.Default.XP3RetainStructure, }; } + public override ResourceOptions GetOptions (object widget) + { + var options = this.GetDefaultOptions() as Xp3Options; + var w = widget as GUI.CreateXP3Widget; + if (null != w) + { + options.Scheme = w.EncryptionWidget.GetScheme(); + } + return options; + } + + public override object GetCreationWidget () + { + return new GUI.CreateXP3Widget(); + } + + public override object GetAccessWidget () + { + return new GUI.WidgetXP3(); + } + ICrypt QueryCryptAlgorithm () { - var widget = new GUI.WidgetXP3(); + var widget = this.GetAccessWidget() as GUI.WidgetXP3; var args = new ParametersRequestEventArgs { Notice = arcStrings.ArcEncryptedNotice, @@ -280,25 +314,24 @@ NextEntry: (byte)'X', (byte)'P', (byte)'3', 0x0d, 0x0a, 0x20, 0x0a, 0x1a, 0x8b, 0x67, 0x01 }; - public override void Create (Stream output, IEnumerable list, ResourceOptions options) + public override void Create (Stream output, IEnumerable list, ResourceOptions options, + EntryCallback callback) { - int version = Settings.Default.XP3Version; - ICrypt scheme = NoCryptAlgorithm; - bool compress_header = Settings.Default.XP3CompressHeader; - bool compress_contents = Settings.Default.XP3CompressContents; - bool retain_dirs = Settings.Default.XP3RetainStructure; + var xp3_options = options as Xp3Options; + if (null == xp3_options) + xp3_options = this.GetDefaultOptions() as Xp3Options; + + ICrypt scheme = xp3_options.Scheme; + bool compress_index = xp3_options.CompressIndex; + bool compress_contents = xp3_options.CompressContents; + bool retain_dirs = xp3_options.RetainDirs; - var widget = options.Widget as GUI.CreateXP3Widget; - if (null != widget) - { - scheme = widget.EncryptionWidget.GetScheme(); - } bool use_encryption = scheme != NoCryptAlgorithm; using (var writer = new BinaryWriter (output, Encoding.ASCII, true)) { writer.Write (s_xp3_header); - if (2 == version) + if (2 == xp3_options.Version) { writer.Write ((long)0x17); writer.Write ((int)1); @@ -308,11 +341,15 @@ NextEntry: long index_pos_offset = writer.BaseStream.Position; writer.BaseStream.Seek (8, SeekOrigin.Current); + int callback_count = 0; var used_names = new HashSet(); var dir = new List(); long current_offset = writer.BaseStream.Position; foreach (var entry in list) { + if (null != callback) + callback (callback_count++, entry, arcStrings.MsgAddingFile); + string name = entry.Name; if (!retain_dirs) name = Path.GetFileName (name); @@ -330,10 +367,13 @@ NextEntry: Cipher = scheme, }; bool compress = compress_contents && ShouldCompressFile (entry); - if (!use_encryption) - RawFileCopy (entry.Name, xp3entry, output, compress); - else - EncryptedFileCopy (entry.Name, xp3entry, output, compress); + using (var file = File.Open (name, FileMode.Open, FileAccess.Read)) + { + if (!use_encryption || 0 == file.Length) + RawFileCopy (file, xp3entry, output, compress); + else + EncryptedFileCopy (file, xp3entry, output, compress); + } dir.Add (xp3entry); } @@ -345,6 +385,9 @@ NextEntry: using (var header = new BinaryWriter (new MemoryStream (dir.Count*0x58), Encoding.Unicode)) { + if (null != callback) + callback (callback_count++, null, arcStrings.MsgWritingIndex); + long dir_pos = 0; foreach (var entry in dir) { @@ -381,10 +424,13 @@ NextEntry: } header.BaseStream.Position = 0; - writer.Write ((byte)(compress_header ? 1 : 0)); + writer.Write ((byte)(compress_index ? 1 : 0)); long unpacked_dir_size = header.BaseStream.Length; - if (compress_header) + if (compress_index) { + if (null != callback) + callback (callback_count++, null, arcStrings.MsgCompressingIndex); + long packed_dir_size_pos = writer.BaseStream.Position; writer.Write ((long)0); writer.Write (unpacked_dir_size); @@ -407,48 +453,45 @@ NextEntry: output.Seek (0, SeekOrigin.End); } - void RawFileCopy (string name, Xp3Entry xp3entry, Stream output, bool compress) + void RawFileCopy (FileStream file, Xp3Entry xp3entry, Stream output, bool compress) { - using (var file = File.Open (name, FileMode.Open, FileAccess.Read)) - { - if (file.Length > uint.MaxValue) - throw new FileSizeException(); + if (file.Length > uint.MaxValue) + throw new FileSizeException(); - uint unpacked_size = (uint)file.Length; - xp3entry.UnpackedSize = (uint)unpacked_size; - xp3entry.Size = (uint)unpacked_size; - var segment = new Xp3Segment { - IsCompressed = compress, - Offset = output.Position, - Size = unpacked_size, - PackedSize = unpacked_size - }; - if (compress) + uint unpacked_size = (uint)file.Length; + xp3entry.UnpackedSize = (uint)unpacked_size; + xp3entry.Size = (uint)unpacked_size; + compress = compress && unpacked_size > 0; + var segment = new Xp3Segment { + IsCompressed = compress, + Offset = output.Position, + Size = unpacked_size, + PackedSize = unpacked_size + }; + if (compress) + { + using (var zstream = new ZLibStream (output, CompressionMode.Compress, true)) { - using (var zstream = new ZLibStream (output, CompressionMode.Compress, true)) - { - xp3entry.Hash = CheckedCopy (file, zstream); - zstream.Flush(); - segment.PackedSize = (uint)zstream.TotalOut; - } + xp3entry.Hash = CheckedCopy (file, zstream); + zstream.Flush(); + segment.PackedSize = (uint)zstream.TotalOut; } - else - { - xp3entry.Hash = CheckedCopy (file, output); - } - xp3entry.Segments.Add (segment); } + else + { + xp3entry.Hash = CheckedCopy (file, output); + } + xp3entry.Segments.Add (segment); } - void EncryptedFileCopy (string name, Xp3Entry xp3entry, Stream output, bool compress) + void EncryptedFileCopy (FileStream file, Xp3Entry xp3entry, Stream output, bool compress) { - var file = File.Open (name, FileMode.Open, FileAccess.Read); - using (var map = MemoryMappedFile.CreateFromFile (file, null, 0, - MemoryMappedFileAccess.Read, null, HandleInheritability.None, false)) - { - if (file.Length > int.MaxValue) - throw new FileSizeException(); + if (file.Length > int.MaxValue) + throw new FileSizeException(); + using (var map = MemoryMappedFile.CreateFromFile (file, null, 0, + MemoryMappedFileAccess.Read, null, HandleInheritability.None, true)) + { uint unpacked_size = (uint)file.Length; xp3entry.UnpackedSize = (uint)unpacked_size; xp3entry.Size = (uint)unpacked_size; @@ -671,7 +714,7 @@ NextEntry: public virtual void Encrypt (Xp3Entry entry, long offset, byte[] values, int pos, int count) { - throw new NotImplementedException ("Encryption method not implemented"); + throw new NotImplementedException (arcStrings.MsgEncNotImplemented); } } diff --git a/ArcFormats/Strings/arcStrings.Designer.cs b/ArcFormats/Strings/arcStrings.Designer.cs index a05926c3..fd53eac8 100644 --- a/ArcFormats/Strings/arcStrings.Designer.cs +++ b/ArcFormats/Strings/arcStrings.Designer.cs @@ -171,6 +171,33 @@ namespace GameRes.Formats.Strings { } } + /// + /// Looks up a localized string similar to Adding file. + /// + public static string MsgAddingFile { + get { + return ResourceManager.GetString("MsgAddingFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Compressing index.... + /// + public static string MsgCompressingIndex { + get { + return ResourceManager.GetString("MsgCompressingIndex", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Encryption method not implemented. + /// + public static string MsgEncNotImplemented { + get { + return ResourceManager.GetString("MsgEncNotImplemented", resourceCulture); + } + } + /// /// Looks up a localized string similar to File name is too long. /// @@ -189,6 +216,15 @@ namespace GameRes.Formats.Strings { } } + /// + /// Looks up a localized string similar to Writing index.... + /// + public static string MsgWritingIndex { + get { + return ResourceManager.GetString("MsgWritingIndex", resourceCulture); + } + } + /// /// Looks up a localized string similar to Nitro+ resource archive. /// diff --git a/ArcFormats/Strings/arcStrings.resx b/ArcFormats/Strings/arcStrings.resx index 3cb60a76..5c9c23af 100644 --- a/ArcFormats/Strings/arcStrings.resx +++ b/ArcFormats/Strings/arcStrings.resx @@ -156,12 +156,24 @@ predefined encryption scheme. Liar-soft image archive + + Adding file + + + Compressing index... + + + Encryption method not implemented + File name is too long File name contains illegal characters + + Writing index... + Nitro+ resource archive diff --git a/ArcFormats/Strings/arcStrings.ru-RU.resx b/ArcFormats/Strings/arcStrings.ru-RU.resx index 7e458522..ec5f4ac0 100644 --- a/ArcFormats/Strings/arcStrings.ru-RU.resx +++ b/ArcFormats/Strings/arcStrings.ru-RU.resx @@ -141,12 +141,24 @@ Введите ключ шифрования или выберите один из предопределённых вариантов. + + Добавляется файл + + + Сжимается оглавление... + + + Метод шифрования не реализован + Слишком длинное имя файла Имя файла содержит недопустимые символы + + Записывается оглавление... + Сжать содержимое diff --git a/GameRes/ArcFile.cs b/GameRes/ArcFile.cs index ea81255a..355d0eb3 100644 --- a/GameRes/ArcFile.cs +++ b/GameRes/ArcFile.cs @@ -11,13 +11,6 @@ using System.Diagnostics; namespace GameRes { - public enum ExtractAction - { - Abort, - Skip, - Continue, - } - public class ArcFile : IDisposable { private ArcView m_arc; @@ -36,8 +29,6 @@ namespace GameRes /// Archive contents. public ICollection Dir { get { return m_dir; } } - public delegate ExtractAction ExtractCallback (int num, Entry entry); - public ArcFile (ArcView arc, ArchiveFormat impl, ICollection dir) { m_arc = arc; @@ -93,15 +84,15 @@ namespace GameRes /// Extract all entries from the archive into current directory. /// could be used to observe/control extraction process. /// - public void ExtractFiles (ExtractCallback callback) + public void ExtractFiles (EntryCallback callback) { int i = 0; foreach (var entry in Dir.OrderBy (e => e.Offset)) { - var action = callback (i, entry); - if (ExtractAction.Abort == action) + var action = callback (i, entry, null); + if (ArchiveOperation.Abort == action) break; - if (ExtractAction.Skip != action) + if (ArchiveOperation.Skip != action) Extract (entry); ++i; } diff --git a/GameRes/GameRes.cs b/GameRes/GameRes.cs index 1c2f40c3..2aa0e4fc 100644 --- a/GameRes/GameRes.cs +++ b/GameRes/GameRes.cs @@ -96,9 +96,17 @@ namespace GameRes public class ResourceOptions { - public object Widget { get; set; } } + public enum ArchiveOperation + { + Abort, + Skip, + Continue, + } + + public delegate ArchiveOperation EntryCallback (int num, Entry entry, string description); + public abstract class ArchiveFormat : IResource { public override string Type { get { return "archive"; } } @@ -149,12 +157,28 @@ namespace GameRes /// Create resource wihin stream containing entries from the /// supplied and applying necessary . /// - public virtual void Create (Stream file, IEnumerable list, ResourceOptions options = null) + public virtual void Create (Stream file, IEnumerable list, ResourceOptions options = null, + EntryCallback callback = null) { throw new NotImplementedException ("ArchiveFormat.Create is not implemented"); } - public virtual ResourceOptions GetOptions () + public virtual ResourceOptions GetDefaultOptions () + { + return null; + } + + public virtual ResourceOptions GetOptions (object widget) + { + return null; + } + + public virtual object GetCreationWidget () + { + return null; + } + + public virtual object GetAccessWidget () { return null; } @@ -178,6 +202,11 @@ namespace GameRes /// Return value from ShowDialog() /// public bool InputResult { get; set; } + + /// + /// Archive-specific options set by InputWidget. + /// + public ResourceOptions Options { get; set; } } public sealed class FormatCatalog