implemented Cyberworks archives.

This commit is contained in:
morkt 2016-07-06 04:14:42 +04:00
parent 03d62b91d9
commit 55ba371949
10 changed files with 731 additions and 0 deletions

View File

@ -79,8 +79,14 @@
<Compile Include="Cmvs\ArcPBZ.cs" />
<Compile Include="Cmvs\AudioMV.cs" />
<Compile Include="Cmvs\ImagePSB.cs" />
<Compile Include="Cyberworks\ArcDAT.cs" />
<Compile Include="Cyberworks\AudioTINK.cs" />
<Compile Include="Bruns\AudioUM3.cs" />
<Compile Include="Bruns\ImageEENC.cs" />
<Compile Include="Cyberworks\ImageTINK.cs" />
<Compile Include="Cyberworks\WidgetBELL.xaml.cs">
<DependentUpon>WidgetBELL.xaml</DependentUpon>
</Compile>
<Compile Include="LiveMaker\ArcVF.cs" />
<Compile Include="ArcZIP.cs" />
<Compile Include="AudioVOC.cs" />
@ -561,6 +567,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Cyberworks\WidgetBELL.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="DxLib\WidgetSCR.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

View File

@ -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<Entry> 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(?<id>(?<num>\d)(?<idx>[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<Entry> (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<BellOptions> (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<string, AImageScheme> KnownSchemes = new Dictionary<string, AImageScheme>();
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<string, AImageScheme> KnownSchemes;
}
public class BellOptions : ResourceOptions
{
public AImageScheme Scheme;
}
}

View File

@ -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<uint, byte[]> 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<uint, byte[]> KnownKeys = new Dictionary<uint, byte[]>();
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;
}
}
}

View File

@ -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
}
}

View File

@ -0,0 +1,7 @@
<StackPanel x:Class="GameRes.Formats.GUI.WidgetBELL"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:p="clr-namespace:GameRes.Formats.Properties">
<ComboBox Name="Title" Width="250" ItemsSource="{Binding}"
SelectedValue="{Binding Source={x:Static p:Settings.Default}, Path=BELLTitle, Mode=TwoWay}"/>
</StackPanel>

View File

@ -0,0 +1,22 @@
using System.Linq;
using System.Windows.Controls;
using GameRes.Formats.Cyberworks;
using GameRes.Formats.Strings;
namespace GameRes.Formats.GUI
{
/// <summary>
/// Interaction logic for WidgetBELL.xaml
/// </summary>
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;
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -140,5 +140,8 @@
<Setting Name="MGPKTitle" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="BELLTitle" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
</Settings>
</SettingsFile>

View File

@ -142,6 +142,9 @@
<setting name="MGPKTitle" serializeAs="String">
<value />
</setting>
<setting name="BELLTitle" serializeAs="String">
<value />
</setting>
</GameRes.Formats.Properties.Settings>
</userSettings>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup></configuration>

View File

@ -556,6 +556,7 @@ Sora no Iro, Mizu no Iro<br/>
Thunder Claps!<br/>
Tokumei Kyoushi Hitomi<br/>
Toshiue Lesson ~Mama to Oba-san to Sensei to~<br/>
Tutorial Summer<br/>
</td></tr>
<tr><td>*.iks</td><td><tt>NPSR</tt></td><td>No</td><td>X[iks]</td><td>Shikkan ~Hazukashimerareta Karada, Oreta Kokoro~</td></tr>
<tr class="odd"><td>*.wbp</td><td><tt>ARCFORM3 WBUG</tt></td><td>No</td><td rowspan="4">Wild Bug</td><td rowspan="4">
@ -932,6 +933,12 @@ Natsunone -Ring-<br/>
Cartagra<br/>
Kara no Shoujo 2<br/>
</td></td>
<tr><td>data.NN<br/>ArcNN.dat</td><td>-</td><td>No</td><td>TinkerBell</td><td>
Hime Kami 1/2<br/>
Oshioki ~Gakuen Reijou Kousei Keikaku~<br/>
Ore Maou! ~Kudake Chitta Tamashii<br/>
Zoku Etsuraku no Tane<br/>
</td></tr>
</table>
<p><a name="note-1" class="footnote">1</a> Non-encrypted only</p>
</body>