mirror of
https://github.com/crskycode/GARbro.git
synced 2024-12-23 19:34:15 +08:00
added creation options for AMI archives.
This commit is contained in:
parent
b1015ff785
commit
eefa7d2fc2
@ -35,27 +35,56 @@ using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using ZLibNet;
|
||||
using GameRes.Formats.Strings;
|
||||
using GameRes.Formats.Properties;
|
||||
|
||||
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
|
||||
{
|
||||
get { return m_item.Value.Item1; }
|
||||
set { m_item = new Lazy<Tuple<string, string>>(() => new Tuple<string, string>(value, Type)); }
|
||||
get { return m_name.Value; }
|
||||
set { m_name = new Lazy<string> (() => value); }
|
||||
}
|
||||
public override string Type
|
||||
{
|
||||
get { return m_item.Value.Item2; }
|
||||
set { m_item = new Lazy<Tuple<string, string>>(() => new Tuple<string, string>(Name, value)); }
|
||||
get { return m_type.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))]
|
||||
@ -93,18 +122,14 @@ namespace GameRes.Formats
|
||||
uint size = file.View.ReadUInt32 (cur_offset+8);
|
||||
uint packed_size = file.View.ReadUInt32 (cur_offset+12);
|
||||
|
||||
var entry = new AmiEntry (() => {
|
||||
var entry = new AmiEntry (id, () => {
|
||||
uint signature = file.View.ReadUInt32 (offset);
|
||||
string ext, type;
|
||||
if (0x00524353 == signature) {
|
||||
ext = "scr"; type = "script";
|
||||
} else if (0 != packed_size) {
|
||||
ext = "grp"; type = "image";
|
||||
} else {
|
||||
ext = "dat"; type = "";
|
||||
}
|
||||
string name = string.Format ("{0:x8}.{1}", id, ext);
|
||||
return new Tuple<string,string> (name, type);
|
||||
if (0x00524353 == signature)
|
||||
return "scr";
|
||||
else if (0 != packed_size || 0x00505247 == signature)
|
||||
return "grp";
|
||||
else
|
||||
return "dat";
|
||||
});
|
||||
|
||||
entry.Offset = offset;
|
||||
@ -132,49 +157,87 @@ namespace GameRes.Formats
|
||||
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)
|
||||
ArcFile base_archive = null;
|
||||
var ami_options = GetOptions<AmiOptions> (options);
|
||||
if (null != ami_options && ami_options.UseBaseArchive && !string.IsNullOrEmpty (ami_options.BaseArchive))
|
||||
{
|
||||
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);
|
||||
var base_file = new ArcView (ami_options.BaseArchive);
|
||||
try
|
||||
{
|
||||
if (base_file.View.ReadUInt32(0) == Signature)
|
||||
base_archive = TryOpen (base_file);
|
||||
if (null == base_archive)
|
||||
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)
|
||||
callback (callback_count++, null, arcStrings.MsgWritingIndex);
|
||||
output.Position = start_offset;
|
||||
using (var header = new BinaryWriter (output, Encoding.ASCII, true))
|
||||
try
|
||||
{
|
||||
header.Write (Signature);
|
||||
header.Write (file_count);
|
||||
header.Write (data_offset);
|
||||
header.Write ((uint)0);
|
||||
var file_table = new SortedDictionary<uint, PackedEntry>();
|
||||
if (null != base_archive)
|
||||
{
|
||||
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)
|
||||
{
|
||||
header.Write (entry.Key);
|
||||
header.Write ((uint)entry.Value.Offset);
|
||||
header.Write ((uint)entry.Value.UnpackedSize);
|
||||
header.Write ((uint)entry.Value.Size);
|
||||
if (null != callback)
|
||||
callback (callback_count++, entry.Value, arcStrings.MsgAddingFile);
|
||||
long current_offset = output.Position;
|
||||
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)
|
||||
{
|
||||
string ext = Path.GetExtension (entry.Name).ToLower();
|
||||
@ -185,7 +248,7 @@ namespace GameRes.Formats
|
||||
CultureInfo.InvariantCulture, out id))
|
||||
continue;
|
||||
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_old = new FileInfo (existing.Name);
|
||||
@ -197,8 +260,15 @@ namespace GameRes.Formats
|
||||
Name = entry.Name,
|
||||
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)
|
||||
@ -247,6 +317,19 @@ namespace GameRes.Formats
|
||||
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))]
|
||||
|
18
ArcFormats/CreateAMIWidget.xaml
Normal file
18
ArcFormats/CreateAMIWidget.xaml
Normal 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>
|
59
ArcFormats/CreateAMIWidget.xaml.cs
Normal file
59
ArcFormats/CreateAMIWidget.xaml.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
24
ArcFormats/Properties/Settings.Designer.cs
generated
24
ArcFormats/Properties/Settings.Designer.cs
generated
@ -141,5 +141,29 @@ namespace GameRes.Formats.Properties {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,5 +32,11 @@
|
||||
<Setting Name="ONSCompression" Type="GameRes.Formats.ONScripter.Compression" Scope="User">
|
||||
<Value Profile="(Default)">None</Value>
|
||||
</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>
|
||||
</SettingsFile>
|
27
ArcFormats/Strings/arcStrings.Designer.cs
generated
27
ArcFormats/Strings/arcStrings.Designer.cs
generated
@ -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>
|
||||
/// Looks up a localized string similar to Amaterasu Translations Muv-Luv archive.
|
||||
/// </summary>
|
||||
|
@ -117,6 +117,15 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</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">
|
||||
<value>Amaterasu Translations Muv-Luv archive</value>
|
||||
</data>
|
||||
|
@ -117,6 +117,15 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</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">
|
||||
<value>Не найдено файлов, подходящих для архива AMI.</value>
|
||||
</data>
|
||||
|
@ -34,6 +34,12 @@
|
||||
<setting name="ONSCompression" serializeAs="String">
|
||||
<value>None</value>
|
||||
</setting>
|
||||
<setting name="AMIBaseArchive" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="AMIUseBaseArchive" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
</GameRes.Formats.Properties.Settings>
|
||||
</userSettings>
|
||||
</configuration>
|
Loading…
x
Reference in New Issue
Block a user