diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj
index e4a716ef..f8e37d86 100644
--- a/ArcFormats/ArcFormats.csproj
+++ b/ArcFormats/ArcFormats.csproj
@@ -204,6 +204,10 @@
+
+
+
+
diff --git a/ArcFormats/VnMaker/ArcZIP.cs b/ArcFormats/VnMaker/ArcZIP.cs
new file mode 100644
index 00000000..e8ac81f5
--- /dev/null
+++ b/ArcFormats/VnMaker/ArcZIP.cs
@@ -0,0 +1,127 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.IO;
+using System.Linq;
+
+using SharpZip = ICSharpCode.SharpZipLib.Zip;
+
+namespace GameRes.Formats.VnMaker
+{
+ internal class ZipEntry : PackedEntry
+ {
+ public readonly SharpZip.ZipEntry NativeEntry;
+
+ public ZipEntry (SharpZip.ZipEntry zip_entry)
+ {
+ NativeEntry = zip_entry;
+ Name = zip_entry.Name;
+ Type = FormatCatalog.Instance.GetTypeFromName (zip_entry.Name);
+ IsPacked = true;
+ // design decision of having 32bit entry sizes was made early during GameRes
+ // library development. nevertheless, large files will be extracted correctly
+ // despite the fact that size is reported as uint.MaxValue, because extraction is
+ // performed by .Net framework based on real size value.
+ Size = (uint)Math.Min (zip_entry.CompressedSize, uint.MaxValue);
+ UnpackedSize = (uint)Math.Min (zip_entry.Size, uint.MaxValue);
+ Offset = zip_entry.Offset;
+ }
+ }
+
+ internal class PkZipArchive : ArcFile
+ {
+ readonly SharpZip.ZipFile m_zip;
+
+ public SharpZip.ZipFile Native { get { return m_zip; } }
+
+ public PkZipArchive (ArcView arc, ArchiveFormat impl, ICollection dir, SharpZip.ZipFile native)
+ : base (arc, impl, dir)
+ {
+ m_zip = native;
+ }
+
+ #region IDisposable implementation
+ bool _zip_disposed = false;
+ protected override void Dispose (bool disposing)
+ {
+ if (!_zip_disposed)
+ {
+ if (disposing)
+ m_zip.Close();
+ _zip_disposed = true;
+ }
+ base.Dispose (disposing);
+ }
+ #endregion
+ }
+
+ [Export(typeof(ArchiveFormat))]
+ public class ZipOpener : ArchiveFormat
+ {
+ public override string Tag { get { return "ZIP/VnMaker Encrypted ZIP Archive"; } }
+ public override string Description { get { return "VnMaker Encrypted ZIP Archive"; } }
+ public override uint Signature { get { return 0; } }
+ public override bool IsHierarchic { get { return true; } }
+ public override bool CanWrite { get { return false; } }
+
+ public ZipOpener ()
+ {
+ Settings = new[] { ZipEncoding };
+ Extensions = new string[] { "zip" };
+ }
+
+ readonly EncodingSetting ZipEncoding = new EncodingSetting ("ZIPEncodingCP", "DefaultEncoding");
+
+ public override ArcFile TryOpen (ArcView file)
+ {
+ var input = file.CreateStream ();
+ try
+ {
+ var zip = DeobfuscateStream (input, GuessEncryptionKey (input));
+ if ((zip.Signature & 0xFFFF) != 0x4B50) // 'PK'
+ throw new InvalidFormatException ();
+ return OpenZipArchive (file, zip.AsStream);
+ }
+ catch
+ {
+ input.Dispose ();
+ throw;
+ }
+ }
+
+ byte[] GuessEncryptionKey (IBinaryStream file)
+ {
+ return new byte[] { 0x0A, 0x2B, 0x36, 0x6F, 0x0B };
+ }
+
+ IBinaryStream DeobfuscateStream (IBinaryStream file, byte[] key)
+ {
+ var zip = new ByteStringEncryptedStream (file.AsStream, key, true);
+ return new BinaryStream (zip, file.Name);
+ }
+
+ internal ArcFile OpenZipArchive (ArcView file, Stream input)
+ {
+ SharpZip.ZipStrings.CodePage = Properties.Settings.Default.ZIPEncodingCP;
+ var zip = new SharpZip.ZipFile (input);
+ try
+ {
+ var files = zip.Cast().Where (z => !z.IsDirectory);
+ var dir = files.Select (z => new ZipEntry (z) as Entry).ToList();
+ return new PkZipArchive (file, this, dir, zip);
+ }
+ catch
+ {
+ zip.Close();
+ throw;
+ }
+ }
+
+ public override Stream OpenEntry (ArcFile arc, Entry entry)
+ {
+ var zarc = (PkZipArchive)arc;
+ var zent = (ZipEntry)entry;
+ return zarc.Native.GetInputStream (zent.NativeEntry);
+ }
+ }
+}
diff --git a/ArcFormats/VnMaker/AudioMP3.cs b/ArcFormats/VnMaker/AudioMP3.cs
new file mode 100644
index 00000000..4f194a0c
--- /dev/null
+++ b/ArcFormats/VnMaker/AudioMP3.cs
@@ -0,0 +1,38 @@
+using System.ComponentModel.Composition;
+
+namespace GameRes.Formats.VnMaker
+{
+ [Export(typeof(AudioFormat))]
+ public class AudioMP3 : Mp3Audio
+ {
+ public override string Tag { get { return "MP3/VnMaker Encrypted MP3 Audio"; } }
+ public override string Description { get { return "VnMaker Encrypted MP3 Audio"; } }
+ public override uint Signature { get { return 0; } }
+ public override bool CanWrite { get { return false; } }
+
+ public AudioMP3() : base()
+ {
+ Signatures = new uint[] { 0 };
+ Extensions = new[] { "mp3" };
+ }
+
+ public override SoundInput TryOpen (IBinaryStream file)
+ {
+ using (var input = DeobfuscateStream (file, GuessEncryptionKey (file)))
+ {
+ return base.TryOpen (input);
+ }
+ }
+
+ byte[] GuessEncryptionKey (IBinaryStream file)
+ {
+ return new byte[] { 0x0A, 0x2B, 0x36, 0x6F, 0x0B };
+ }
+
+ IBinaryStream DeobfuscateStream (IBinaryStream file, byte[] key)
+ {
+ var mp3 = new ByteStringEncryptedStream (file.AsStream, key, true);
+ return new BinaryStream (mp3, file.Name);
+ }
+ }
+}
diff --git a/ArcFormats/VnMaker/AudioOGG.cs b/ArcFormats/VnMaker/AudioOGG.cs
new file mode 100644
index 00000000..eb374030
--- /dev/null
+++ b/ArcFormats/VnMaker/AudioOGG.cs
@@ -0,0 +1,40 @@
+using System.ComponentModel.Composition;
+
+namespace GameRes.Formats.VnMaker
+{
+ [Export(typeof(AudioFormat))]
+ public class AudioOGG : AudioFormat
+ {
+ public override string Tag { get { return "OGG/VnMaker Encrypted OGG Audio"; } }
+ public override string Description { get { return "VnMaker Encrypted OGG Audio"; } }
+ public override uint Signature { get { return 0; } }
+ public override bool CanWrite { get { return false; } }
+
+ public AudioOGG() : base()
+ {
+ Signatures = new uint[] { 0 };
+ Extensions = new[] { "ogg" };
+ }
+
+ public override SoundInput TryOpen (IBinaryStream file)
+ {
+ using (var input = DeobfuscateStream (file, GuessEncryptionKey (file)))
+ {
+ if (input.Signature != 0x5367674F)
+ throw new InvalidFormatException ();
+ return new OggInput (input.AsStream);
+ }
+ }
+
+ byte[] GuessEncryptionKey (IBinaryStream file)
+ {
+ return new byte[] { 0x0A, 0x2B, 0x36, 0x6F, 0x0B };
+ }
+
+ IBinaryStream DeobfuscateStream (IBinaryStream file, byte[] key)
+ {
+ var ogg = new ByteStringEncryptedStream (file.AsStream, key, true);
+ return new BinaryStream (ogg, file.Name);
+ }
+ }
+}
diff --git a/ArcFormats/VnMaker/ImagePNG.cs b/ArcFormats/VnMaker/ImagePNG.cs
new file mode 100644
index 00000000..6e517f57
--- /dev/null
+++ b/ArcFormats/VnMaker/ImagePNG.cs
@@ -0,0 +1,61 @@
+using System.ComponentModel.Composition;
+using System.IO;
+
+namespace GameRes.Formats.VnMaker
+{
+ [Export(typeof(ImageFormat))]
+ public class PngFormat : GameRes.PngFormat
+ {
+ public override string Tag { get { return "PNG/VnMaker Encrypted PNG Image"; } }
+ public override string Description { get { return "VnMaker Encrypted PNG Image"; } }
+ public override uint Signature { get { return 0; } }
+ public override bool CanWrite { get { return true; } }
+
+ public PngFormat() : base()
+ {
+ Signatures = new uint[] { 0 };
+ Extensions = new[] { "png" };
+ }
+
+ public override ImageMetaData ReadMetaData (IBinaryStream file)
+ {
+ using (var input = DeobfuscateStream (file, GuessEncryptionKey (file)))
+ {
+ if (input.Signature != 0x474E5089)
+ throw new InvalidFormatException ();
+ return base.ReadMetaData (input);
+ }
+ }
+
+ public override ImageData Read (IBinaryStream file, ImageMetaData info)
+ {
+ using (var input = DeobfuscateStream (file, GuessEncryptionKey (file)))
+ {
+ if (input.Signature != 0x474E5089)
+ throw new InvalidFormatException ();
+ return base.Read (input, info);
+ }
+ }
+
+ byte[] GuessEncryptionKey (IBinaryStream file)
+ {
+ return new byte[] { 0x0A, 0x2B, 0x36, 0x6F, 0x0B };
+ }
+
+ IBinaryStream DeobfuscateStream (IBinaryStream file, byte[] key)
+ {
+ var png = new ByteStringEncryptedStream (file.AsStream, key, true);
+ return new BinaryStream (png, file.Name);
+ }
+
+ public override void Write (Stream file, ImageData image)
+ {
+ var ms = new MemoryStream ();
+ base.Write (ms, image);
+ ms.Position = 0;
+ var es = new ByteStringEncryptedStream (ms, GuessEncryptionKey (null));
+ es.CopyTo (file);
+ file.Flush ();
+ }
+ }
+}