diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj
index c0c6a083..766150e6 100644
--- a/ArcFormats/ArcFormats.csproj
+++ b/ArcFormats/ArcFormats.csproj
@@ -150,6 +150,7 @@
+
diff --git a/ArcFormats/SuperNekoX/ArcGPC.cs b/ArcFormats/SuperNekoX/ArcGPC.cs
new file mode 100644
index 00000000..810427a3
--- /dev/null
+++ b/ArcFormats/SuperNekoX/ArcGPC.cs
@@ -0,0 +1,257 @@
+//! \file ArcGPC.cs
+//! \date Tue Mar 22 01:38:13 2016
+//! \brief Super Neko X engine resource archive.
+//
+// Copyright (C) 2016 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 GameRes.Utility;
+
+namespace GameRes.Formats.SuperNekoX
+{
+ [Export(typeof(ArchiveFormat))]
+ public class GpcOpener : ArchiveFormat
+ {
+ public override string Tag { get { return "GPC7"; } }
+ public override string Description { get { return "Super NekoX engine resource archive"; } }
+ public override uint Signature { get { return 0x37637047; } } // 'Gpc7'
+ public override bool IsHierarchic { get { return false; } }
+ public override bool CanCreate { get { return false; } }
+
+ public GpcOpener ()
+ {
+ Extensions = new string[] { "gpc" };
+ }
+
+ public override ArcFile TryOpen (ArcView file)
+ {
+ int count = file.View.ReadInt32 (4);
+ if (!IsSaneCount (count))
+ return null;
+
+ var base_name = Path.GetFileNameWithoutExtension (file.Name);
+ var dir = new List (count);
+ int index_offset = 8;
+ long data_offset = count * 4 + 8;
+ uint next_offset = file.View.ReadUInt32 (index_offset);
+ for (int i = 0; i < count; ++i)
+ {
+ index_offset += 4;
+ var entry = new PackedEntry { Offset = next_offset };
+ next_offset = i + 1 < count ? file.View.ReadUInt32 (index_offset) : (uint)file.MaxOffset;
+ entry.Size = next_offset - (uint)entry.Offset;
+ if (entry.Offset < data_offset || !entry.CheckPlacement (file.MaxOffset))
+ return null;
+ entry.Name = string.Format ("{0}#{1:D4}", base_name, i);
+ dir.Add (entry);
+ }
+ DetectFileTypes (file, dir);
+ return new ArcFile (file, this, dir);
+ }
+
+ public override Stream OpenEntry (ArcFile arc, Entry entry)
+ {
+ var pent = entry as PackedEntry;
+ Stream input = arc.File.CreateStream (entry.Offset, entry.Size);
+ if (null != pent && pent.IsPacked)
+ {
+ Stream unpacked;
+ using (input)
+ {
+ var data = new byte[pent.UnpackedSize];
+ UnpackEntry (input, data);
+ unpacked = new MemoryStream (data);
+ }
+ input = unpacked;
+ }
+ if (input.Length > 4 && input.Length < 0x10000)
+ {
+ using (var reader = new ArcView.Reader (input))
+ {
+ int unpacked_size = reader.ReadUInt16();
+ int packed_size = reader.ReadUInt16();
+ if (packed_size == input.Length-4)
+ {
+ using (input)
+ {
+ var data = new byte[unpacked_size];
+ UnpackLz77 (input, data);
+ return new MemoryStream (data);
+ }
+ }
+ input.Position = 0;
+ }
+ }
+ return input;
+ }
+
+ void DetectFileTypes (ArcView file, List dir)
+ {
+ using (var input = file.CreateStream())
+ using (var reader = new ArcView.Reader (input))
+ {
+ var buffer = new byte[0x10];
+ foreach (PackedEntry entry in dir)
+ {
+ input.Position = entry.Offset;
+ uint packed_size = reader.ReadUInt32();
+ entry.UnpackedSize = reader.ReadUInt32();
+ entry.Offset += 8;
+ if (0 == packed_size)
+ {
+ entry.Size = entry.UnpackedSize;
+ }
+ else
+ {
+ entry.IsPacked = true;
+ entry.Size = packed_size;
+ }
+ if (entry.Size < 0x10)
+ continue;
+ uint signature;
+ if (entry.IsPacked)
+ {
+ UnpackEntry (input, buffer);
+ signature = LittleEndian.ToUInt32 (buffer, 0);
+ }
+ else
+ signature = reader.ReadUInt32();
+ IResource res;
+ if (0x020000 == signature)
+ res = ImageFormat.Tga;
+ else
+ res = AutoEntry.DetectFileType (signature);
+ if (null != res)
+ {
+ entry.Type = res.Type;
+ var ext = res.Extensions.FirstOrDefault();
+ if (!string.IsNullOrEmpty (ext))
+ entry.Name = Path.ChangeExtension (entry.Name, ext);
+ }
+ }
+ }
+ }
+
+ void UnpackEntry (Stream input, byte[] output)
+ {
+ int dst = 0;
+ while (dst < output.Length)
+ {
+ int ctl = input.ReadByte();
+ if (-1 == ctl)
+ break;
+ int count, offset;
+ if (ctl >= 0x20)
+ {
+ if (ctl >= 0x80)
+ {
+ count = (ctl >> 5) & 3;
+ offset = (ctl & 0x1F) << 8;
+ offset |= input.ReadByte();
+ }
+ else if ((ctl & 0x60) == 0x20)
+ {
+ offset = (ctl >> 2) & 7;
+ count = ctl & 3;
+ }
+ else if ((ctl & 0x60) == 0x40)
+ {
+ offset = (ctl & 0x1F) << 8;
+ offset |= input.ReadByte();
+ count = input.ReadByte() + 4;
+ }
+ else
+ {
+ offset = (ctl & 0x1F) << 8 | input.ReadByte();
+ count = input.ReadByte() << 24;
+ count |= input.ReadByte() << 16;
+ count |= input.ReadByte() << 8;
+ count |= input.ReadByte();
+ }
+ count = Math.Min (count + 3, output.Length-dst);
+ Binary.CopyOverlapped (output, dst-offset-1, dst, count);
+ }
+ else
+ {
+ if (ctl < 0x1D)
+ {
+ count = ctl + 1;
+ }
+ else if (0x1D == ctl)
+ {
+ count = input.ReadByte() + 0x1E;
+ }
+ else if (0x1E == ctl)
+ {
+ count = input.ReadByte() << 8;
+ count |= input.ReadByte();
+ count += 286;
+ }
+ else
+ {
+ count = input.ReadByte() << 24;
+ count |= input.ReadByte() << 16;
+ count |= input.ReadByte() << 8;
+ count |= input.ReadByte();
+ }
+ count = Math.Min (count, output.Length-dst);
+ input.Read (output, dst, count);
+ }
+ dst += count;
+ }
+ }
+
+ void UnpackLz77 (Stream input, byte[] output)
+ {
+ int dst = 0;
+ int mask = 0;
+ int bits = 0;
+ while (dst < output.Length)
+ {
+ mask >>= 1;
+ if (0 == mask)
+ {
+ bits = input.ReadByte();
+ if (-1 == bits)
+ break;
+ mask = 0x80;
+ }
+ if (0 != (bits & mask))
+ {
+ int count = input.ReadByte();
+ int offset = input.ReadByte() << 4 | count >> 4;
+ count = Math.Min ((count & 0xf) + 3, output.Length - dst);
+ Binary.CopyOverlapped (output, dst-offset-1, dst, count);
+ dst += count;
+ }
+ else
+ {
+ output[dst++] = (byte)input.ReadByte();
+ }
+ }
+ }
+ }
+}
diff --git a/supported.html b/supported.html
index 46181cd8..3e1c642a 100644
--- a/supported.html
+++ b/supported.html
@@ -425,8 +425,9 @@ X Change R
X Change
X Change 2
X Change 2R
-Ryoujoku Gojuusou
+Eve to Iu Na no Omocha
Hissatsu Chikannin II
+Ryoujoku Gojuusou
Tokumei Sentai Sirenger
Tokumei Sentai Yuzu Ranger
@@ -758,6 +759,9 @@ Ase Nure Shoujo Misaki "Anata no Nioi de Icchau!"
*.grp | - | No | Ankh |
Mozu
|
+*.gpc | Gpc7 | No | Super NekoX |
+Sister Contrast!
+ |
Non-encrypted only