added creation options for AMI archives.

This commit is contained in:
morkt 2014-08-01 16:44:53 +04:00
parent b1015ff785
commit eefa7d2fc2
9 changed files with 295 additions and 54 deletions

View File

@ -35,27 +35,56 @@ using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using ZLibNet; using ZLibNet;
using GameRes.Formats.Strings; using GameRes.Formats.Strings;
using GameRes.Formats.Properties;
namespace GameRes.Formats namespace GameRes.Formats
{ {
public class AmiEntry : PackedEntry internal class AmiEntry : PackedEntry
{ {
private Lazy<Tuple<string,string>> m_item; public uint Id;
private Lazy<string> m_ext;
private Lazy<string> m_name;
private Lazy<string> m_type;
public override string Name public override string Name
{ {
get { return m_item.Value.Item1; } get { return m_name.Value; }
set { m_item = new Lazy<Tuple<string, string>>(() => new Tuple<string, string>(value, Type)); } set { m_name = new Lazy<string> (() => value); }
} }
public override string Type public override string Type
{ {
get { return m_item.Value.Item2; } get { return m_type.Value; }
set { m_item = new Lazy<Tuple<string, string>>(() => new Tuple<string, string>(Name, value)); } set { m_type = new Lazy<string> (() => value); }
} }
public AmiEntry (Func<Tuple<string, string>> factory) public AmiEntry (uint id, Func<string> ext_factory)
{ {
m_item = new Lazy<Tuple<string, string>> (factory); Id = id;
m_ext = new Lazy<string> (ext_factory);
m_name = new Lazy<string> (GetName);
m_type = new Lazy<string> (GetEntryType);
} }
private string GetName ()
{
return string.Format ("{0:x8}.{1}", Id, m_ext.Value);
}
private string GetEntryType ()
{
var ext = m_ext.Value;
if ("grp" == ext)
return "image";
if ("scr" == ext)
return "script";
return "";
}
}
internal class AmiOptions : ResourceOptions
{
public bool UseBaseArchive;
public string BaseArchive;
} }
[Export(typeof(ArchiveFormat))] [Export(typeof(ArchiveFormat))]
@ -93,18 +122,14 @@ namespace GameRes.Formats
uint size = file.View.ReadUInt32 (cur_offset+8); uint size = file.View.ReadUInt32 (cur_offset+8);
uint packed_size = file.View.ReadUInt32 (cur_offset+12); uint packed_size = file.View.ReadUInt32 (cur_offset+12);
var entry = new AmiEntry (() => { var entry = new AmiEntry (id, () => {
uint signature = file.View.ReadUInt32 (offset); uint signature = file.View.ReadUInt32 (offset);
string ext, type; if (0x00524353 == signature)
if (0x00524353 == signature) { return "scr";
ext = "scr"; type = "script"; else if (0 != packed_size || 0x00505247 == signature)
} else if (0 != packed_size) { return "grp";
ext = "grp"; type = "image"; else
} else { return "dat";
ext = "dat"; type = "";
}
string name = string.Format ("{0:x8}.{1}", id, ext);
return new Tuple<string,string> (name, type);
}); });
entry.Offset = offset; entry.Offset = offset;
@ -132,49 +157,87 @@ namespace GameRes.Formats
public override void Create (Stream output, IEnumerable<Entry> list, ResourceOptions options, public override void Create (Stream output, IEnumerable<Entry> list, ResourceOptions options,
EntryCallback callback) EntryCallback callback)
{ {
IDictionary<uint, PackedEntry> file_table = BuildFileTable (list); ArcFile base_archive = null;
uint file_count = (uint)file_table.Count; var ami_options = GetOptions<AmiOptions> (options);
if (0 == file_count) if (null != ami_options && ami_options.UseBaseArchive && !string.IsNullOrEmpty (ami_options.BaseArchive))
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) var base_file = new ArcView (ami_options.BaseArchive);
callback (callback_count++, entry.Value, arcStrings.MsgAddingFile); try
long current_offset = output.Position; {
if (current_offset > uint.MaxValue) if (base_file.View.ReadUInt32(0) == Signature)
throw new FileSizeException(); base_archive = TryOpen (base_file);
entry.Value.Offset = (uint)current_offset; if (null == base_archive)
entry.Value.Size = WriteAmiEntry (entry.Value, output); throw new InvalidFormatException (string.Format ("{0}: base archive could not be read",
Path.GetFileName (ami_options.BaseArchive)));
base_file = null;
}
finally
{
if (null != base_file)
base_file.Dispose();
}
} }
if (null != callback) try
callback (callback_count++, null, arcStrings.MsgWritingIndex);
output.Position = start_offset;
using (var header = new BinaryWriter (output, Encoding.ASCII, true))
{ {
header.Write (Signature); var file_table = new SortedDictionary<uint, PackedEntry>();
header.Write (file_count); if (null != base_archive)
header.Write (data_offset); {
header.Write ((uint)0); foreach (var entry in base_archive.Dir.Cast<AmiEntry>())
file_table[entry.Id] = entry;
}
int update_count = UpdateFileTable (file_table, list);
if (0 == update_count)
throw new InvalidFormatException (arcStrings.AMINoFiles);
uint file_count = (uint)file_table.Count;
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) foreach (var entry in file_table)
{ {
header.Write (entry.Key); if (null != callback)
header.Write ((uint)entry.Value.Offset); callback (callback_count++, entry.Value, arcStrings.MsgAddingFile);
header.Write ((uint)entry.Value.UnpackedSize); long current_offset = output.Position;
header.Write ((uint)entry.Value.Size); if (current_offset > uint.MaxValue)
throw new FileSizeException();
if (entry.Value is AmiEntry)
CopyAmiEntry (base_archive, entry.Value, output);
else
entry.Value.Size = WriteAmiEntry (entry.Value, output);
entry.Value.Offset = (uint)current_offset;
} }
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);
}
}
}
finally
{
if (null != base_archive)
base_archive.Dispose();
} }
} }
IDictionary<uint, PackedEntry> BuildFileTable (IEnumerable<Entry> list) int UpdateFileTable (IDictionary<uint, PackedEntry> table, IEnumerable<Entry> list)
{ {
var table = new SortedDictionary<uint, PackedEntry>(); int update_count = 0;
foreach (var entry in list) foreach (var entry in list)
{ {
string ext = Path.GetExtension (entry.Name).ToLower(); string ext = Path.GetExtension (entry.Name).ToLower();
@ -185,7 +248,7 @@ namespace GameRes.Formats
CultureInfo.InvariantCulture, out id)) CultureInfo.InvariantCulture, out id))
continue; continue;
PackedEntry existing; PackedEntry existing;
if (table.TryGetValue (id, out existing)) if (table.TryGetValue (id, out existing) && !(existing is AmiEntry))
{ {
var file_new = new FileInfo (entry.Name); var file_new = new FileInfo (entry.Name);
var file_old = new FileInfo (existing.Name); var file_old = new FileInfo (existing.Name);
@ -197,8 +260,15 @@ namespace GameRes.Formats
Name = entry.Name, Name = entry.Name,
Type = entry.Type Type = entry.Type
}; };
++update_count;
} }
return table; return update_count;
}
void CopyAmiEntry (ArcFile base_archive, Entry entry, Stream output)
{
using (var input = base_archive.File.CreateStream (entry.Offset, entry.Size))
input.CopyTo (output);
} }
uint WriteAmiEntry (PackedEntry entry, Stream output) uint WriteAmiEntry (PackedEntry entry, Stream output)
@ -247,6 +317,19 @@ namespace GameRes.Formats
return (uint)zstream.TotalOut; return (uint)zstream.TotalOut;
} }
} }
public override ResourceOptions GetDefaultOptions ()
{
return new AmiOptions {
UseBaseArchive = Settings.Default.AMIUseBaseArchive,
BaseArchive = Settings.Default.AMIBaseArchive,
};
}
public override object GetCreationWidget ()
{
return new GUI.CreateAMIWidget();
}
} }
[Export(typeof(ImageFormat))] [Export(typeof(ImageFormat))]

