implemented AMI archive creation.

This commit is contained in:
morkt 2014-07-29 19:05:58 +04:00
parent cb8dbb229c
commit 6005016a07
4 changed files with 153 additions and 0 deletions

View File

@ -26,12 +26,15 @@
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.Composition; using System.ComponentModel.Composition;
using System.Globalization;
using System.Windows; using System.Windows;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using ZLibNet; using ZLibNet;
using GameRes.Formats.Strings;
namespace GameRes.Formats namespace GameRes.Formats
{ {
@ -62,6 +65,7 @@ namespace GameRes.Formats
public override string Description { get { return Strings.arcStrings.AMIDescription; } } public override string Description { get { return Strings.arcStrings.AMIDescription; } }
public override uint Signature { get { return 0x00494d41; } } public override uint Signature { get { return 0x00494d41; } }
public override bool IsHierarchic { get { return false; } } public override bool IsHierarchic { get { return false; } }
public override bool CanCreate { get { return true; } }
public AmiOpener () public AmiOpener ()
{ {
@ -124,6 +128,125 @@ namespace GameRes.Formats
else else
return new ZLibStream (input, CompressionMode.Decompress); return new ZLibStream (input, CompressionMode.Decompress);
} }
public override void Create (Stream output, IEnumerable<Entry> list, ResourceOptions options,
EntryCallback callback)
{
IDictionary<uint, PackedEntry> file_table = BuildFileTable (list);
uint file_count = (uint)file_table.Count;
if (0 == file_count)
throw new InvalidFormatException (arcStrings.AMINoFiles);
if (null != callback)
callback ((int)file_count+1, null, null);
int callback_count = 0;
long start_offset = output.Position;
uint data_offset = file_count * 16 + 16;
output.Seek (data_offset, SeekOrigin.Current);
foreach (var entry in file_table)
{
if (null != callback)
callback (callback_count++, entry.Value, arcStrings.MsgAddingFile);
long current_offset = output.Position;
if (current_offset > uint.MaxValue)
throw new FileSizeException();
entry.Value.Offset = (uint)current_offset;
entry.Value.Size = WriteAmiEntry (entry.Value, output);
}
if (null != callback)
callback (callback_count++, null, arcStrings.MsgWritingIndex);
output.Position = start_offset;
using (var header = new BinaryWriter (output, Encoding.ASCII, true))
{
header.Write (Signature);
header.Write (file_count);
header.Write (data_offset);
header.Write ((uint)0);
foreach (var entry in file_table)
{
header.Write (entry.Key);
header.Write ((uint)entry.Value.Offset);
header.Write ((uint)entry.Value.UnpackedSize);
header.Write ((uint)entry.Value.Size);
}
}
}
IDictionary<uint, PackedEntry> BuildFileTable (IEnumerable<Entry> list)
{
var table = new SortedDictionary<uint, PackedEntry>();
foreach (var entry in list)
{
string ext = Path.GetExtension (entry.Name).ToLower();
if (entry.Type != "image" && ext != ".scr")
continue;
uint id;
if (!uint.TryParse (Path.GetFileNameWithoutExtension (entry.Name), NumberStyles.HexNumber,
CultureInfo.InvariantCulture, out id))
continue;
PackedEntry existing;
if (table.TryGetValue (id, out existing))
{
var file_new = new FileInfo (entry.Name);
var file_old = new FileInfo (existing.Name);
if (file_new.LastWriteTime <= file_old.LastWriteTime)
continue;
}
table[id] = new PackedEntry
{
Name = entry.Name,
Type = entry.Type
};
}
return table;
}
uint WriteAmiEntry (PackedEntry entry, Stream output)
{
uint packed_size = 0;
using (var input = File.OpenRead (entry.Name))
{
long file_size = input.Length;
if (file_size > uint.MaxValue)
throw new FileSizeException();
entry.UnpackedSize = (uint)file_size;
if ("image" == entry.Type)
{
packed_size = WriteImageEntry (entry, input, output);
}
else
{
input.CopyTo (output);
}
}
return packed_size;
}
uint WriteImageEntry (PackedEntry entry, Stream input, Stream output)
{
var grp = FormatCatalog.Instance.LookupTag<GrpFormat> ("GRP").FirstOrDefault();
if (null == grp) // probably never happens
throw new FileFormatException ("GRP image encoder not available");
bool is_grp = grp.Signature == FormatCatalog.ReadSignature (input);
input.Position = 0;
using (var zstream = new ZLibStream (output, CompressionMode.Compress, true))
{
if (is_grp)
{
input.CopyTo (zstream);
}
else
{
var image = ImageFormat.Read (input);
if (null == image)
throw new InvalidFormatException (string.Format (arcStrings.MsgInvalidImageFormat, entry.Name));
grp.Write (zstream, image);
entry.UnpackedSize = (uint)zstream.TotalIn;
}
zstream.Flush();
return (uint)zstream.TotalOut;
}
}
} }
[Export(typeof(ImageFormat))] [Export(typeof(ImageFormat))]

