diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj
index d81bb8cc..07455422 100644
--- a/ArcFormats/ArcFormats.csproj
+++ b/ArcFormats/ArcFormats.csproj
@@ -61,6 +61,7 @@
+
@@ -72,6 +73,9 @@
CreateONSWidget.xaml
+
+ CreatePDWidget.xaml
+
CreateSGWidget.xaml
@@ -139,6 +143,10 @@
Designer
MSBuild:Compile
+
+ Designer
+ MSBuild:Compile
+
Designer
MSBuild:Compile
diff --git a/ArcFormats/ArcPD.cs b/ArcFormats/ArcPD.cs
new file mode 100644
index 00000000..6ba90049
--- /dev/null
+++ b/ArcFormats/ArcPD.cs
@@ -0,0 +1,221 @@
+//! \file ArcPD.cs
+//! \date Thu Aug 14 19:10:02 2014
+//! \brief PD archive format implementation.
+//
+// Copyright (C) 2014 by morkt
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.IO;
+using System.Linq;
+using System.Text;
+using GameRes.Formats.Properties;
+using GameRes.Formats.Strings;
+
+namespace GameRes.Formats.Fs
+{
+ internal class PackPlusArchive : ArcFile
+ {
+ public PackPlusArchive (ArcView arc, ArchiveFormat impl, ICollection dir)
+ : base (arc, impl, dir)
+ {
+ }
+ }
+
+ public class PdOptions : ResourceOptions
+ {
+ public bool ScrambleContents { get; set; }
+ }
+
+ [Export(typeof(ArchiveFormat))]
+ public class PdOpener : ArchiveFormat
+ {
+ public override string Tag { get { return "PD"; } }
+ public override string Description { get { return arcStrings.PDDescription; } }
+ public override uint Signature { get { return 0x6b636150; } } // Pack
+ public override bool IsHierarchic { get { return false; } }
+ public override bool CanCreate { get { return true; } }
+
+ public override ArcFile TryOpen (ArcView file)
+ {
+ uint version = file.View.ReadUInt32 (4);
+ if (0x796c6e4f != version && 0x73756c50 != version) // 'Only' || 'Plus'
+ return null;
+ uint count = file.View.ReadUInt32 (0x40);
+ if (count > 0x0fffff || count * 0x90 >= file.MaxOffset)
+ return null;
+ bool encrypted = 0x73756c50 == version;
+ long cur_offset = 0x48;
+ var dir = new List ((int)count);
+ for (uint i = 0; i < count; ++i)
+ {
+ string name = file.View.ReadString (cur_offset, 0x80);
+ var entry = FormatCatalog.Instance.CreateEntry (name);
+ entry.Offset = file.View.ReadInt64 (cur_offset+0x80);
+ entry.Size = file.View.ReadUInt32 (cur_offset+0x88);
+ if (!entry.CheckPlacement (file.MaxOffset))
+ return null;
+ if (Path.GetExtension (name).Equals (".dsf", System.StringComparison.OrdinalIgnoreCase))
+ entry.Type = "script";
+ dir.Add (entry);
+ cur_offset += 0x90;
+ }
+ return encrypted ? new PackPlusArchive (file, this, dir) : new ArcFile (file, this, dir);
+ }
+
+ public override Stream OpenEntry (ArcFile arc, Entry entry)
+ {
+ Stream input = arc.File.CreateStream (entry.Offset, entry.Size);
+ if (arc is PackPlusArchive)
+ {
+ MemoryStream buffer;
+ using (input)
+ {
+ buffer = new MemoryStream ((int)entry.Size);
+ input.CopyTo (buffer);
+ }
+ var data = buffer.GetBuffer();
+ for (uint i = 0; i < entry.Size; ++i)
+ data[i] = (byte)~data[i];
+ buffer.Position = 0;
+ input = buffer;
+ }
+ return input;
+ }
+
+ public override ResourceOptions GetDefaultOptions ()
+ {
+ return new PdOptions { ScrambleContents = Settings.Default.PDScrambleContents };
+ }
+
+ public override object GetCreationWidget ()
+ {
+ return new GUI.CreatePDWidget();
+ }
+
+ public override void Create (Stream output, IEnumerable list, ResourceOptions options,
+ EntryCallback callback)
+ {
+ int file_count = list.Count();
+ if (file_count > 0x4000)
+ throw new InvalidFormatException (arcStrings.MsgTooManyFiles);
+ if (null != callback)
+ callback (file_count+2, null, null);
+ int callback_count = 0;
+ var pd_options = GetOptions (options);
+
+ using (var writer = new BinaryWriter (output, Encoding.ASCII, true))
+ {
+ writer.Write (Signature);
+ if (pd_options.ScrambleContents)
+ writer.Write ((uint)0x73756c50);
+ else
+ writer.Write ((uint)0x796c6e4f);
+ output.Seek (0x38, SeekOrigin.Current);
+ writer.Write (file_count);
+ writer.Write ((int)0);
+ long dir_offset = output.Position;
+
+ if (null != callback)
+ callback (callback_count++, null, arcStrings.MsgWritingIndex);
+
+ var encoding = Encodings.cp932.WithFatalFallback();
+ byte[] name_buf = new byte[0x80];
+ int previous_size = 0;
+
+ // first, write names only
+ foreach (var entry in list)
+ {
+ string name = Path.GetFileName (entry.Name);
+ try
+ {
+ int size = encoding.GetBytes (name, 0, name.Length, name_buf, 0);
+ for (int i = size; i < previous_size; ++i)
+ name_buf[i] = 0;
+ previous_size = size;
+ }
+ catch (EncoderFallbackException X)
+ {
+ throw new InvalidFileName (entry.Name, arcStrings.MsgIllegalCharacters, X);
+ }
+ catch (ArgumentException X)
+ {
+ throw new InvalidFileName (entry.Name, arcStrings.MsgFileNameTooLong, X);
+ }
+ writer.Write (name_buf);
+ writer.BaseStream.Seek (16, SeekOrigin.Current);
+ }
+
+ // now, write files and remember offset/sizes
+ long current_offset = 0x240000 + dir_offset;
+ output.Seek (current_offset, SeekOrigin.Begin);
+ foreach (var entry in list)
+ {
+ if (null != callback)
+ callback (callback_count++, entry, arcStrings.MsgAddingFile);
+
+ entry.Offset = current_offset;
+ using (var input = File.OpenRead (entry.Name))
+ {
+ var size = input.Length;
+ if (size > uint.MaxValue)
+ throw new FileSizeException();
+ current_offset += size;
+ entry.Size = (uint)size;
+ if (pd_options.ScrambleContents)
+ CopyScrambled (input, output);
+ else
+ input.CopyTo (output);
+ }
+ }
+
+ if (null != callback)
+ callback (callback_count++, null, arcStrings.MsgUpdatingIndex);
+
+ // at last, go back to directory and write offset/sizes
+ dir_offset += 0x80;
+ foreach (var entry in list)
+ {
+ writer.BaseStream.Position = dir_offset;
+ writer.Write (entry.Offset);
+ writer.Write ((long)entry.Size);
+ dir_offset += 0x90;
+ }
+ }
+ }
+
+ void CopyScrambled (Stream input, Stream output)
+ {
+ byte[] buffer = new byte[81920];
+ for (;;)
+ {
+ int read = input.Read (buffer, 0, buffer.Length);
+ if (0 == read)
+ break;
+ for (int i = 0; i < read; ++i)
+ buffer[i] = (byte)~buffer[i];
+ output.Write (buffer, 0, read);
+ }
+ }
+ }
+}
diff --git a/ArcFormats/CreatePDWidget.xaml b/ArcFormats/CreatePDWidget.xaml
new file mode 100644
index 00000000..166fee2f
--- /dev/null
+++ b/ArcFormats/CreatePDWidget.xaml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/ArcFormats/CreatePDWidget.xaml.cs b/ArcFormats/CreatePDWidget.xaml.cs
new file mode 100644
index 00000000..512f53ed
--- /dev/null
+++ b/ArcFormats/CreatePDWidget.xaml.cs
@@ -0,0 +1,15 @@
+using System.Windows.Controls;
+
+namespace GameRes.Formats.GUI
+{
+ ///
+ /// Interaction logic for CreatePDWidget.xaml
+ ///
+ public partial class CreatePDWidget : Grid
+ {
+ public CreatePDWidget ()
+ {
+ InitializeComponent ();
+ }
+ }
+}
diff --git a/ArcFormats/Properties/Settings.Designer.cs b/ArcFormats/Properties/Settings.Designer.cs
index a5869627..4b59afd3 100644
--- a/ArcFormats/Properties/Settings.Designer.cs
+++ b/ArcFormats/Properties/Settings.Designer.cs
@@ -165,5 +165,17 @@ namespace GameRes.Formats.Properties {
this["AMIUseBaseArchive"] = value;
}
}
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool PDScrambleContents {
+ get {
+ return ((bool)(this["PDScrambleContents"]));
+ }
+ set {
+ this["PDScrambleContents"] = value;
+ }
+ }
}
}
diff --git a/ArcFormats/Properties/Settings.settings b/ArcFormats/Properties/Settings.settings
index 8b465116..be5bc78a 100644
--- a/ArcFormats/Properties/Settings.settings
+++ b/ArcFormats/Properties/Settings.settings
@@ -38,5 +38,8 @@
False
+
+ False
+
\ No newline at end of file
diff --git a/ArcFormats/Strings/arcStrings.Designer.cs b/ArcFormats/Strings/arcStrings.Designer.cs
index e11f4f96..d1e0d0db 100644
--- a/ArcFormats/Strings/arcStrings.Designer.cs
+++ b/ArcFormats/Strings/arcStrings.Designer.cs
@@ -261,6 +261,15 @@ namespace GameRes.Formats.Strings {
}
}
+ ///
+ /// Looks up a localized string similar to Number of files exceedes archive limit..
+ ///
+ public static string MsgTooManyFiles {
+ get {
+ return ResourceManager.GetString("MsgTooManyFiles", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Updating index....
///
@@ -333,6 +342,24 @@ namespace GameRes.Formats.Strings {
}
}
+ ///
+ /// Looks up a localized string similar to Flying Shine resource archive.
+ ///
+ public static string PDDescription {
+ get {
+ return ResourceManager.GetString("PDDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Scramble contents.
+ ///
+ public static string PDScrambleContents {
+ get {
+ return ResourceManager.GetString("PDScrambleContents", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Amaterasu Translations Muv-Luv script file.
///
diff --git a/ArcFormats/Strings/arcStrings.resx b/ArcFormats/Strings/arcStrings.resx
index aa6ee1c0..f186a541 100644
--- a/ArcFormats/Strings/arcStrings.resx
+++ b/ArcFormats/Strings/arcStrings.resx
@@ -186,6 +186,9 @@ predefined encryption scheme.
{0}: image format not recognized.
+
+ Number of files exceedes archive limit.
+
Updating index...
@@ -210,6 +213,12 @@ predefined encryption scheme.
None
+
+ Flying Shine resource archive
+
+
+ Scramble contents
+
Amaterasu Translations Muv-Luv script file
diff --git a/ArcFormats/Strings/arcStrings.ru-RU.resx b/ArcFormats/Strings/arcStrings.ru-RU.resx
index 99cdcfe5..b8dfe3cd 100644
--- a/ArcFormats/Strings/arcStrings.ru-RU.resx
+++ b/ArcFormats/Strings/arcStrings.ru-RU.resx
@@ -171,6 +171,9 @@
{0}: не удалось распознать формат изображения.
+
+ Количество файлов превышает ограничения архива.
+
Обновляется оглавление...
@@ -186,6 +189,9 @@
Без компрессии
+
+ Шифровать содержимое
+
Кодировка имён файлов
diff --git a/ArcFormats/app.config b/ArcFormats/app.config
index 5b0274a2..c03c64d8 100644
--- a/ArcFormats/app.config
+++ b/ArcFormats/app.config
@@ -11,7 +11,7 @@
NotEncrypted
-
+
4294967295
@@ -35,11 +35,14 @@
None
-
+
False
+
+ False
+