diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj
index 7b5fedb3..660733ed 100644
--- a/ArcFormats/ArcFormats.csproj
+++ b/ArcFormats/ArcFormats.csproj
@@ -93,6 +93,9 @@
CreateSGWidget.xaml
+
+ CreateWARCWidget.xaml
+
CreateXP3Widget.xaml
@@ -184,6 +187,10 @@
Designer
MSBuild:Compile
+
+ Designer
+ MSBuild:Compile
+
Designer
MSBuild:Compile
diff --git a/ArcFormats/ArcWILL.cs b/ArcFormats/ArcWILL.cs
index edaf69f2..8af29b62 100644
--- a/ArcFormats/ArcWILL.cs
+++ b/ArcFormats/ArcWILL.cs
@@ -23,8 +23,12 @@
// IN THE SOFTWARE.
//
+using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
+using System.IO;
+using GameRes.Formats.Properties;
+using GameRes.Formats.Strings;
namespace GameRes.Formats.Will
{
@@ -35,14 +39,24 @@ namespace GameRes.Formats.Will
public uint DirOffset;
}
+ public class ArcOptions : ResourceOptions
+ {
+ public int NameLength { get; set; }
+ }
+
[Export(typeof(ArchiveFormat))]
public class ArcOpener : ArchiveFormat
{
- public override string Tag { get { return "ARC"; } }
+ public override string Tag { get { return "WARC"; } }
public override string Description { get { return "Will Co. game engine resource archive"; } }
public override uint Signature { get { return 0; } }
public override bool IsHierarchic { get { return false; } }
- public override bool CanCreate { get { return false; } }
+ public override bool CanCreate { get { return true; } }
+
+ ArcOpener ()
+ {
+ Extensions = new string[] { "arc" };
+ }
public override ArcFile TryOpen (ArcView file)
{
@@ -52,16 +66,14 @@ namespace GameRes.Formats.Will
uint dir_offset = 4;
var ext_list = new List (ext_count);
- int file_count = 0;
for (int i = 0; i < ext_count; ++i)
{
string ext = file.View.ReadString (dir_offset, 4).ToLowerInvariant();
int count = file.View.ReadInt32 (dir_offset+4);
uint offset = file.View.ReadUInt32 (dir_offset+8);
- if (count <= 0 || count > 0xffff || offset <= dir_offset)
+ if (count <= 0 || count > 0xffff || offset <= dir_offset || offset > file.MaxOffset)
return null;
ext_list.Add (new ExtRecord { Extension = ext, FileCount = count, DirOffset = offset });
- file_count += count;
dir_offset += 12;
}
var dir = ReadFileList (file, ext_list, 9);
@@ -77,10 +89,14 @@ namespace GameRes.Formats.Will
var dir = new List();
foreach (var ext in ext_list)
{
+ dir.Capacity = dir.Count + ext.FileCount;
uint dir_offset = ext.DirOffset;
for (int i = 0; i < ext.FileCount; ++i)
{
- string name = file.View.ReadString (dir_offset, name_size).ToLowerInvariant()+'.'+ext.Extension;
+ string name = file.View.ReadString (dir_offset, name_size);
+ if (string.IsNullOrEmpty (name))
+ return null;
+ name = name.ToLowerInvariant()+'.'+ext.Extension;
var entry = FormatCatalog.Instance.CreateEntry (name);
entry.Size = file.View.ReadUInt32 (dir_offset+name_size);
entry.Offset = file.View.ReadUInt32 (dir_offset+name_size+4);
@@ -92,5 +108,119 @@ namespace GameRes.Formats.Will
}
return dir;
}
+
+ public override ResourceOptions GetDefaultOptions ()
+ {
+ return new ArcOptions { NameLength = Settings.Default.WARCNameLength };
+ }
+
+ public override object GetCreationWidget ()
+ {
+ return new GUI.CreateWARCWidget();
+ }
+
+ internal class ArcEntry : Entry
+ {
+ public byte[] RawName;
+ }
+
+ internal class ArcDirectory
+ {
+ public byte[] Extension;
+ public uint DirOffset;
+ public List Files;
+ }
+
+ public override void Create (Stream output, IEnumerable list, ResourceOptions options,
+ EntryCallback callback)
+ {
+ var arc_options = GetOptions (options);
+ var encoding = Encodings.cp932.WithFatalFallback();
+
+ int file_count = 0;
+ var file_table = new SortedDictionary();
+ foreach (var entry in list)
+ {
+ string ext = Path.GetExtension (entry.Name).TrimStart ('.').ToUpperInvariant();
+ if (string.IsNullOrEmpty (ext))
+ throw new InvalidFileName (entry.Name, arcStrings.MsgNoExtension);
+ if (ext.Length > 3)
+ throw new InvalidFileName (entry.Name, arcStrings.MsgExtensionTooLong);
+ string name = Path.GetFileNameWithoutExtension (entry.Name).ToUpperInvariant();
+ byte[] raw_name = encoding.GetBytes (name);
+ if (raw_name.Length > arc_options.NameLength)
+ throw new InvalidFileName (entry.Name, arcStrings.MsgFileNameTooLong);
+
+ ArcDirectory dir;
+ if (!file_table.TryGetValue (ext, out dir))
+ {
+ byte[] raw_ext = encoding.GetBytes (ext);
+ if (raw_ext.Length > 3)
+ throw new InvalidFileName (entry.Name, arcStrings.MsgExtensionTooLong);
+ dir = new ArcDirectory { Extension = raw_ext, Files = new List() };
+ file_table[ext] = dir;
+ }
+ dir.Files.Add (new ArcEntry { Name = entry.Name, RawName = raw_name });
+ ++file_count;
+ }
+ if (null != callback)
+ callback (file_count+1, null, null);
+
+ int callback_count = 0;
+ long dir_offset = 4 + file_table.Count * 12;
+ long data_offset = dir_offset + (arc_options.NameLength + 9) * file_count;
+ output.Position = data_offset;
+ foreach (var ext in file_table.Keys)
+ {
+ var dir = file_table[ext];
+ dir.DirOffset = (uint)dir_offset;
+ dir_offset += (arc_options.NameLength + 9) * dir.Files.Count;
+ foreach (var entry in dir.Files)
+ {
+ if (null != callback)
+ callback (callback_count++, entry, arcStrings.MsgAddingFile);
+ entry.Offset = data_offset;
+ using (var input = File.OpenRead (entry.Name))
+ {
+ var size = input.Length;
+ if (size > uint.MaxValue || data_offset + size > uint.MaxValue)
+ throw new FileSizeException();
+ data_offset += size;
+ entry.Size = (uint)size;
+ input.CopyTo (output);
+ }
+ }
+ }
+ if (null != callback)
+ callback (callback_count++, null, arcStrings.MsgWritingIndex);
+
+ output.Position = 0;
+ using (var header = new BinaryWriter (output, encoding, true))
+ {
+ byte[] buffer = new byte[arc_options.NameLength+1];
+ header.Write (file_table.Count);
+ foreach (var ext in file_table)
+ {
+ Array.Copy (ext.Value.Extension, buffer, ext.Value.Extension.Length);
+ for (int i = ext.Value.Extension.Length; i < 4; ++i)
+ buffer[i] = 0;
+ header.Write (buffer, 0, 4);
+ header.Write (ext.Value.Files.Count);
+ header.Write (ext.Value.DirOffset);
+ }
+ foreach (var ext in file_table)
+ {
+ foreach (var entry in ext.Value.Files)
+ {
+ Array.Copy (entry.RawName, buffer, entry.RawName.Length);
+ for (int i = entry.RawName.Length; i < buffer.Length; ++i)
+ buffer[i] = 0;
+ header.Write (buffer);
+ header.Write (entry.Size);
+ header.Write ((uint)entry.Offset);
+ }
+ }
+ }
+ }
}
}
diff --git a/ArcFormats/CreateWARCWidget.xaml b/ArcFormats/CreateWARCWidget.xaml
new file mode 100644
index 00000000..2360976b
--- /dev/null
+++ b/ArcFormats/CreateWARCWidget.xaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ArcFormats/CreateWARCWidget.xaml.cs b/ArcFormats/CreateWARCWidget.xaml.cs
new file mode 100644
index 00000000..6fd5e086
--- /dev/null
+++ b/ArcFormats/CreateWARCWidget.xaml.cs
@@ -0,0 +1,15 @@
+using System.Windows.Controls;
+
+namespace GameRes.Formats.GUI
+{
+ ///
+ /// Interaction logic for CreateWARCWidget.xaml
+ ///
+ public partial class CreateWARCWidget : Grid
+ {
+ public CreateWARCWidget ()
+ {
+ InitializeComponent ();
+ }
+ }
+}
diff --git a/ArcFormats/Properties/AssemblyInfo.cs b/ArcFormats/Properties/AssemblyInfo.cs
index 3287c38f..1a18288e 100644
--- a/ArcFormats/Properties/AssemblyInfo.cs
+++ b/ArcFormats/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion ("1.0.2.20")]
-[assembly: AssemblyFileVersion ("1.0.2.20")]
+[assembly: AssemblyVersion ("1.0.2.21")]
+[assembly: AssemblyFileVersion ("1.0.2.21")]
diff --git a/ArcFormats/Properties/Settings.Designer.cs b/ArcFormats/Properties/Settings.Designer.cs
index 8b41edd0..523f8e8e 100644
--- a/ArcFormats/Properties/Settings.Designer.cs
+++ b/ArcFormats/Properties/Settings.Designer.cs
@@ -237,5 +237,17 @@ namespace GameRes.Formats.Properties {
this["NPAKey2"] = value;
}
}
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("8")]
+ public int WARCNameLength {
+ get {
+ return ((int)(this["WARCNameLength"]));
+ }
+ set {
+ this["WARCNameLength"] = value;
+ }
+ }
}
}
diff --git a/ArcFormats/Properties/Settings.settings b/ArcFormats/Properties/Settings.settings
index da1a068d..1085099c 100644
--- a/ArcFormats/Properties/Settings.settings
+++ b/ArcFormats/Properties/Settings.settings
@@ -56,5 +56,8 @@
555831124
+
+ 8
+
\ No newline at end of file
diff --git a/ArcFormats/Strings/arcStrings.Designer.cs b/ArcFormats/Strings/arcStrings.Designer.cs
index 23e6886b..6f0e8b49 100644
--- a/ArcFormats/Strings/arcStrings.Designer.cs
+++ b/ArcFormats/Strings/arcStrings.Designer.cs
@@ -279,6 +279,15 @@ namespace GameRes.Formats.Strings {
}
}
+ ///
+ /// Looks up a localized string similar to File name extension too long..
+ ///
+ public static string MsgExtensionTooLong {
+ get {
+ return ResourceManager.GetString("MsgExtensionTooLong", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to File name is too long.
///
@@ -315,6 +324,15 @@ namespace GameRes.Formats.Strings {
}
}
+ ///
+ /// Looks up a localized string similar to File name without extension..
+ ///
+ public static string MsgNoExtension {
+ get {
+ return ResourceManager.GetString("MsgNoExtension", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Number of files exceedes archive limit..
///
@@ -487,6 +505,16 @@ namespace GameRes.Formats.Strings {
}
}
+ ///
+ /// Looks up a localized string similar to Maximum file name length
+ ///(not including extension).
+ ///
+ public static string WARCLabelLength {
+ get {
+ return ResourceManager.GetString("WARCLabelLength", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Liar-soft game resource archive.
///
diff --git a/ArcFormats/Strings/arcStrings.resx b/ArcFormats/Strings/arcStrings.resx
index ce0df232..a0b602ea 100644
--- a/ArcFormats/Strings/arcStrings.resx
+++ b/ArcFormats/Strings/arcStrings.resx
@@ -192,6 +192,9 @@ predefined encryption scheme.
Encryption method not implemented
+
+ File name extension too long.
+
File name is too long
@@ -204,6 +207,9 @@ predefined encryption scheme.
Invalid archive version specified.
+
+ File name without extension.
+
Number of files exceedes archive limit.
@@ -262,6 +268,10 @@ predefined encryption scheme.
Hex number
+
+ Maximum file name length
+(not including extension)
+
Liar-soft game resource archive
diff --git a/ArcFormats/Strings/arcStrings.ru-RU.resx b/ArcFormats/Strings/arcStrings.ru-RU.resx
index 84862a7f..aeb8bbb9 100644
--- a/ArcFormats/Strings/arcStrings.ru-RU.resx
+++ b/ArcFormats/Strings/arcStrings.ru-RU.resx
@@ -174,6 +174,9 @@
Метод шифрования не реализован
+
+ Слишком длинное расширение имени файла.
+
Слишком длинное имя файла
@@ -186,6 +189,9 @@
Указана некорректная версия архива.
+
+ Имя файла без расширения.
+
Количество файлов превышает ограничения архива.
@@ -226,6 +232,10 @@
Шестнадцатеричное число
+
+ Максимальная длина имени файла
+(не считая расширения)
+
Сжать содержимое
diff --git a/ArcFormats/app.config b/ArcFormats/app.config
index cdb202d3..476a6b4d 100644
--- a/ArcFormats/app.config
+++ b/ArcFormats/app.config
@@ -58,6 +58,9 @@
555831124
+
+ 8
+