View File

@ -69,6 +69,15 @@ namespace GameRes.Formats.Strings {
} }
} }
/// <summary>
/// Looks up a localized string similar to No files suitable for AMI archive found..
/// </summary>
public static string AMINoFiles {
get {
return ResourceManager.GetString("AMINoFiles", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Archive content is encrypted. /// Looks up a localized string similar to Archive content is encrypted.
///Choose appropriate encryption scheme.. ///Choose appropriate encryption scheme..
@ -216,6 +225,15 @@ namespace GameRes.Formats.Strings {
} }
} }
/// <summary>
/// Looks up a localized string similar to {0}: image format not recognized..
/// </summary>
public static string MsgInvalidImageFormat {
get {
return ResourceManager.GetString("MsgInvalidImageFormat", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Updating index.... /// Looks up a localized string similar to Updating index....
/// </summary> /// </summary>

View File

@ -120,6 +120,9 @@
<data name="AMIDescription" xml:space="preserve"> <data name="AMIDescription" xml:space="preserve">
<value>Amaterasu Translations Muv-Luv archive</value> <value>Amaterasu Translations Muv-Luv archive</value>
</data> </data>
<data name="AMINoFiles" xml:space="preserve">
<value>No files suitable for AMI archive found.</value>
</data>
<data name="ArcEncryptedNotice" xml:space="preserve"> <data name="ArcEncryptedNotice" xml:space="preserve">
<value>Archive content is encrypted. <value>Archive content is encrypted.
Choose appropriate encryption scheme.</value> Choose appropriate encryption scheme.</value>
@ -171,6 +174,9 @@ predefined encryption scheme.</value>
<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="MsgInvalidImageFormat" xml:space="preserve">
<value>{0}: image format not recognized.</value>
</data>
<data name="MsgUpdatingIndex" xml:space="preserve"> <data name="MsgUpdatingIndex" xml:space="preserve">
<value>Updating index...</value> <value>Updating index...</value>
</data> </data>

View File

@ -117,6 +117,9 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="AMINoFiles" xml:space="preserve">
<value>Не найдено файлов, подходящих для архива AMI.</value>
</data>
<data name="ArcEncryptedNotice" xml:space="preserve"> <data name="ArcEncryptedNotice" xml:space="preserve">
<value>Содержимое архива зашифровано. <value>Содержимое архива зашифровано.
Выберите алгоритм шифрования.</value> Выберите алгоритм шифрования.</value>
@ -156,6 +159,9 @@
<data name="MsgIllegalCharacters" xml:space="preserve"> <data name="MsgIllegalCharacters" xml:space="preserve">
<value>Имя файла содержит недопустимые символы</value> <value>Имя файла содержит недопустимые символы</value>
</data> </data>
<data name="MsgInvalidImageFormat" xml:space="preserve">
<value>{0}: не удалось распознать формат изображения.</value>
</data>
<data name="MsgUpdatingIndex" xml:space="preserve"> <data name="MsgUpdatingIndex" xml:space="preserve">
<value>Обновляется оглавление...</value> <value>Обновляется оглавление...</value>
</data> </data>