diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index 05426858..a9c56bdd 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -79,8 +79,14 @@ + + + + + WidgetBELL.xaml + @@ -561,6 +567,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/ArcFormats/Cyberworks/ArcDAT.cs b/ArcFormats/Cyberworks/ArcDAT.cs new file mode 100644 index 00000000..7fed101c --- /dev/null +++ b/ArcFormats/Cyberworks/ArcDAT.cs @@ -0,0 +1,303 @@ +//! \file ArcDAT.cs +//! \date Thu Jun 16 13:48:04 2016 +//! \brief Tinker Bell 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.Text; +using System.Text.RegularExpressions; +using GameRes.Compression; +using GameRes.Formats.Properties; +using GameRes.Formats.Strings; +using GameRes.Utility; + +namespace GameRes.Formats.Cyberworks +{ + internal class BellArchive : ArcFile + { + public readonly AImageScheme Scheme; + + public BellArchive (ArcView arc, ArchiveFormat impl, ICollection dir, AImageScheme scheme) + : base (arc, impl, dir) + { + Scheme = scheme; + } + } + + [Export(typeof(ArchiveFormat))] + public class DatOpener : ArchiveFormat + { + public override string Tag { get { return "ARC/Cyberworks"; } } + public override string Description { get { return "Cyberworks/TinkerBell resource archive"; } } + public override uint Signature { get { return 0; } } + public override bool IsHierarchic { get { return false; } } + public override bool CanCreate { get { return false; } } + + public DatOpener () + { + Extensions = new string[] { "dat", "04", "05", "06" }; + } + + static Regex s_arcname_re = new Regex (@"^.+0(?(?\d)(?[a-z])?)(?:|\..*)$", RegexOptions.IgnoreCase); + + public override ArcFile TryOpen (ArcView file) + { + var arc_name = Path.GetFileName (file.Name); + var match = s_arcname_re.Match (arc_name); + if (!match.Success) + return null; + char num = match.Groups["num"].Value[0]; + if (num < '4' || num > '6') + return null; + int arc_idx = 0; + if (match.Groups["idx"].Success) + arc_idx = char.ToUpper (match.Groups["idx"].Value[0]) - '@'; + + var toc_name_builder = new StringBuilder (arc_name); + var num_pos = match.Groups["id"].Index; + toc_name_builder.Remove (num_pos, match.Groups["id"].Length); + toc_name_builder.Insert (num_pos, num-'3'); + var toc_name = toc_name_builder.ToString(); + + toc_name = VFS.CombinePath (VFS.GetDirectoryName (file.Name), toc_name); + byte[] toc; + using (var toc_view = VFS.OpenView (toc_name)) + { + if (toc_view.MaxOffset <= 0x10) + return null; + uint unpacked_size = DecodeDecimal (toc_view, 0); + if (unpacked_size <= 4 || unpacked_size > 0x1000000) + return null; + uint packed_size = DecodeDecimal (toc_view, 8); + if (packed_size > toc_view.MaxOffset) + return null; + toc = new byte[unpacked_size]; + using (var toc_s = toc_view.CreateStream (0x10, packed_size)) + using (var lzss = new LzssStream (toc_s)) + { + if (toc.Length != lzss.Read (toc, 0, toc.Length)) + return null; + } + } + int entry_size = LittleEndian.ToInt32 (toc, 0) + 4; + if (entry_size < 0x16) + return null; + int count = toc.Length / entry_size; + if (!IsSaneCount (count)) + return null; + var type = new char[2]; + var dir = new List (count); + bool has_images = false; + using (var input = new MemoryStream (toc)) + using (var index = new BinaryReader (input)) + { + while (input.Position < input.Length) + { + entry_size = index.ReadInt32(); + if (entry_size <= 0) + return null; + var next_pos = index.BaseStream.Position + entry_size; + uint id = index.ReadUInt32(); + var entry = new PackedEntry { Name = id.ToString ("D6") }; + entry.UnpackedSize = index.ReadUInt32(); + entry.Size = index.ReadUInt32(); + entry.IsPacked = entry.UnpackedSize != entry.Size; + entry.Offset = index.ReadUInt32(); + type[0] = (char)index.ReadByte(); + type[1] = (char)index.ReadByte(); + int entry_idx = 0; + if (entry_size >= 0x17) + { + index.ReadInt32(); + entry_idx = index.ReadByte(); + } + if (entry_idx == arc_idx) + { + if (type[0] > 0x20 && type[0] < 0x7F) + { + string ext; + if (type[1] > 0x20 && type[1] < 0x7F) + ext = new string (type); + else + ext = new string (type[0], 1); + if ("b0" == ext || "n0" == ext || "o0" == ext) + { + entry.Type = "image"; + has_images = true; + } + else if ("j0" == ext || "k0" == ext || "u0" == ext) + entry.Type = "audio"; + entry.Name = Path.ChangeExtension (entry.Name, ext); + } + dir.Add (entry); + } + index.BaseStream.Position = next_pos; + } + } + if (0 == dir.Count) + return null; + if (!has_images) + return new ArcFile (file, this, dir); + var options = Query (arcStrings.ArcEncryptedNotice); + return new BellArchive (file, this, dir, options.Scheme); + } + + public override Stream OpenEntry (ArcFile arc, Entry entry) + { + uint entry_size = entry.Size; + Stream input = arc.File.CreateStream (entry.Offset, entry_size); + var pent = entry as PackedEntry; + if (null != pent && pent.IsPacked) + { + input = new LzssStream (input); + entry_size = pent.UnpackedSize; + } + var barc = arc as BellArchive; + if (null == barc) + return input; + try + { + if ("image" == entry.Type && entry_size > 5) + return DecryptImage (input, entry_size, barc.Scheme); + return input; + } + catch + { + input.Dispose(); + throw; + } + } + + Stream DecryptImage (Stream input, uint entry_size, AImageScheme scheme) + { + byte[] header = null; + byte type = (byte)input.ReadByte(); + if ('c' == type || 'b' == type) + { + header = new byte[5]; + header[0] = type; + input.Read (header, 1, 4); + uint img_size = BigEndian.ToUInt32 (header, 1); + if (entry_size - 5 == img_size) + { + if (input.CanSeek) + input = new StreamRegion (input, 5, img_size); + return input; + } + } + else if (scheme != null && 'a' == type && entry_size > 21) + { + int id = input.ReadByte(); + if (id == scheme.Value2) + { + using (var seekable = new SeekableStream (input)) + using (var reader = new AImageReader (seekable, scheme)) + { + reader.Unpack(); + return TgaStream.Create (reader.Info, reader.Data, scheme.Flipped); + } + } + header = new byte[2] { type, (byte)id }; + } + if (input.CanSeek) + { + input.Position = 0; + } + else + { + if (null == header) + header = new byte[1] { type }; + input = new PrefixStream (header, input); + } + return input; + } + + uint DecodeDecimal (ArcView file, long offset) + { + uint v = 0; + uint rank = 1; + for (int i = 7; i >= 0; --i, rank *= 10) + { + uint b = file.View.ReadByte (offset+i); + if (b != 0xFF) + v += (b ^ 0x7F) * rank; + } + return v; + } + + public override ResourceOptions GetDefaultOptions () + { + return new BellOptions { Scheme = GetScheme (Settings.Default.BELLTitle) }; + } + + public override object GetAccessWidget () + { + return new GUI.WidgetBELL(); + } + + public static AImageScheme GetScheme (string title) + { + AImageScheme scheme = null; + if (string.IsNullOrEmpty (title) || !KnownSchemes.TryGetValue (title, out scheme)) + return null; + return scheme; + } + + public static Dictionary KnownSchemes = new Dictionary(); + + public override ResourceScheme Scheme + { + get { return new SchemeMap { KnownSchemes = KnownSchemes }; } + set { KnownSchemes = ((SchemeMap)value).KnownSchemes; } + } + } + + [Serializable] + public class AImageScheme + { + public byte Value1; + public byte Value2; + public byte Value3; + public byte[] HeaderOrder; + public bool Flipped; + + public AImageScheme () + { + Flipped = true; + } + } + + [Serializable] + public class SchemeMap : ResourceScheme + { + public Dictionary KnownSchemes; + } + + public class BellOptions : ResourceOptions + { + public AImageScheme Scheme; + } +} diff --git a/ArcFormats/Cyberworks/AudioTINK.cs b/ArcFormats/Cyberworks/AudioTINK.cs new file mode 100644 index 00000000..4eba887f --- /dev/null +++ b/ArcFormats/Cyberworks/AudioTINK.cs @@ -0,0 +1,99 @@ +//! \file AudioTINK.cs +//! \date Fri Jun 17 14:13:39 2016 +//! \brief Cyberworks encrypted OGG audio. +// +// 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.Text; +using GameRes.Utility; + +namespace GameRes.Formats.Cyberworks +{ + [Serializable] + public class TinkAudioScheme : ResourceScheme + { + public Dictionary KnownKeys; + } + + [Export(typeof(AudioFormat))] + public class TinkAudio : AudioFormat + { + public override string Tag { get { return "OGG/TINK"; } } + public override string Description { get { return "Cyberworks encrypted OGG audio"; } } + public override uint Signature { get { return 0x6B6E6954; } } + + public TinkAudio () + { + Signatures = new uint[] { 0x6B6E6954, 0x676E6F53, 0 }; // 'Tink', 'Song' + Extensions = new string[] { "j0", "k0", "u0" }; + } + + static Dictionary KnownKeys = new Dictionary(); + + public override ResourceScheme Scheme + { + get { return new TinkAudioScheme { KnownKeys = KnownKeys }; } + set { KnownKeys = ((TinkAudioScheme)value).KnownKeys; } + } + + public override SoundInput TryOpen (Stream file) + { + var header = new byte[Math.Min (0xE1F, file.Length)]; + if (0x10 != file.Read (header, 0, 0x10)) + return null; + var signature = LittleEndian.ToUInt32 (header, 0); + byte[] key; + if (!KnownKeys.TryGetValue (signature, out key)) + { + signature = LittleEndian.ToUInt32 (header, 0xC); + if (!KnownKeys.TryGetValue (signature, out key)) + return null; + file.Read (header, 4, 0xC); + } + header[0] = (byte)'O'; + header[1] = (byte)'g'; + header[2] = (byte)'g'; + header[3] = (byte)'S'; + file.Read (header, 0x10, header.Length-0x10); + int k = 0; + for (int i = 4; i < header.Length; ++i) + { + header[i] ^= key[k++]; + if (k >= key.Length) + k = 1; + } + Stream input; + if (header.Length >= file.Length) + input = new MemoryStream (header); + else + input = new PrefixStream (header, new StreamRegion (file, file.Position)); + var sound = OggAudio.Instance.TryOpen (input); + if (sound != null && header.Length >= file.Length) + file.Dispose(); + return sound; + } + } +} diff --git a/ArcFormats/Cyberworks/ImageTINK.cs b/ArcFormats/Cyberworks/ImageTINK.cs new file mode 100644 index 00000000..10115cda --- /dev/null +++ b/ArcFormats/Cyberworks/ImageTINK.cs @@ -0,0 +1,265 @@ +//! \file ImageTINK.cs +//! \date Fri Jun 17 18:49:04 2016 +//! \brief Tinker Bell encrypted image file. +// +// 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.ComponentModel.Composition; +using System.IO; + +namespace GameRes.Formats.Cyberworks +{ + public enum AImageHeader + { + Flags = 0, + Field1 = 1, + Field2 = 2, + Height = 3, + Width = 4, + UnpackedSize = 5, + AlphaSize = 6, + BitsSize = 7, + } + + internal sealed class AImageReader : IDisposable + { + public readonly ImageMetaData Info = new ImageMetaData(); + + BinaryReader m_input; + byte[] m_output; + AImageScheme m_scheme; + + public byte[] Data { get { return m_output; } } + + public AImageReader (Stream input, AImageScheme scheme) + { + m_input = new ArcView.Reader (input); + m_scheme = scheme; + } + + public void Unpack () + { + var header = new int[m_scheme.HeaderOrder.Length]; + for (int i = 0; i < m_scheme.HeaderOrder.Length; ++i) + { + int b = GetInt(); + header[m_scheme.HeaderOrder[i]] = b; + } + Info.Width = (uint)header[4]; + Info.Height = (uint)header[3]; + if (0 == Info.Width || Info.Width >= 0x8000 || 0 == Info.Height || Info.Height >= 0x8000) + throw new InvalidFormatException(); + int unpacked_size = header[5]; + if (unpacked_size <= 0) + throw new InvalidFormatException(); + int flags = header[0]; + int bits_size = header[7]; + int data_offset = bits_size * 2; + if (0 == flags) + CopyV0 (unpacked_size); + else if (2 == (flags & 6)) + UnpackV2 (bits_size, data_offset); + else if (6 == (flags & 6)) + { + if (0 == bits_size) + CopyV6 (unpacked_size, header[6]); + else + UnpackV6 (bits_size, data_offset, data_offset + header[6]); + } + else + throw new InvalidFormatException(); + } + + void CopyV0 (int data_size) + { + int plane_size = (int)Info.Width * (int)Info.Height; + if (plane_size == data_size) + { + Info.BPP = 8; + m_output = m_input.ReadBytes (data_size); + } + else if (3 * plane_size == data_size) + { + Info.BPP = 24; + m_output = m_input.ReadBytes (data_size); + } + else if (4 * plane_size == data_size) + { + Info.BPP = 32; + m_output = m_input.ReadBytes (data_size); + } + else + { + Info.BPP = 24; + int dst_stride = (int)Info.Width * 3; + int src_stride = (dst_stride + 3) & ~3; + if (src_stride * (int)Info.Height != data_size) + throw new InvalidFormatException(); + m_output = new byte[dst_stride * (int)Info.Height]; + int dst = 0; + for (uint y = 0; y < Info.Height; ++y) + { + m_input.Read (m_output, dst, dst_stride); + dst += dst_stride; + m_input.BaseStream.Seek (src_stride-dst_stride, SeekOrigin.Current); + } + } + } + + void UnpackV2 (int offset1, int rgb_offset) + { + Info.BPP = 24; + var rgb_map = m_input.ReadBytes (offset1); + var alpha_map = m_input.ReadBytes (rgb_offset-offset1); + int plane_size = (int)Info.Width * (int)Info.Height; + m_output = new byte[plane_size * 3]; + + int bit = 1; + int bit_src = 0; + int dst = 0; + for (int i = 0; i < plane_size; ++i) + { + if ((bit & alpha_map[bit_src]) == 0 && (bit & rgb_map[bit_src]) != 0) + { + m_input.Read (m_output, dst, 3); + } + dst += 3; + if (0x80 == bit) + { + ++bit_src; + bit = 1; + } + else + bit <<= 1; + } + } + + void CopyV6 (int alpha_size, int rgb_size) + { + Info.BPP = 32; + int plane_size = (int)Info.Width * (int)Info.Height; + m_output = new byte[plane_size * 4]; + int stride = ((int)Info.Width * 3 + 3) & ~3; + var line = new byte[stride]; + int dst = 3; + for (uint y = 0; y < Info.Height; ++y) + { + m_input.Read (line, 0, stride); + int src = 0; + for (uint x = 0; x < Info.Width; ++x) + { + m_output[dst] = line[src]; + dst += 4; + src += 3; + } + } + dst = 0; + for (uint y = 0; y < Info.Height; ++y) + { + m_input.Read (line, 0, stride); + int src = 0; + for (uint x = 0; x < Info.Width; ++x) + { + m_output[dst ] = line[src++]; + m_output[dst+1] = line[src++]; + m_output[dst+2] = line[src++]; + dst += 4; + } + } + } + + void UnpackV6 (int offset1, int alpha_offset, int rgb_offset) + { + Info.BPP = 32; + var rgb_map = m_input.ReadBytes (offset1); + var alpha_map = m_input.ReadBytes (alpha_offset - offset1); + var alpha = m_input.ReadBytes (rgb_offset - alpha_offset); + int plane_size = (int)Info.Width * (int)Info.Height; + m_output = new byte[plane_size * 4]; + + int bit = 1; + int bit_src = 0; + int alpha_src = 0; + int dst = 0; + for (int i = 0; i < plane_size; ++i) + { + bool has_alpha = (bit & alpha_map[bit_src]) != 0; + if (has_alpha || (bit & rgb_map[bit_src]) != 0) + { + m_input.Read (m_output, dst, 3); + if (has_alpha) + { + m_output[dst+3] = alpha[alpha_src]; + alpha_src += 3; + } + else + m_output[dst+3] = 0xFF; + } + dst += 4; + if (0x80 == bit) + { + ++bit_src; + bit = 1; + } + else + bit <<= 1; + } + } + + int GetInt () + { + byte a = m_input.ReadByte(); + if (a == m_scheme.Value3) + a = 0; + int d = 0; + int c = 0; + for (;;) + { + byte a1 = m_input.ReadByte(); + if (a1 == m_scheme.Value2) + break; + if (a1 != m_scheme.Value1) + { + c = (a1 == m_scheme.Value3) ? 0 : a1; + } + else + { + ++d; + } + } + return a + (c + d * m_scheme.Value1) * m_scheme.Value1; + } + + #region IDisposable Members + bool _disposed = false; + public void Dispose () + { + if (!_disposed) + { + m_input.Dispose(); + _disposed = true; + } + } + #endregion + } +} diff --git a/ArcFormats/Cyberworks/WidgetBELL.xaml b/ArcFormats/Cyberworks/WidgetBELL.xaml new file mode 100644 index 00000000..b3cc313f --- /dev/null +++ b/ArcFormats/Cyberworks/WidgetBELL.xaml @@ -0,0 +1,7 @@ + + + diff --git a/ArcFormats/Cyberworks/WidgetBELL.xaml.cs b/ArcFormats/Cyberworks/WidgetBELL.xaml.cs new file mode 100644 index 00000000..35ef3f1f --- /dev/null +++ b/ArcFormats/Cyberworks/WidgetBELL.xaml.cs @@ -0,0 +1,22 @@ +using System.Linq; +using System.Windows.Controls; +using GameRes.Formats.Cyberworks; +using GameRes.Formats.Strings; + +namespace GameRes.Formats.GUI +{ + /// + /// Interaction logic for WidgetBELL.xaml + /// + public partial class WidgetBELL : StackPanel + { + public WidgetBELL() + { + InitializeComponent(); + var keys = new string[] { arcStrings.ArcIgnoreEncryption }; + Title.ItemsSource = keys.Concat (DatOpener.KnownSchemes.Keys.OrderBy (x => x)); + if (-1 == Title.SelectedIndex) + Title.SelectedIndex = 0; + } + } +} diff --git a/ArcFormats/Properties/Settings.Designer.cs b/ArcFormats/Properties/Settings.Designer.cs index b4075d35..b820742d 100644 --- a/ArcFormats/Properties/Settings.Designer.cs +++ b/ArcFormats/Properties/Settings.Designer.cs @@ -573,5 +573,17 @@ namespace GameRes.Formats.Properties { this["MGPKTitle"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string BELLTitle { + get { + return ((string)(this["BELLTitle"])); + } + set { + this["BELLTitle"] = value; + } + } } } diff --git a/ArcFormats/Properties/Settings.settings b/ArcFormats/Properties/Settings.settings index 26898952..16dc5afc 100644 --- a/ArcFormats/Properties/Settings.settings +++ b/ArcFormats/Properties/Settings.settings @@ -140,5 +140,8 @@ + + + \ No newline at end of file diff --git a/ArcFormats/app.config b/ArcFormats/app.config index 24f0e518..fe5f6783 100644 --- a/ArcFormats/app.config +++ b/ArcFormats/app.config @@ -142,6 +142,9 @@ + + + diff --git a/supported.html b/supported.html index 9603273d..a705bd52 100644 --- a/supported.html +++ b/supported.html @@ -556,6 +556,7 @@ Sora no Iro, Mizu no Iro
Thunder Claps!
Tokumei Kyoushi Hitomi
Toshiue Lesson ~Mama to Oba-san to Sensei to~
+Tutorial Summer
*.iksNPSRNoX[iks]Shikkan ~Hazukashimerareta Karada, Oreta Kokoro~ *.wbpARCFORM3 WBUGNoWild Bug @@ -932,6 +933,12 @@ Natsunone -Ring-
Cartagra
Kara no Shoujo 2
+data.NN
ArcNN.dat-NoTinkerBell +Hime Kami 1/2
+Oshioki ~Gakuen Reijou Kousei Keikaku~
+Ore Maou! ~Kudake Chitta Tamashii
+Zoku Etsuraku no Tane
+

1 Non-encrypted only