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();
}
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());
}
}
}

View File

@ -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 ()

View File

@ -54,7 +54,8 @@ namespace GameRes.Formats
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))
{
@ -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)

View File

@ -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<Entry> list, ResourceOptions options)
public override void Create (Stream output, IEnumerable<Entry> 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<string>();
var dir = new List<Xp3Entry>();
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);
}
}

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>
/// Looks up a localized string similar to File name is too long.
/// </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>
/// Looks up a localized string similar to Nitro+ resource archive.
/// </summary>

View File

@ -156,12 +156,24 @@ predefined encryption scheme.</value>
<data name="LWGDescription" xml:space="preserve">
<value>Liar-soft image archive</value>
</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">
<value>File name is too long</value>
</data>
<data name="MsgIllegalCharacters" xml:space="preserve">
<value>File name contains illegal characters</value>
</data>
<data name="MsgWritingIndex" xml:space="preserve">
<value>Writing index...</value>
</data>
<data name="NPADescription" xml:space="preserve">
<value>Nitro+ resource archive</value>
</data>

View File

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

View File

@ -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
/// <summary>Archive contents.</summary>
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)
{
m_arc = arc;
@ -93,15 +84,15 @@ namespace GameRes
/// Extract all entries from the archive into current directory.
/// <paramref name="callback"/> could be used to observe/control extraction process.
/// </summary>
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;
}

View File

@ -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 <paramref name="file"/> containing entries from the
/// supplied <paramref name="list"/> and applying necessary <paramref name="options"/>.
/// </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");
}
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()
/// </summary>
public bool InputResult { get; set; }
/// <summary>
/// Archive-specific options set by InputWidget.
/// </summary>
public ResourceOptions Options { get; set; }
}
public sealed class FormatCatalog