View File

@ -0,0 +1,18 @@
<Grid x:Class="GameRes.Formats.GUI.CreateAMIWidget"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:GameRes.Formats.Strings"
xmlns:p="clr-namespace:GameRes.Formats.Properties"
xmlns:local="clr-namespace:GameRes.Formats.GUI">
<StackPanel Orientation="Vertical" Margin="0">
<Label Target="{Binding ElementName=BaseArchive}" ToolTip="{x:Static s:arcStrings.AMIBaseTooltip}" Padding="1,0,0,5">
<CheckBox Name="UseBaseArchive" Content="{x:Static s:arcStrings.AMIBaseArchive}"
IsChecked="{Binding Source={x:Static p:Settings.Default}, Path=AMIUseBaseArchive, Mode=TwoWay}"/>
</Label>
<StackPanel Orientation="Horizontal" IsEnabled="{Binding ElementName=UseBaseArchive, Path=IsChecked}">
<TextBox Name="BaseArchive" Width="200" Text="{Binding Source={x:Static p:Settings.Default}, Path=AMIBaseArchive, Mode=TwoWay}" ToolTip="{x:Static s:arcStrings.AMIBaseTooltip}" HorizontalAlignment="Left"/>
<Button Content="..." Height="{Binding ElementName=BaseArchive, Path=ActualHeight}" Width="{Binding Path=Height, RelativeSource={RelativeSource Self}, Mode=OneWay}"
Margin="7,0,0,0" Click="Browse_Click"/>
</StackPanel>
</StackPanel>
</Grid>

