mirror of
https://github.com/crskycode/GARbro.git
synced 2024-12-23 19:34:15 +08:00
added callback into archive creation method.
This commit is contained in:
parent
c13b9bb12e
commit
08b8e8a46b
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 ()
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
36
ArcFormats/Strings/arcStrings.Designer.cs
generated
36
ArcFormats/Strings/arcStrings.Designer.cs
generated
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user