added callback into archive creation method.

This commit is contained in:
morkt 2014-07-27 03:37:51 +04:00
parent c13b9bb12e
commit 08b8e8a46b
9 changed files with 211 additions and 81 deletions

View File

@ -284,11 +284,9 @@ namespace GameRes.Formats
return widget.GetKey(); return widget.GetKey();
} }
public override ResourceOptions GetOptions () public override object GetAccessWidget ()
{ {
return new ResourceOptions { return new GUI.WidgetINT (Settings.Default.INTEncryption ?? new IntEncryptionInfo());
Widget = new GUI.WidgetINT (Settings.Default.INTEncryption ?? new IntEncryptionInfo())
};
} }
} }
} }

View File

@ -273,11 +273,9 @@ namespace GameRes.Formats
return table; return table;
} }
public override ResourceOptions GetOptions () public override object GetAccessWidget ()
{ {
return new ResourceOptions { return new GUI.WidgetNPA();
Widget = new GUI.WidgetNPA()
};
} }
NpaTitleId QueryGameEncryption () NpaTitleId QueryGameEncryption ()

View File

@ -54,7 +54,8 @@ namespace GameRes.Formats
return new ArcFile (file, this, dir); return new ArcFile (file, this, dir);
} }
public override void Create (Stream output, IEnumerable<Entry> list, ResourceOptions options) public override void Create (Stream output, IEnumerable<Entry> list, ResourceOptions options,
EntryCallback callback)
{ {
using (var writer = new BinaryWriter (output, Encoding.ASCII, true)) using (var writer = new BinaryWriter (output, Encoding.ASCII, true))
{ {
@ -68,6 +69,10 @@ namespace GameRes.Formats
encoding.EncoderFallback = EncoderFallback.ExceptionFallback; encoding.EncoderFallback = EncoderFallback.ExceptionFallback;
byte[] name_buf = new byte[32]; byte[] name_buf = new byte[32];
int callback_count = 0;
if (null != callback)
callback (callback_count++, null, "Writing index...");
// first, write names only // first, write names only
foreach (var entry in list) foreach (var entry in list)
@ -95,6 +100,9 @@ namespace GameRes.Formats
uint current_offset = 0; uint current_offset = 0;
foreach (var entry in list) foreach (var entry in list)
{ {
if (null != callback)
callback (callback_count++, entry, "Adding file");
entry.Offset = current_offset; entry.Offset = current_offset;
using (var input = File.Open (entry.Name, FileMode.Open, FileAccess.Read)) 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 // at last, go back to directory and write offset/sizes
long dir_offset = 12+32; long dir_offset = 12+32;
foreach (var entry in list) foreach (var entry in list)

View File

@ -39,6 +39,15 @@ namespace GameRes.Formats.KiriKiri
public uint Hash { get; set; } 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 // Archive version 1: encrypt file first, then calculate checksum
// version 2: calculate checksum, then encrypt // version 2: calculate checksum, then encrypt
@ -229,16 +238,41 @@ NextEntry:
return new Xp3Stream (arc.File, xp3_entry); return new Xp3Stream (arc.File, xp3_entry);
} }
public override ResourceOptions GetOptions () public override ResourceOptions GetDefaultOptions ()
{ {
return new ResourceOptions { return new Xp3Options {
Widget = new GUI.CreateXP3Widget() 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 () ICrypt QueryCryptAlgorithm ()
{ {
var widget = new GUI.WidgetXP3(); var widget = this.GetAccessWidget() as GUI.WidgetXP3;
var args = new ParametersRequestEventArgs var args = new ParametersRequestEventArgs
{ {
Notice = arcStrings.ArcEncryptedNotice, Notice = arcStrings.ArcEncryptedNotice,
@ -280,25 +314,24 @@ NextEntry:
(byte)'X', (byte)'P', (byte)'3', 0x0d, 0x0a, 0x20, 0x0a, 0x1a, 0x8b, 0x67, 0x01 (byte)'X', (byte)'P', (byte)'3', 0x0d, 0x0a, 0x20, 0x0a, 0x1a, 0x8b, 0x67, 0x01
}; };
public override void Create (Stream output, IEnumerable<Entry> list, ResourceOptions options) public override void Create (Stream output, IEnumerable<Entry> list, ResourceOptions options,
EntryCallback callback)
{ {
int version = Settings.Default.XP3Version; var xp3_options = options as Xp3Options;
ICrypt scheme = NoCryptAlgorithm; if (null == xp3_options)
bool compress_header = Settings.Default.XP3CompressHeader; xp3_options = this.GetDefaultOptions() as Xp3Options;
bool compress_contents = Settings.Default.XP3CompressContents;
bool retain_dirs = Settings.Default.XP3RetainStructure; 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; bool use_encryption = scheme != NoCryptAlgorithm;
using (var writer = new BinaryWriter (output, Encoding.ASCII, true)) using (var writer = new BinaryWriter (output, Encoding.ASCII, true))
{ {
writer.Write (s_xp3_header); writer.Write (s_xp3_header);
if (2 == version) if (2 == xp3_options.Version)
{ {
writer.Write ((long)0x17); writer.Write ((long)0x17);
writer.Write ((int)1); writer.Write ((int)1);
@ -308,11 +341,15 @@ NextEntry:
long index_pos_offset = writer.BaseStream.Position; long index_pos_offset = writer.BaseStream.Position;
writer.BaseStream.Seek (8, SeekOrigin.Current); writer.BaseStream.Seek (8, SeekOrigin.Current);
int callback_count = 0;
var used_names = new HashSet<string>(); var used_names = new HashSet<string>();
var dir = new List<Xp3Entry>(); var dir = new List<Xp3Entry>();
long current_offset = writer.BaseStream.Position; long current_offset = writer.BaseStream.Position;
foreach (var entry in list) foreach (var entry in list)
{ {
if (null != callback)
callback (callback_count++, entry, arcStrings.MsgAddingFile);
string name = entry.Name; string name = entry.Name;
if (!retain_dirs) if (!retain_dirs)
name = Path.GetFileName (name); name = Path.GetFileName (name);
@ -330,10 +367,13 @@ NextEntry:
Cipher = scheme, Cipher = scheme,
}; };
bool compress = compress_contents && ShouldCompressFile (entry); bool compress = compress_contents && ShouldCompressFile (entry);
if (!use_encryption) using (var file = File.Open (name, FileMode.Open, FileAccess.Read))
RawFileCopy (entry.Name, xp3entry, output, compress); {
else if (!use_encryption || 0 == file.Length)
EncryptedFileCopy (entry.Name, xp3entry, output, compress); RawFileCopy (file, xp3entry, output, compress);
else
EncryptedFileCopy (file, xp3entry, output, compress);
}
dir.Add (xp3entry); dir.Add (xp3entry);
} }
@ -345,6 +385,9 @@ NextEntry:
using (var header = new BinaryWriter (new MemoryStream (dir.Count*0x58), Encoding.Unicode)) 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; long dir_pos = 0;
foreach (var entry in dir) foreach (var entry in dir)
{ {
@ -381,10 +424,13 @@ NextEntry:
} }
header.BaseStream.Position = 0; 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; 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; long packed_dir_size_pos = writer.BaseStream.Position;
writer.Write ((long)0); writer.Write ((long)0);
writer.Write (unpacked_dir_size); writer.Write (unpacked_dir_size);
@ -407,48 +453,45 @@ NextEntry:
output.Seek (0, SeekOrigin.End); 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; uint unpacked_size = (uint)file.Length;
xp3entry.UnpackedSize = (uint)unpacked_size; xp3entry.UnpackedSize = (uint)unpacked_size;
xp3entry.Size = (uint)unpacked_size; xp3entry.Size = (uint)unpacked_size;
var segment = new Xp3Segment { compress = compress && unpacked_size > 0;
IsCompressed = compress, var segment = new Xp3Segment {
Offset = output.Position, IsCompressed = compress,
Size = unpacked_size, Offset = output.Position,
PackedSize = unpacked_size Size = unpacked_size,
}; PackedSize = unpacked_size
if (compress) };
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();
xp3entry.Hash = CheckedCopy (file, zstream); segment.PackedSize = (uint)zstream.TotalOut;
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); if (file.Length > int.MaxValue)
using (var map = MemoryMappedFile.CreateFromFile (file, null, 0, throw new FileSizeException();
MemoryMappedFileAccess.Read, null, HandleInheritability.None, false))
{
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; uint unpacked_size = (uint)file.Length;
xp3entry.UnpackedSize = (uint)unpacked_size; xp3entry.UnpackedSize = (uint)unpacked_size;
xp3entry.Size = (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) 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);
} }
} }

View File

@ -171,6 +171,33 @@ namespace GameRes.Formats.Strings {
} }
} }
/// <summary>
/// Looks up a localized string similar to Adding file.
/// </summary>
public static string MsgAddingFile {
get {
return ResourceManager.GetString("MsgAddingFile", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Compressing index....
/// </summary>
public static string MsgCompressingIndex {
get {
return ResourceManager.GetString("MsgCompressingIndex", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Encryption method not implemented.
/// </summary>
public static string MsgEncNotImplemented {
get {
return ResourceManager.GetString("MsgEncNotImplemented", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to File name is too long. /// Looks up a localized string similar to File name is too long.
/// </summary> /// </summary>
@ -189,6 +216,15 @@ namespace GameRes.Formats.Strings {
} }
} }
/// <summary>
/// Looks up a localized string similar to Writing index....
/// </summary>
public static string MsgWritingIndex {
get {
return ResourceManager.GetString("MsgWritingIndex", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Nitro+ resource archive. /// Looks up a localized string similar to Nitro+ resource archive.
/// </summary> /// </summary>

View File

@ -156,12 +156,24 @@ predefined encryption scheme.</value>
<data name="LWGDescription" xml:space="preserve"> <data name="LWGDescription" xml:space="preserve">
<value>Liar-soft image archive</value> <value>Liar-soft image archive</value>
</data> </data>
<data name="MsgAddingFile" xml:space="preserve">
<value>Adding file</value>
</data>
<data name="MsgCompressingIndex" xml:space="preserve">
<value>Compressing index...</value>
</data>
<data name="MsgEncNotImplemented" xml:space="preserve">
<value>Encryption method not implemented</value>
</data>
<data name="MsgFileNameTooLong" xml:space="preserve"> <data name="MsgFileNameTooLong" xml:space="preserve">
<value>File name is too long</value> <value>File name is too long</value>
</data> </data>
<data name="MsgIllegalCharacters" xml:space="preserve"> <data name="MsgIllegalCharacters" xml:space="preserve">
<value>File name contains illegal characters</value> <value>File name contains illegal characters</value>
</data> </data>
<data name="MsgWritingIndex" xml:space="preserve">
<value>Writing index...</value>
</data>
<data name="NPADescription" xml:space="preserve"> <data name="NPADescription" xml:space="preserve">
<value>Nitro+ resource archive</value> <value>Nitro+ resource archive</value>
</data> </data>

View File

@ -141,12 +141,24 @@
Введите ключ шифрования или выберите Введите ключ шифрования или выберите
один из предопределённых вариантов.</value> один из предопределённых вариантов.</value>
</data> </data>
<data name="MsgAddingFile" xml:space="preserve">
<value>Добавляется файл</value>
</data>
<data name="MsgCompressingIndex" xml:space="preserve">
<value>Сжимается оглавление...</value>
</data>
<data name="MsgEncNotImplemented" xml:space="preserve">
<value>Метод шифрования не реализован</value>
</data>
<data name="MsgFileNameTooLong" xml:space="preserve"> <data name="MsgFileNameTooLong" xml:space="preserve">
<value>Слишком длинное имя файла</value> <value>Слишком длинное имя файла</value>
</data> </data>
<data name="MsgIllegalCharacters" xml:space="preserve"> <data name="MsgIllegalCharacters" xml:space="preserve">
<value>Имя файла содержит недопустимые символы</value> <value>Имя файла содержит недопустимые символы</value>
</data> </data>
<data name="MsgWritingIndex" xml:space="preserve">
<value>Записывается оглавление...</value>
</data>
<data name="XP3CompressContents" xml:space="preserve"> <data name="XP3CompressContents" xml:space="preserve">
<value>Сжать содержимое</value> <value>Сжать содержимое</value>
</data> </data>

View File

@ -11,13 +11,6 @@ using System.Diagnostics;
namespace GameRes namespace GameRes
{ {
public enum ExtractAction
{
Abort,
Skip,
Continue,
}
public class ArcFile : IDisposable public class ArcFile : IDisposable
{ {
private ArcView m_arc; private ArcView m_arc;
@ -36,8 +29,6 @@ namespace GameRes
/// <summary>Archive contents.</summary> /// <summary>Archive contents.</summary>
public ICollection<Entry> Dir { get { return m_dir; } } public ICollection<Entry> Dir { get { return m_dir; } }
public delegate ExtractAction ExtractCallback (int num, Entry entry);
public ArcFile (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir) public ArcFile (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir)
{ {
m_arc = arc; m_arc = arc;
@ -93,15 +84,15 @@ namespace GameRes
/// Extract all entries from the archive into current directory. /// Extract all entries from the archive into current directory.
/// <paramref name="callback"/> could be used to observe/control extraction process. /// <paramref name="callback"/> could be used to observe/control extraction process.
/// </summary> /// </summary>
public void ExtractFiles (ExtractCallback callback) public void ExtractFiles (EntryCallback callback)
{ {
int i = 0; int i = 0;
foreach (var entry in Dir.OrderBy (e => e.Offset)) foreach (var entry in Dir.OrderBy (e => e.Offset))
{ {
var action = callback (i, entry); var action = callback (i, entry, null);
if (ExtractAction.Abort == action) if (ArchiveOperation.Abort == action)
break; break;
if (ExtractAction.Skip != action) if (ArchiveOperation.Skip != action)
Extract (entry); Extract (entry);
++i; ++i;
} }

View File

@ -96,9 +96,17 @@ namespace GameRes
public class ResourceOptions 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 abstract class ArchiveFormat : IResource
{ {
public override string Type { get { return "archive"; } } public override string Type { get { return "archive"; } }
@ -149,12 +157,28 @@ namespace GameRes
/// Create resource wihin stream <paramref name="file"/> containing entries from the /// Create resource wihin stream <paramref name="file"/> containing entries from the
/// supplied <paramref name="list"/> and applying necessary <paramref name="options"/>. /// supplied <paramref name="list"/> and applying necessary <paramref name="options"/>.
/// </summary> /// </summary>
public virtual void Create (Stream file, IEnumerable<Entry> list, ResourceOptions options = null) public virtual void Create (Stream file, IEnumerable<Entry> list, ResourceOptions options = null,
EntryCallback callback = null)
{ {
throw new NotImplementedException ("ArchiveFormat.Create is not implemented"); 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; return null;
} }
@ -178,6 +202,11 @@ namespace GameRes
/// Return value from ShowDialog() /// Return value from ShowDialog()
/// </summary> /// </summary>
public bool InputResult { get; set; } public bool InputResult { get; set; }
/// <summary>
/// Archive-specific options set by InputWidget.
/// </summary>
public ResourceOptions Options { get; set; }
} }
public sealed class FormatCatalog public sealed class FormatCatalog