View File

@ -0,0 +1,59 @@
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using GameRes.Formats.Strings;
using Microsoft.Win32;
namespace GameRes.Formats.GUI
{
/// <summary>
/// Interaction logic for CreateAMIWidget.xaml
/// </summary>
public partial class CreateAMIWidget : Grid
{
public CreateAMIWidget ()
{
InitializeComponent ();
}
private void Browse_Click (object sender, RoutedEventArgs e)
{
string initial = BaseArchive.Text;
string dir = ".";
if (!string.IsNullOrEmpty (initial))
{
var parent = Directory.GetParent (initial);
if (null != parent)
{
dir = parent.FullName;
initial = Path.GetFileName (initial);
}
}
dir = Path.GetFullPath (dir);
var dlg = new OpenFileDialog {
CheckFileExists = true,
CheckPathExists = true,
FileName = initial,
InitialDirectory = dir,
Multiselect = false,
Title = arcStrings.AMIChooseBase,
};
var owner = FindVisualParent<Window> (this);
if (dlg.ShowDialog (owner).Value && !string.IsNullOrEmpty (dlg.FileName))
BaseArchive.Text = dlg.FileName;
}
static parentItem FindVisualParent<parentItem> (DependencyObject obj) where parentItem : DependencyObject
{
if (null == obj)
return null;
DependencyObject parent = VisualTreeHelper.GetParent (obj);
while (parent != null && !(parent is parentItem))
{
parent = VisualTreeHelper.GetParent (parent);
}
return parent as parentItem;
}
}
}

View File

@ -141,5 +141,29 @@ namespace GameRes.Formats.Properties {
this["ONSCompression"] = value; this["ONSCompression"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string AMIBaseArchive {
get {
return ((string)(this["AMIBaseArchive"]));
}
set {
this["AMIBaseArchive"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool AMIUseBaseArchive {
get {
return ((bool)(this["AMIUseBaseArchive"]));
}
set {
this["AMIUseBaseArchive"] = value;
}
}
} }
} }

View File

@ -32,5 +32,11 @@
<Setting Name="ONSCompression" Type="GameRes.Formats.ONScripter.Compression" Scope="User"> <Setting Name="ONSCompression" Type="GameRes.Formats.ONScripter.Compression" Scope="User">
<Value Profile="(Default)">None</Value> <Value Profile="(Default)">None</Value>
</Setting> </Setting>
<Setting Name="AMIBaseArchive" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="AMIUseBaseArchive" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
</Settings> </Settings>
</SettingsFile> </SettingsFile>

View File

@ -60,6 +60,33 @@ namespace GameRes.Formats.Strings {
} }
} }
/// <summary>
/// Looks up a localized string similar to Base archive.
/// </summary>
public static string AMIBaseArchive {
get {
return ResourceManager.GetString("AMIBaseArchive", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to New archive will also contain entries from the base archive..
/// </summary>
public static string AMIBaseTooltip {
get {
return ResourceManager.GetString("AMIBaseTooltip", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Select base archive.
/// </summary>
public static string AMIChooseBase {
get {
return ResourceManager.GetString("AMIChooseBase", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Amaterasu Translations Muv-Luv archive. /// Looks up a localized string similar to Amaterasu Translations Muv-Luv archive.
/// </summary> /// </summary>

View File

@ -117,6 +117,15 @@
<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="AMIBaseArchive" xml:space="preserve">
<value>Base archive</value>
</data>
<data name="AMIBaseTooltip" xml:space="preserve">
<value>New archive will also contain entries from the base archive.</value>
</data>
<data name="AMIChooseBase" xml:space="preserve">
<value>Select base archive</value>
</data>
<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>

View File

@ -117,6 +117,15 @@
<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="AMIBaseArchive" xml:space="preserve">
<value>Базовый архив</value>
</data>
<data name="AMIBaseTooltip" xml:space="preserve">
<value>В новый архив будут также включены файлы из базового архива.</value>
</data>
<data name="AMIChooseBase" xml:space="preserve">
<value>Укажите базовый архив</value>
</data>
<data name="AMINoFiles" xml:space="preserve"> <data name="AMINoFiles" xml:space="preserve">
<value>Не найдено файлов, подходящих для архива AMI.</value> <value>Не найдено файлов, подходящих для архива AMI.</value>
</data> </data>

View File

@ -34,6 +34,12 @@
<setting name="ONSCompression" serializeAs="String"> <setting name="ONSCompression" serializeAs="String">
<value>None</value> <value>None</value>
</setting> </setting>
<setting name="AMIBaseArchive" serializeAs="String">
<value />
</setting>
<setting name="AMIUseBaseArchive" serializeAs="String">
<value>False</value>
</setting>
</GameRes.Formats.Properties.Settings> </GameRes.Formats.Properties.Settings>
</userSettings> </userSettings>
</configuration> </configuration>