(count);
+ for (int i = 0; i < count; ++i)
+ {
+ int name_length = file.View.ReadUInt16 (index_offset);
+ if (name_length > name_buffer.Length)
+ name_buffer = new byte[name_length];
+ if (name_length != file.View.Read (index_offset+2, name_buffer, 0, (uint)name_length))
+ return null;
+
+ int key = name_length + (pack_key ^ 0x3e);
+ for (int k = 0; k < name_length; ++k)
+ name_buffer[k] ^= (byte)(((k + 1) ^ key) + k + 1);
+
+ string name = Encodings.cp932.GetString (name_buffer, 0, name_length);
+ var entry = new QlieEntry { Name = name };
+ entry.Type = FormatCatalog.Instance.GetTypeFromName (name);
+
+ index_offset += 2 + name_length;
+ entry.Offset = file.View.ReadInt64 (index_offset);
+ entry.Size = file.View.ReadUInt32 (index_offset+8);
+ if (!entry.CheckPlacement (file.MaxOffset))
+ return null;
+ entry.UnpackedSize = file.View.ReadUInt32 (index_offset+12);
+ entry.IsPacked = 0 != file.View.ReadInt32 (index_offset+0x10);
+ entry.IsEncrypted = 0 != file.View.ReadInt32 (index_offset+0x14);
+ entry.Hash = file.View.ReadUInt32 (index_offset+0x18);
+ if (3 == pack_version)
+ entry.Key = (uint)pack_key;
+ else
+ entry.Key = 0;
+ dir.Add (entry);
+ index_offset += 0x1c;
+ }
+ return new ArcFile (file, this, dir);
+ }
+
+ public override Stream OpenEntry (ArcFile arc, Entry entry)
+ {
+ var qent = entry as QlieEntry;
+ if (null == qent || (!qent.IsEncrypted && !qent.IsPacked))
+ return arc.File.CreateStream (entry.Offset, entry.Size);
+ var data = new byte[entry.Size];
+ if (entry.Size != arc.File.View.Read (entry.Offset, data, 0, entry.Size))
+ return arc.File.CreateStream (entry.Offset, entry.Size);
+
+ if (qent.IsEncrypted)
+ Decrypt (data, 0, data.Length, qent.Key);
+ if (qent.IsPacked)
+ {
+ data = Decompress (data);
+ if (null == data) // probably wrong decryption
+ return arc.File.CreateStream (entry.Offset, entry.Size);
+ }
+ return new MemoryStream (data);
+ }
+
+ private void Decrypt (byte[] buffer, int offset, int length, uint key)
+ {
+ if (offset < 0)
+ throw new ArgumentOutOfRangeException ("offset");
+ if (length > buffer.Length || offset > buffer.Length - length)
+ throw new ArgumentOutOfRangeException ("length");
+
+ var table = new uint[2,2];
+ var xor = new uint[2];
+
+ for (int i = 0; i < 2; ++i)
+ {
+ table[0,i] = 0xA73C5F9Du;
+ table[1,i] = 0xCE24F523u;
+ xor[i] = ((uint)length + key) ^ 0xFEC9753Eu;
+ }
+ unsafe
+ {
+ fixed (byte* raw = buffer)
+ {
+ uint* encoded = (uint*)(raw + offset);
+ for (int i = 0; i < length / 8; ++i)
+ {
+ for (int k = 0; k < 2; ++k)
+ {
+ table[0,k] = (table[0,k] + table[1,k]) ^ xor[k];
+ *encoded ^= table[0,k];
+ xor[k] = *encoded++;
+ }
+ }
+ }
+ }
+ }
+
+ private byte[] Decompress (byte[] input)
+ {
+ if (LittleEndian.ToUInt32 (input, 0) != 0xFF435031)
+ return null;
+
+ bool is_16bit = 0 != (input[4] & 1);
+
+ var node = new byte[2,256];
+ var child_node = new byte[256];
+
+ int output_length = LittleEndian.ToInt32 (input, 8);
+ var output = new byte[output_length];
+
+ int src = 12;
+ int dst = 0;
+ while (src < input.Length)
+ {
+ int i, k, count, index;
+
+ for (i = 0; i < 256; i++)
+ node[0,i] = (byte)i;
+
+ for (i = 0; i < 256; )
+ {
+ count = input[src++];
+
+ if (count > 127)
+ {
+ int step = count - 127;
+ i += step;
+ count = 0;
+ }
+
+ if (i > 255)
+ break;
+
+ count++;
+ for (k = 0; k < count; k++)
+ {
+ node[0,i] = input[src++];
+ if (node[0,i] != i)
+ node[1,i] = input[src++];
+ i++;
+ }
+ }
+
+ if (is_16bit)
+ {
+ count = LittleEndian.ToUInt16 (input, src);
+ src += 2;
+ }
+ else
+ {
+ count = LittleEndian.ToInt32 (input, src);
+ src += 4;
+ }
+
+ k = 0;
+ for (;;)
+ {
+ if (k > 0)
+ index = child_node[--k];
+ else
+ {
+ if (0 == count)
+ break;
+ count--;
+ index = input[src++];
+ }
+
+ if (node[0,index] == index)
+ output[dst++] = (byte)index;
+ else
+ {
+ child_node[k++] = node[1,index];
+ child_node[k++] = node[0,index];
+ }
+ }
+ }
+ if (dst != output.Length)
+ return null;
+
+ return output;
+ }
+ }
+}
diff --git a/ArcFormats/Properties/AssemblyInfo.cs b/ArcFormats/Properties/AssemblyInfo.cs
index 451c7186..2d1d7ed4 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.6.63")]
-[assembly: AssemblyFileVersion ("1.0.6.63")]
+[assembly: AssemblyVersion ("1.0.6.64")]
+[assembly: AssemblyFileVersion ("1.0.6.64")]
diff --git a/supported.html b/supported.html
index 29e552e9..ae68f70c 100644
--- a/supported.html
+++ b/supported.html
@@ -26,6 +26,7 @@ Kana ~Imouto~
Natsu no Hitoshizuku
Private Nurse
Sensei 2
+Shoujo Settai
| *.ggd | \xB9\xAA\xB3\xB3 \xAB\xAD\xAA\xBA \xB7\xB6\xB8\xB7 \xCD\xCA\xC9\xB8 | Yes |
*.gg1 *.gg2 *.gg3 *.gg0 | GGA00000 | No |
@@ -190,8 +191,8 @@ Yatohime Zankikou
Onna Kyoushi
*.bg_ *.cg_ | AP | Yes |
-| *.dpk | DPK | No | DAC | Yumemiru Tsuki no Lunalutia |
-| *.dgc | DGC | No |
+| *.dpk | DPK | No | DAC | Yumemiru Tsuki no Lunalutia |
+| *.dgc | DGC | No |
| *.pck | - | No | Crowd |
X Change R
X Change
@@ -201,6 +202,9 @@ X Change 2
|
| *.cwd | cwd | No |
| *.eog | CRM | No |
*.zbm *.cwl | SZDD | No |
+| *.pack | FilePackVer2.0 | No | QLIE |
+Mehime no Toriko
+ |
[1] Non-encrypted only