diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj
index d09fd8bb..6a74580f 100644
--- a/ArcFormats/ArcFormats.csproj
+++ b/ArcFormats/ArcFormats.csproj
@@ -178,6 +178,7 @@
+
diff --git a/ArcFormats/ShiinaRio/ArcWARC.cs b/ArcFormats/ShiinaRio/ArcWARC.cs
index 0fedd227..d1a2aba7 100644
--- a/ArcFormats/ShiinaRio/ArcWARC.cs
+++ b/ArcFormats/ShiinaRio/ArcWARC.cs
@@ -2,7 +2,7 @@
//! \date Fri Apr 10 03:10:42 2015
//! \brief ShiinaRio engine archive format.
//
-// Copyright (C) 2015 by morkt
+// Copyright (C) 2015-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
@@ -28,7 +28,6 @@ using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
-using System.Runtime.InteropServices;
using GameRes.Compression;
using GameRes.Formats.Properties;
using GameRes.Formats.Strings;
@@ -160,8 +159,8 @@ namespace GameRes.Formats.ShiinaRio // 椎名里緒
if (0 != (wentry.Flags & 0x80000000u) && entry.Size > 8) // encrypted entry
warc.Decoder.Decrypt (enc_data, 8, entry.Size-8);
- if (warc.Decoder.SchemeVersion >= 2490)
- warc.Decoder.DecryptExtra (enc_data, 8, entry.Size-8, 0x202);
+ if (warc.Decoder.ExtraCrypt != null)
+ warc.Decoder.ExtraCrypt.Decrypt (enc_data, 8, entry.Size-8, 0x202);
if (0 != (wentry.Flags & 0x20000000u) && entry.Size > 8)
warc.Decoder.Decrypt2 (enc_data, 8, entry.Size-8);
@@ -185,8 +184,8 @@ namespace GameRes.Formats.ShiinaRio // 椎名里緒
unpack (enc_data, unpacked);
if (0 != (wentry.Flags & 0x40000000))
warc.Decoder.Decrypt2 (unpacked, 0, (uint)unpacked.Length);
- if (warc.Decoder.SchemeVersion >= 2490)
- warc.Decoder.DecryptExtra (unpacked, 0, (uint)unpacked.Length, 0x204);
+ if (warc.Decoder.ExtraCrypt != null)
+ warc.Decoder.ExtraCrypt.Decrypt (unpacked, 0, (uint)unpacked.Length, 0x204);
}
return new MemoryStream (unpacked);
}
@@ -545,602 +544,4 @@ namespace GameRes.Formats.ShiinaRio // 椎名里緒
return i;
}
}
-
- [Serializable]
- public class EncryptionScheme
- {
- public string Name { get; set; }
- public string OriginalTitle { get; set; }
- public int Version { get; set; }
- public int EntryNameSize;
- public byte[] CryptKey;
- public uint[] HelperKey { get; set; }
- public byte[] ShiinaImage;
- public byte[] Region;
- public byte[] DecodeBin;
- public byte[] DecodeExtra;
- }
-
- internal class Decoder
- {
- EncryptionScheme m_scheme;
-
- public int SchemeVersion { get { return m_scheme.Version; } }
- public int WarcVersion { get; private set; }
- public uint MaxIndexLength { get; private set; }
- public int EntryNameSize { get { return m_scheme.EntryNameSize; } }
-
- private uint Rand { get; set; }
-
- public Decoder (int version, EncryptionScheme scheme)
- {
- m_scheme = scheme;
- WarcVersion = version;
- MaxIndexLength = GetMaxIndexLength (version);
- }
-
- public void Decrypt (byte[] data, int index, uint data_length)
- {
- if (data_length < 3)
- return;
- uint effective_length = Math.Min (data_length, 1024u);
- int a, b;
- uint fac = 0;
- if (WarcVersion > 120)
- {
- Rand = data_length;
- a = (sbyte)data[index] ^ (sbyte)data_length;
- b = (sbyte)data[index+1] ^ (sbyte)(data_length / 2);
- if (data_length != MaxIndexLength)
- {
- // ... regular entry decryption
- int idx = (int)((double)NextRand() * (m_scheme.ShiinaImage.Length / 4294967296.0));
- if (WarcVersion >= 160)
- {
- fac = Rand + m_scheme.ShiinaImage[idx];
- fac = DecryptHelper3 (fac) & 0xfffffff;
- if (effective_length > 0x80)
- {
- DecryptHelper4 (data, index+4, m_scheme.HelperKey);
- index += 0x80;
- effective_length -= 0x80;
- }
- }
- else if (150 == WarcVersion)
- {
- fac = Rand + m_scheme.ShiinaImage[idx];
- fac ^= (fac & 0xfff) * (fac & 0xfff);
- uint v = 0;
- for (int i = 0; i < 32; ++i)
- {
- uint bit = fac & 1;
- fac >>= 1;
- if (0 != bit)
- v += fac;
- }
- fac = v;
- }
- else if (140 == WarcVersion)
- {
- fac = m_scheme.ShiinaImage[idx];
- }
- else if (130 == WarcVersion)
- {
- fac = m_scheme.ShiinaImage[idx & 0xff];
- }
- }
- }
- else
- {
- a = data[index];
- b = data[index+1];
- }
- Rand ^= (uint)(DecryptHelper1 (a) * 100000000.0);
-
- double token = 0.0;
- if (0 != (a|b))
- {
- token = Math.Acos ((double)a / Math.Sqrt ((double)(a*a + b*b)));
- token = token / Math.PI * 180.0;
- }
- if (b < 0)
- token = 360.0 - token;
-
- uint x = (fac + (byte)DecryptHelper2 (token)) % (uint)m_scheme.CryptKey.Length;
- int n = 0;
- for (int i = 2; i < effective_length; ++i)
- {
- byte d = data[index+i];
- if (WarcVersion > 120)
- d ^= (byte)((double)NextRand() / 16777216.0);
- else
- d ^= (byte)((double)NextRand() / 4294967296.0); // ? effectively a no-op
- d = (byte)(((d & 1) << 7) | (d >> 1));
- d ^= (byte)(m_scheme.CryptKey[n++] ^ m_scheme.CryptKey[x]);
- data[index+i] = d;
- x = d % (uint)m_scheme.CryptKey.Length;
- if (n >= m_scheme.CryptKey.Length)
- n = 0;
- }
- }
-
- public void DecryptIndex (uint index_offset, byte[] enc_index)
- {
- Decrypt (enc_index, 0, (uint)enc_index.Length);
- unsafe
- {
- fixed (byte* buf_raw = enc_index)
- {
- uint* encoded = (uint*)buf_raw;
- for (int i = 0; i < enc_index.Length/4; ++i)
- encoded[i] ^= index_offset;
- if (WarcVersion >= 170)
- {
- byte key = (byte)~WarcVersion;
- for (int i = 0; i < enc_index.Length; ++i)
- buf_raw[i] ^= key;
- }
- }
- }
- }
-
- public void Decrypt2 (byte[] data, int index, uint length)
- {
- if (length < 0x400 || null == m_scheme.DecodeBin)
- return;
- uint crc = 0xffffffff;
- for (int i = 0; i < 0x100; ++i)
- {
- crc ^= (uint)data[index++] << 24;
- for (int j = 0; j < 8; ++j)
- {
- uint bit = crc & 0x80000000u;
- crc <<= 1;
- if (0 != bit)
- crc ^= 0x04c11db7;
- }
- }
- for (int i = 0; i < 0x40; ++i)
- {
- uint src = LittleEndian.ToUInt32 (data, index) & 0x1ffcu;
- src = LittleEndian.ToUInt32 (m_scheme.DecodeBin, (int)src);
- uint key = src ^ crc;
- data[index++ + 0x100] ^= (byte)key;
- data[index++ + 0x100] ^= (byte)(key >> 8);
- data[index++ + 0x100] ^= (byte)(key >> 16);
- data[index++ + 0x100] ^= (byte)(key >> 24);
- }
- }
-
- public void Decrypt3 (byte[] data, int index, uint length)
- {
- if (length < 0x400)
- return;
- int src = index;
- uint key = 0;
- for (uint i = (length & 0x7eu) + 1; i != 0; --i)
- {
- key ^= data[src++];
- for (int j = 0; j < 8; ++j)
- {
- uint bit = key & 1;
- key = bit << 15 | key >> 1;
- if (0 == bit)
- key ^= 0x408;
- }
- }
- data[index+0x104] ^= (byte)key;
- data[index+0x105] ^= (byte)(key >> 8);
- }
-
- public void DecryptExtra (byte[] data, int index, uint length, uint flags)
- {
- if (length < 0x400 || null == m_scheme.DecodeExtra)
- return;
- uint key = 0xECB2F5B2;
- uint k0 = key + 1;
- uint k3 = k0 + 2;
- uint k2 = k0 + 1;
- uint k1 = k0 + 3;
- if ((flags & 0x202) == 0x202)
- {
- for (int i = 0; i < 0xFF; ++i)
- {
- uint j = k3 ^ (k3 << 11) ^ k0 ^ ((k3 ^ (k3 << 11) ^ (k0 >> 11)) >> 8);
- k3 = k2;
- k2 = k1;
- k1 = k0;
- k0 = j;
- data[index + i] ^= m_scheme.DecodeExtra[j % m_scheme.DecodeExtra.Length];
- }
- }
- if ((flags & 0x204) == 0x204)
- {
- data[index + 0x200] ^= (byte)key;
- data[index + 0x201] ^= (byte)(key >> 8);
- data[index + 0x202] ^= (byte)(key >> 16);
- data[index + 0x203] ^= (byte)(key >> 24);
- }
- }
-
- double DecryptHelper1 (double a)
- {
- if (a < 0)
- return -DecryptHelper1 (-a);
-
- double v0;
- double v1;
- if (a < 18.0)
- {
- v0 = a;
- v1 = a;
- double v2 = -(a * a);
-
- for (int j = 3; j < 1000; j += 2)
- {
- v1 *= v2 / (j * (j - 1));
- v0 += v1 / j;
- if (v0 == v2)
- break;
- }
- return v0;
- }
-
- int flags = 0;
- double v0_l = 0;
- v1 = 0;
- double div = 1 / a;
- double v1_h = 2.0;
- double v0_h = 2.0;
- double v1_l = 0;
- v0 = 0;
- int i = 0;
-
- do
- {
- v0 += div;
- div *= ++i / a;
- if (v0 < v0_h)
- v0_h = v0;
- else
- flags |= 1;
-
- v1 += div;
- div *= ++i / a;
- if (v1 < v1_h)
- v1_h = v1;
- else
- flags |= 2;
-
- v0 -= div;
- div *= ++i / a;
- if (v0 > v0_l)
- v0_l = v0;
- else
- flags |= 4;
-
- v1 -= div;
- div *= ++i / a;
- if (v1 > v1_l)
- v1_l = v1;
- else
- flags |= 8;
- }
- while (flags != 0xf);
-
- return ((Math.PI - Math.Cos(a) * (v0_l + v0_h)) - (Math.Sin(a) * (v1_l + v1_h))) / 2.0;
- }
-
- uint DecryptHelper2 (double a)
- {
- double v0, v1, v2, v3;
-
- if (a > 1.0)
- {
- v0 = Math.Sqrt (a * 2 - 1);
- for (;;)
- {
- v1 = 1 - (double)NextRand() / 4294967296.0;
- v2 = 2.0 * (double)NextRand() / 4294967296.0 - 1.0;
- if (v1 * v1 + v2 * v2 > 1.0)
- continue;
-
- v2 /= v1;
- v3 = v2 * v0 + a - 1.0;
- if (v3 <= 0)
- continue;
-
- v1 = (a - 1.0) * Math.Log (v3 / (a - 1.0)) - v2 * v0;
- if (v1 < -50.0)
- continue;
-
- if (((double)NextRand() / 4294967296.0) <= (Math.Exp(v1) * (v2 * v2 + 1.0)))
- break;
- }
- }
- else
- {
- v0 = Math.Exp(1.0) / (a + Math.Exp(1.0));
- do
- {
- v1 = (double)NextRand() / 4294967296.0;
- v2 = (double)NextRand() / 4294967296.0;
- if (v1 < v0)
- {
- v3 = Math.Pow(v2, 1.0 / a);
- v1 = Math.Exp(-v3);
- } else
- {
- v3 = 1.0 - Math.Log(v2);
- v1 = Math.Pow(v3, a - 1.0);
- }
- }
- while ((double)NextRand() / 4294967296.0 >= v1);
- }
-
- if (WarcVersion > 120)
- return (uint)(v3 * 256.0);
- else
- return (byte)((double)NextRand() / 4294967296.0);
- }
-
- [StructLayout(LayoutKind.Explicit)]
- struct Union
- {
- [FieldOffset(0)]
- public int i;
- [FieldOffset (0)]
- public uint u;
- [FieldOffset(0)]
- public float f;
- [FieldOffset(0)]
- public byte b0;
- [FieldOffset(1)]
- public byte b1;
- [FieldOffset(2)]
- public byte b2;
- [FieldOffset(3)]
- public byte b3;
- }
-
- uint DecryptHelper3 (uint key)
- {
- var p = new Union();
- p.u = key;
- var fv = new Union();
- fv.f = (float)(1.5 * (double)p.b0 + 0.1);
- uint v0 = Binary.BigEndian (fv.u);
- fv.f = (float)(1.5 * (double)p.b1 + 0.1);
- uint v1 = (uint)fv.f;
- fv.f = (float)(1.5 * (double)p.b2 + 0.1);
- uint v2 = (uint)-fv.i;
- fv.f = (float)(1.5 * (double)p.b3 + 0.1);
- uint v3 = ~fv.u;
-
- return ((v0 + v1) | (v2 - v3));
- }
-
- void DecryptHelper4 (byte[] data, int index, uint[] key_src)
- {
- uint[] buf = new uint[0x50];
- int i;
- for (i = 0; i < 0x10; ++i)
- {
- buf[i] = BigEndian.ToUInt32 (data, index+40+4*i);
- }
- for (; i < 0x50; ++i)
- {
- uint v = buf[i-16];
- v ^= buf[i-14];
- v ^= buf[i-8];
- v ^= buf[i-3];
- v = v << 1 | v >> 31;
- buf[i] = v;
- }
- uint[] key = new uint[10];
- Array.Copy (key_src, key, 5);
- uint k0 = key[0];
- uint k1 = key[1];
- uint k2 = key[2];
- uint k3 = key[3];
- uint k4 = key[4];
-
- uint pc = 0;
- uint v26 = 0;
- int buf_idx = 0;
- for (int ebp = 0; ebp < 0x50; ++ebp)
- {
- if (ebp >= 0x10)
- {
- if (ebp >= 0x20)
- {
- if (ebp >= 0x30)
- {
- uint v27 = ~k3;
- if (ebp >= 0x40)
- {
- v26 = k1 ^ (k2 | v27);
- pc = 0xA953FD4E;
- }
- else
- {
- v26 = k1 & k3 | k2 & v27;
- pc = 0x8F1BBCDC;
- }
- }
- else
- {
- v26 = k3 ^ (k1 | ~k2);
- pc = 0x6ED9EBA1;
- }
- }
- else
- {
- v26 = k1 & k2 | k3 & ~k1;
- pc = 0x5A827999;
- }
- }
- else
- {
- v26 = k1 ^ k2 ^ k3;
- pc = 0;
- }
- uint v28 = buf[buf_idx] + k4 + v26 + pc + (k0 << 5 | k0 >> 27);
- uint v29 = (k1 >> 2) | (k1 << 30);
- k1 = k0;
- k4 = k3;
- k3 = k2;
- k2 = v29;
- k0 = v28;
- ++buf_idx;
- }
- key[0] += k0;
- key[1] += k1;
- key[2] += k2;
- key[3] += k3;
- key[4] += k4;
- var ft = new FILETIME {
- DateTimeLow = key[1],
- DateTimeHigh = key[0] & 0x7FFFFFFF
- };
- var sys_time = new SYSTEMTIME (ft);
- key[5] = (uint)(sys_time.Year | sys_time.Month << 16);
- key[7] = (uint)(sys_time.Hour | sys_time.Minute << 16);
- key[8] = (uint)(sys_time.Second | sys_time.Milliseconds << 16);
-
- uint flags = LittleEndian.ToUInt32 (data, index+40) | 0x80000000;
-// uint rgb = BigEndian.ToUInt32 (data, index+44) >> 8;
- uint rgb = buf[1] >> 8;
- if (0 == (flags & 0x78000000))
- flags |= 0x98000000;
- key[6] = RegionCrc32 (m_scheme.Region, flags, rgb);
- key[9] = (uint)(((int)key[2] * (long)(int)key[3]) >> 8);
- if (m_scheme.Version >= 2390)
- key[6] += key[9];
- unsafe
- {
- fixed (byte* data_fixed = data)
- {
- uint* encoded = (uint*)(data_fixed+index);
- for (i = 0; i < 10; ++i)
- {
- encoded[i] ^= key[i];
- }
- }
- }
- }
-
- static readonly uint[] CustomCrcTable = InitCrcTable();
-
- static uint[] InitCrcTable ()
- {
- var table = new uint[0x100];
- for (uint i = 0; i != 256; ++i)
- {
- uint poly = i;
- for (int j = 0; j < 8; ++j)
- {
- uint bit = poly & 1;
- poly = poly >> 1 | poly << 31; // ror 1
- if (0 == bit)
- poly ^= 0x6DB88320;
- }
- table[i] = poly;
- }
- return table;
- }
-
- uint RegionCrc32 (byte[] src, uint flags, uint rgb)
- {
- int src_alpha = (int)flags & 0x1ff;
- int dst_alpha = (int)(flags >> 12) & 0x1ff;
- flags >>= 24;
- if (0 == (flags & 0x10))
- dst_alpha = 0;
- if (0 == (flags & 8))
- src_alpha = 0x100;
- int y_step = 0;
- int x_step = 4;
- int width = 48;
- int pos = 0;
- if (0 != (flags & 0x40)) // horizontal flip
- {
- y_step += width;
- pos += (width-1)*4;
- x_step = -x_step;
- }
- if (0 != (flags & 0x20)) // vertical flip
- {
- y_step -= width;
- pos += width*0x2f*4; // width*(height-1)*4;
- }
- y_step <<= 3;
- uint checksum = 0;
- for (int y = 0; y < 48; ++y)
- {
- for (int x = 0; x < 48; ++x)
- {
- int alpha = src[pos+3] * src_alpha;
- alpha >>= 8;
- uint color = rgb;
- for (int i = 0; i < 3; ++i)
- {
- int v = src[pos+i];
- int c = (int)(color & 0xff); // rgb[i];
- c -= v;
- c = (c * dst_alpha) >> 8;
- c = (c + v) & 0xff;
- c = (c * alpha) >> 8;
- checksum = (checksum >> 8) ^ CustomCrcTable[(c ^ checksum) & 0xff];
- color >>= 8;
- }
- pos += x_step;
- }
- pos += y_step;
- }
- return checksum;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- private struct FILETIME
- {
- public uint DateTimeLow;
- public uint DateTimeHigh;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- private struct SYSTEMTIME
- {
- [MarshalAs(UnmanagedType.U2)] public ushort Year;
- [MarshalAs(UnmanagedType.U2)] public ushort Month;
- [MarshalAs(UnmanagedType.U2)] public ushort DayOfWeek;
- [MarshalAs(UnmanagedType.U2)] public ushort Day;
- [MarshalAs(UnmanagedType.U2)] public ushort Hour;
- [MarshalAs(UnmanagedType.U2)] public ushort Minute;
- [MarshalAs(UnmanagedType.U2)] public ushort Second;
- [MarshalAs(UnmanagedType.U2)] public ushort Milliseconds;
-
- public SYSTEMTIME (FILETIME ft)
- {
- FileTimeToSystemTime (ref ft, out this);
- }
-
- [DllImport ("kernel32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
- static extern bool FileTimeToSystemTime (ref FILETIME lpFileTime, out SYSTEMTIME lpSystemTime);
- }
-
- uint NextRand ()
- {
- Rand = 1566083941u * Rand + 1u;
- return Rand;
- }
-
- uint GetMaxIndexLength (int version)
- {
- int max_index_entries = version < 150 ? 8192 : 16384;
- return (uint)((m_scheme.EntryNameSize + 0x18) * max_index_entries);
- }
-
- public static EncryptionScheme[] KnownSchemes = new EncryptionScheme[0];
- }
}
diff --git a/ArcFormats/ShiinaRio/WarcEncryption.cs b/ArcFormats/ShiinaRio/WarcEncryption.cs
new file mode 100644
index 00000000..111b5d0b
--- /dev/null
+++ b/ArcFormats/ShiinaRio/WarcEncryption.cs
@@ -0,0 +1,682 @@
+//! \file WarcEncryption.cs
+//! \date Mon Aug 15 08:56:04 2016
+//! \brief ShiinaRio archives encryption.
+//
+// Copyright (C) 2015-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.Runtime.InteropServices;
+using GameRes.Utility;
+
+namespace GameRes.Formats.ShiinaRio
+{
+ [Serializable]
+ public class EncryptionScheme
+ {
+ public string Name { get; set; }
+ public string OriginalTitle { get; set; }
+ public int Version { get; set; }
+ public int EntryNameSize;
+ public byte[] CryptKey;
+ public uint[] HelperKey;
+ public byte[] ShiinaImage;
+ public byte[] Region;
+ public byte[] DecodeBin;
+ public IDecryptExtra ExtraCrypt;
+ }
+
+ internal class Decoder
+ {
+ EncryptionScheme m_scheme;
+
+ public int SchemeVersion { get { return m_scheme.Version; } }
+ public int WarcVersion { get; private set; }
+ public uint MaxIndexLength { get; private set; }
+ public int EntryNameSize { get { return m_scheme.EntryNameSize; } }
+ public IDecryptExtra ExtraCrypt { get { return m_scheme.ExtraCrypt; } }
+
+ private uint Rand { get; set; }
+
+ public Decoder (int version, EncryptionScheme scheme)
+ {
+ m_scheme = scheme;
+ WarcVersion = version;
+ MaxIndexLength = GetMaxIndexLength (version);
+ }
+
+ public void Decrypt (byte[] data, int index, uint data_length)
+ {
+ if (data_length < 3)
+ return;
+ uint effective_length = Math.Min (data_length, 1024u);
+ int a, b;
+ uint fac = 0;
+ if (WarcVersion > 120)
+ {
+ Rand = data_length;
+ a = (sbyte)data[index] ^ (sbyte)data_length;
+ b = (sbyte)data[index+1] ^ (sbyte)(data_length / 2);
+ if (data_length != MaxIndexLength)
+ {
+ // ... regular entry decryption
+ int idx = (int)((double)NextRand() * (m_scheme.ShiinaImage.Length / 4294967296.0));
+ if (WarcVersion >= 160)
+ {
+ fac = Rand + m_scheme.ShiinaImage[idx];
+ fac = DecryptHelper3 (fac) & 0xfffffff;
+ if (effective_length > 0x80)
+ {
+ DecryptHelper4 (data, index+4, m_scheme.HelperKey);
+ index += 0x80;
+ effective_length -= 0x80;
+ }
+ }
+ else if (150 == WarcVersion)
+ {
+ fac = Rand + m_scheme.ShiinaImage[idx];
+ fac ^= (fac & 0xfff) * (fac & 0xfff);
+ uint v = 0;
+ for (int i = 0; i < 32; ++i)
+ {
+ uint bit = fac & 1;
+ fac >>= 1;
+ if (0 != bit)
+ v += fac;
+ }
+ fac = v;
+ }
+ else if (140 == WarcVersion)
+ {
+ fac = m_scheme.ShiinaImage[idx];
+ }
+ else if (130 == WarcVersion)
+ {
+ fac = m_scheme.ShiinaImage[idx & 0xff];
+ }
+ }
+ }
+ else
+ {
+ a = data[index];
+ b = data[index+1];
+ }
+ Rand ^= (uint)(DecryptHelper1 (a) * 100000000.0);
+
+ double token = 0.0;
+ if (0 != (a|b))
+ {
+ token = Math.Acos ((double)a / Math.Sqrt ((double)(a*a + b*b)));
+ token = token / Math.PI * 180.0;
+ }
+ if (b < 0)
+ token = 360.0 - token;
+
+ uint x = (fac + (byte)DecryptHelper2 (token)) % (uint)m_scheme.CryptKey.Length;
+ int n = 0;
+ for (int i = 2; i < effective_length; ++i)
+ {
+ byte d = data[index+i];
+ if (WarcVersion > 120)
+ d ^= (byte)((double)NextRand() / 16777216.0);
+ else
+ d ^= (byte)((double)NextRand() / 4294967296.0); // ? effectively a no-op
+ d = (byte)(((d & 1) << 7) | (d >> 1));
+ d ^= (byte)(m_scheme.CryptKey[n++] ^ m_scheme.CryptKey[x]);
+ data[index+i] = d;
+ x = d % (uint)m_scheme.CryptKey.Length;
+ if (n >= m_scheme.CryptKey.Length)
+ n = 0;
+ }
+ }
+
+ public void DecryptIndex (uint index_offset, byte[] enc_index)
+ {
+ Decrypt (enc_index, 0, (uint)enc_index.Length);
+ unsafe
+ {
+ fixed (byte* buf_raw = enc_index)
+ {
+ uint* encoded = (uint*)buf_raw;
+ for (int i = 0; i < enc_index.Length/4; ++i)
+ encoded[i] ^= index_offset;
+ if (WarcVersion >= 170)
+ {
+ byte key = (byte)~WarcVersion;
+ for (int i = 0; i < enc_index.Length; ++i)
+ buf_raw[i] ^= key;
+ }
+ }
+ }
+ }
+
+ public void Decrypt2 (byte[] data, int index, uint length)
+ {
+ if (length < 0x400 || null == m_scheme.DecodeBin)
+ return;
+ uint crc = 0xffffffff;
+ for (int i = 0; i < 0x100; ++i)
+ {
+ crc ^= (uint)data[index++] << 24;
+ for (int j = 0; j < 8; ++j)
+ {
+ uint bit = crc & 0x80000000u;
+ crc <<= 1;
+ if (0 != bit)
+ crc ^= 0x04c11db7;
+ }
+ }
+ for (int i = 0; i < 0x40; ++i)
+ {
+ uint src = LittleEndian.ToUInt32 (data, index) & 0x1ffcu;
+ src = LittleEndian.ToUInt32 (m_scheme.DecodeBin, (int)src);
+ uint key = src ^ crc;
+ data[index++ + 0x100] ^= (byte)key;
+ data[index++ + 0x100] ^= (byte)(key >> 8);
+ data[index++ + 0x100] ^= (byte)(key >> 16);
+ data[index++ + 0x100] ^= (byte)(key >> 24);
+ }
+ }
+
+ double DecryptHelper1 (double a)
+ {
+ if (a < 0)
+ return -DecryptHelper1 (-a);
+
+ double v0;
+ double v1;
+ if (a < 18.0)
+ {
+ v0 = a;
+ v1 = a;
+ double v2 = -(a * a);
+
+ for (int j = 3; j < 1000; j += 2)
+ {
+ v1 *= v2 / (j * (j - 1));
+ v0 += v1 / j;
+ if (v0 == v2)
+ break;
+ }
+ return v0;
+ }
+
+ int flags = 0;
+ double v0_l = 0;
+ v1 = 0;
+ double div = 1 / a;
+ double v1_h = 2.0;
+ double v0_h = 2.0;
+ double v1_l = 0;
+ v0 = 0;
+ int i = 0;
+
+ do
+ {
+ v0 += div;
+ div *= ++i / a;
+ if (v0 < v0_h)
+ v0_h = v0;
+ else
+ flags |= 1;
+
+ v1 += div;
+ div *= ++i / a;
+ if (v1 < v1_h)
+ v1_h = v1;
+ else
+ flags |= 2;
+
+ v0 -= div;
+ div *= ++i / a;
+ if (v0 > v0_l)
+ v0_l = v0;
+ else
+ flags |= 4;
+
+ v1 -= div;
+ div *= ++i / a;
+ if (v1 > v1_l)
+ v1_l = v1;
+ else
+ flags |= 8;
+ }
+ while (flags != 0xf);
+
+ return ((Math.PI - Math.Cos(a) * (v0_l + v0_h)) - (Math.Sin(a) * (v1_l + v1_h))) / 2.0;
+ }
+
+ uint DecryptHelper2 (double a)
+ {
+ double v0, v1, v2, v3;
+
+ if (a > 1.0)
+ {
+ v0 = Math.Sqrt (a * 2 - 1);
+ for (;;)
+ {
+ v1 = 1 - (double)NextRand() / 4294967296.0;
+ v2 = 2.0 * (double)NextRand() / 4294967296.0 - 1.0;
+ if (v1 * v1 + v2 * v2 > 1.0)
+ continue;
+
+ v2 /= v1;
+ v3 = v2 * v0 + a - 1.0;
+ if (v3 <= 0)
+ continue;
+
+ v1 = (a - 1.0) * Math.Log (v3 / (a - 1.0)) - v2 * v0;
+ if (v1 < -50.0)
+ continue;
+
+ if (((double)NextRand() / 4294967296.0) <= (Math.Exp(v1) * (v2 * v2 + 1.0)))
+ break;
+ }
+ }
+ else
+ {
+ v0 = Math.Exp(1.0) / (a + Math.Exp(1.0));
+ do
+ {
+ v1 = (double)NextRand() / 4294967296.0;
+ v2 = (double)NextRand() / 4294967296.0;
+ if (v1 < v0)
+ {
+ v3 = Math.Pow(v2, 1.0 / a);
+ v1 = Math.Exp(-v3);
+ } else
+ {
+ v3 = 1.0 - Math.Log(v2);
+ v1 = Math.Pow(v3, a - 1.0);
+ }
+ }
+ while ((double)NextRand() / 4294967296.0 >= v1);
+ }
+
+ if (WarcVersion > 120)
+ return (uint)(v3 * 256.0);
+ else
+ return (byte)((double)NextRand() / 4294967296.0);
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ struct Union
+ {
+ [FieldOffset(0)]
+ public int i;
+ [FieldOffset (0)]
+ public uint u;
+ [FieldOffset(0)]
+ public float f;
+ [FieldOffset(0)]
+ public byte b0;
+ [FieldOffset(1)]
+ public byte b1;
+ [FieldOffset(2)]
+ public byte b2;
+ [FieldOffset(3)]
+ public byte b3;
+ }
+
+ uint DecryptHelper3 (uint key)
+ {
+ var p = new Union();
+ p.u = key;
+ var fv = new Union();
+ fv.f = (float)(1.5 * (double)p.b0 + 0.1);
+ uint v0 = Binary.BigEndian (fv.u);
+ fv.f = (float)(1.5 * (double)p.b1 + 0.1);
+ uint v1 = (uint)fv.f;
+ fv.f = (float)(1.5 * (double)p.b2 + 0.1);
+ uint v2 = (uint)-fv.i;
+ fv.f = (float)(1.5 * (double)p.b3 + 0.1);
+ uint v3 = ~fv.u;
+
+ return ((v0 + v1) | (v2 - v3));
+ }
+
+ void DecryptHelper4 (byte[] data, int index, uint[] key_src)
+ {
+ uint[] buf = new uint[0x50];
+ int i;
+ for (i = 0; i < 0x10; ++i)
+ {
+ buf[i] = BigEndian.ToUInt32 (data, index+40+4*i);
+ }
+ for (; i < 0x50; ++i)
+ {
+ uint v = buf[i-16];
+ v ^= buf[i-14];
+ v ^= buf[i-8];
+ v ^= buf[i-3];
+ v = v << 1 | v >> 31;
+ buf[i] = v;
+ }
+ uint[] key = new uint[10];
+ Array.Copy (key_src, key, 5);
+ uint k0 = key[0];
+ uint k1 = key[1];
+ uint k2 = key[2];
+ uint k3 = key[3];
+ uint k4 = key[4];
+
+ uint pc = 0;
+ uint v26 = 0;
+ int buf_idx = 0;
+ for (int ebp = 0; ebp < 0x50; ++ebp)
+ {
+ if (ebp >= 0x10)
+ {
+ if (ebp >= 0x20)
+ {
+ if (ebp >= 0x30)
+ {
+ uint v27 = ~k3;
+ if (ebp >= 0x40)
+ {
+ v26 = k1 ^ (k2 | v27);
+ pc = 0xA953FD4E;
+ }
+ else
+ {
+ v26 = k1 & k3 | k2 & v27;
+ pc = 0x8F1BBCDC;
+ }
+ }
+ else
+ {
+ v26 = k3 ^ (k1 | ~k2);
+ pc = 0x6ED9EBA1;
+ }
+ }
+ else
+ {
+ v26 = k1 & k2 | k3 & ~k1;
+ pc = 0x5A827999;
+ }
+ }
+ else
+ {
+ v26 = k1 ^ k2 ^ k3;
+ pc = 0;
+ }
+ uint v28 = buf[buf_idx] + k4 + v26 + pc + (k0 << 5 | k0 >> 27);
+ uint v29 = (k1 >> 2) | (k1 << 30);
+ k1 = k0;
+ k4 = k3;
+ k3 = k2;
+ k2 = v29;
+ k0 = v28;
+ ++buf_idx;
+ }
+ key[0] += k0;
+ key[1] += k1;
+ key[2] += k2;
+ key[3] += k3;
+ key[4] += k4;
+ var ft = new FILETIME {
+ DateTimeLow = key[1],
+ DateTimeHigh = key[0] & 0x7FFFFFFF
+ };
+ var sys_time = new SYSTEMTIME (ft);
+ key[5] = (uint)(sys_time.Year | sys_time.Month << 16);
+ key[7] = (uint)(sys_time.Hour | sys_time.Minute << 16);
+ key[8] = (uint)(sys_time.Second | sys_time.Milliseconds << 16);
+
+ uint flags = LittleEndian.ToUInt32 (data, index+40) | 0x80000000;
+ uint rgb = buf[1] >> 8; // BigEndian.ToUInt32 (data, index+44) >> 8;
+ if (0 == (flags & 0x78000000))
+ flags |= 0x98000000;
+ key[6] = RegionCrc32 (m_scheme.Region, flags, rgb);
+ key[9] = (uint)(((int)key[2] * (long)(int)key[3]) >> 8);
+ if (m_scheme.Version >= 2390)
+ key[6] += key[9];
+ unsafe
+ {
+ fixed (byte* data_fixed = data)
+ {
+ uint* encoded = (uint*)(data_fixed+index);
+ for (i = 0; i < 10; ++i)
+ {
+ encoded[i] ^= key[i];
+ }
+ }
+ }
+ }
+
+ static readonly uint[] CustomCrcTable = InitCrcTable();
+
+ static uint[] InitCrcTable ()
+ {
+ var table = new uint[0x100];
+ for (uint i = 0; i != 256; ++i)
+ {
+ uint poly = i;
+ for (int j = 0; j < 8; ++j)
+ {
+ uint bit = poly & 1;
+ poly = Binary.RotR (poly, 1);
+ if (0 == bit)
+ poly ^= 0x6DB88320;
+ }
+ table[i] = poly;
+ }
+ return table;
+ }
+
+ uint RegionCrc32 (byte[] src, uint flags, uint rgb)
+ {
+ int src_alpha = (int)flags & 0x1ff;
+ int dst_alpha = (int)(flags >> 12) & 0x1ff;
+ flags >>= 24;
+ if (0 == (flags & 0x10))
+ dst_alpha = 0;
+ if (0 == (flags & 8))
+ src_alpha = 0x100;
+ int y_step = 0;
+ int x_step = 4;
+ int width = 48;
+ int pos = 0;
+ if (0 != (flags & 0x40)) // horizontal flip
+ {
+ y_step += width;
+ pos += (width-1)*4;
+ x_step = -x_step;
+ }
+ if (0 != (flags & 0x20)) // vertical flip
+ {
+ y_step -= width;
+ pos += width*0x2f*4; // width*(height-1)*4;
+ }
+ y_step <<= 3;
+ uint checksum = 0;
+ for (int y = 0; y < 48; ++y)
+ {
+ for (int x = 0; x < 48; ++x)
+ {
+ int alpha = src[pos+3] * src_alpha;
+ alpha >>= 8;
+ uint color = rgb;
+ for (int i = 0; i < 3; ++i)
+ {
+ int v = src[pos+i];
+ int c = (int)(color & 0xff); // rgb[i];
+ c -= v;
+ c = (c * dst_alpha) >> 8;
+ c = (c + v) & 0xff;
+ c = (c * alpha) >> 8;
+ checksum = (checksum >> 8) ^ CustomCrcTable[(c ^ checksum) & 0xff];
+ color >>= 8;
+ }
+ pos += x_step;
+ }
+ pos += y_step;
+ }
+ return checksum;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct FILETIME
+ {
+ public uint DateTimeLow;
+ public uint DateTimeHigh;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct SYSTEMTIME
+ {
+ [MarshalAs(UnmanagedType.U2)] public ushort Year;
+ [MarshalAs(UnmanagedType.U2)] public ushort Month;
+ [MarshalAs(UnmanagedType.U2)] public ushort DayOfWeek;
+ [MarshalAs(UnmanagedType.U2)] public ushort Day;
+ [MarshalAs(UnmanagedType.U2)] public ushort Hour;
+ [MarshalAs(UnmanagedType.U2)] public ushort Minute;
+ [MarshalAs(UnmanagedType.U2)] public ushort Second;
+ [MarshalAs(UnmanagedType.U2)] public ushort Milliseconds;
+
+ public SYSTEMTIME (FILETIME ft)
+ {
+ FileTimeToSystemTime (ref ft, out this);
+ }
+
+ [DllImport ("kernel32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
+ static extern bool FileTimeToSystemTime (ref FILETIME lpFileTime, out SYSTEMTIME lpSystemTime);
+ }
+
+ uint NextRand ()
+ {
+ Rand = 1566083941u * Rand + 1u;
+ return Rand;
+ }
+
+ uint GetMaxIndexLength (int version)
+ {
+ int max_index_entries = version < 150 ? 8192 : 16384;
+ return (uint)((m_scheme.EntryNameSize + 0x18) * max_index_entries);
+ }
+
+ public static EncryptionScheme[] KnownSchemes = new EncryptionScheme[0];
+ }
+
+ public interface IDecryptExtra
+ {
+ void Decrypt (byte[] data, int index, uint length, uint flags);
+ }
+
+ [Serializable]
+ public abstract class KeyDecryptExtra : IDecryptExtra
+ {
+ readonly uint Key;
+ readonly byte[] DecodeTable;
+
+ public KeyDecryptExtra (uint key, byte[] decode_bin)
+ {
+ Key = key;
+ DecodeTable = decode_bin;
+ }
+
+ public void Decrypt (byte[] data, int index, uint length, uint flags)
+ {
+ if (length < 0x400)
+ return;
+ if ((flags & 0x202) == 0x202)
+ {
+ var k = new uint[4];
+ InitKey (Key, k);
+ for (int i = 0; i < 0xFF; ++i)
+ {
+ uint j = k[3] ^ (k[3] << 11) ^ k[0] ^ ((k[3] ^ (k[3] << 11) ^ (k[0] >> 11)) >> 8);
+ k[3] = k[2];
+ k[2] = k[1];
+ k[1] = k[0];
+ k[0] = j;
+ data[index + i] ^= DecodeTable[j % DecodeTable.Length];
+ }
+ }
+ if ((flags & 0x204) == 0x204)
+ {
+ data[index + 0x200] ^= (byte)Key;
+ data[index + 0x201] ^= (byte)(Key >> 8);
+ data[index + 0x202] ^= (byte)(Key >> 16);
+ data[index + 0x203] ^= (byte)(Key >> 24);
+ }
+ }
+
+ protected abstract void InitKey (uint key, uint[] k);
+ }
+
+ [Serializable]
+ public class ShojoMamaCrypt : KeyDecryptExtra
+ {
+ public ShojoMamaCrypt (uint key, byte[] bin) : base (key, bin)
+ {
+ }
+
+ protected override void InitKey (uint key, uint[] k)
+ {
+ k[0] = key + 1;
+ k[1] = key + 4;
+ k[2] = key + 2;
+ k[3] = key + 3;
+ }
+ }
+
+ [Serializable]
+ public class TestamentCrypt : KeyDecryptExtra // Shinigami no Testament
+ {
+ public TestamentCrypt (uint key, byte[] bin) : base (key, bin)
+ {
+ }
+
+ protected override void InitKey (uint key, uint[] k)
+ {
+ k[0] = key + 3;
+ k[1] = key + 2;
+ k[2] = key + 1;
+ k[3] = key;
+ }
+ }
+
+ [Serializable]
+ public class UnknownCrypt : IDecryptExtra
+ {
+ public void Decrypt (byte[] data, int index, uint length, uint flags)
+ {
+ if (length < 0x400)
+ return;
+ int src = index;
+ uint key = 0;
+ for (uint i = (length & 0x7eu) + 1; i != 0; --i)
+ {
+ key ^= data[src++];
+ for (int j = 0; j < 8; ++j)
+ {
+ uint bit = key & 1;
+ key = bit << 15 | key >> 1;
+ if (0 == bit)
+ key ^= 0x408;
+ }
+ }
+ data[index+0x104] ^= (byte)key;
+ data[index+0x105] ^= (byte)(key >> 8);
+ }
+ }
+}