Compare commits

...

23 Commits

Author SHA1 Message Date
morkt
0c8b3e3e62 released v1.4.30 2017-07-05 14:24:25 +04:00
morkt
2aa6f92b1a (DxLib): implemented archives version 6. 2017-07-05 01:11:23 +04:00
morkt
8c37a7e107 (Csystem): support new incremental images. 2017-07-04 07:41:54 +04:00
morkt
525c2ff29b (KiriKiri.RhapsodyCrypt): added "txt" extension. 2017-07-04 07:38:19 +04:00
morkt
1aeb6cb791 updated formats. 2017-06-27 14:48:04 +04:00
morkt
5bd63c01cb (XP3): adjusted reading filenames from archive index.
some modified Kirikiri builds don't store filenames within archives at
all, only filename hashes.
2017-06-27 14:42:27 +04:00
morkt
37a038b090 updated formats. 2017-06-14 01:21:32 +04:00
morkt
96ebe2bd85 (PFS): recognize 'pf6' archives. 2017-06-14 01:19:06 +04:00
morkt
5097a98cb3 updated formats. 2017-06-08 08:29:31 +04:00
morkt
241cb95f8b (CfiDecryptor): null argument check. 2017-06-08 08:27:01 +04:00
morkt
8a74eb1c77 (malie): LIBP alignment handling. 2017-06-07 00:54:03 +04:00
morkt
360664343f updated formats. 2017-06-06 23:25:16 +04:00
morkt
da3403339e (malie): generalized encryption handling. 2017-06-06 23:22:19 +04:00
morkt
2d186edb39 another KiriKiri encryption scheme. 2017-06-03 03:45:06 +04:00
morkt
abd6ff6b39 released v1.4.29 2017-06-01 22:50:36 +04:00
morkt
a5f398a2d5 (Will.ArcOpener): added common signatures. 2017-05-28 13:34:33 +04:00
morkt
337f238198 (HDatOpener): preserve filenames in external archive index. 2017-05-21 23:45:14 +04:00
morkt
6185af55cd updated formats. 2017-05-21 18:30:36 +04:00
morkt
b1ebf89890 implemented YaneSDK archive variation (#67). 2017-05-21 18:28:10 +04:00
morkt
2f2b067272 (PSB): added DXT5 decoder. 2017-05-21 18:18:02 +04:00
morkt
11273de4f6 updated formats. 2017-05-04 04:26:28 +04:00
morkt
2ceafe55df updated projects. 2017-05-04 04:22:22 +04:00
morkt
0fd301d72b (Kaguya): implemented AN21 animation resources. 2017-05-02 22:00:36 +04:00
29 changed files with 1330 additions and 25815 deletions

View File

@@ -12,6 +12,7 @@
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -108,10 +109,13 @@
<Compile Include="GameSystem\AudioADP4.cs" />
<Compile Include="Kaas\ArcPB.cs" />
<Compile Include="Kaas\AudioKAAS.cs" />
<Compile Include="Kaguya\ArcAN21.cs" />
<Compile Include="Kaguya\ArcUF.cs" />
<Compile Include="Kurumi\ImageGRA.cs" />
<Compile Include="Leaf\ArcPX.cs" />
<Compile Include="Malie\ArcLIBU.cs" />
<Compile Include="Malie\LibScheme.cs" />
<Compile Include="Malie\MalieEncryption.cs" />
<Compile Include="MicroVision\ArcARC.cs" />
<Compile Include="MicroVision\ArcGSD.cs" />
<Compile Include="MicroVision\AudioIKM.cs" />
@@ -557,6 +561,7 @@
<Compile Include="Xuse\ArcXARC.cs" />
<Compile Include="Xuse\ArcXuse.cs" />
<Compile Include="YaneSDK\ArcDAT.cs" />
<Compile Include="YaneSDK\ArcHibiki.cs" />
<Compile Include="Yatagarasu\ArcPKG.cs" />
<Compile Include="Yatagarasu\ArcPKG2.cs" />
<Compile Include="Youkai\ArcDAT.cs" />

View File

@@ -52,13 +52,14 @@ namespace GameRes.Formats.Artemis
int version = file.View.ReadByte (2) - '0';
switch (version)
{
case 8: return OpenPf8 (file);
case 6:
case 8: return OpenPf (file, version);
case 2: return OpenPf2 (file);
default: return null;
}
}
ArcFile OpenPf8 (ArcView file)
ArcFile OpenPf (ArcView file, int version)
{
uint index_size = file.View.ReadUInt32 (3);
int count = file.View.ReadInt32 (7);
@@ -81,9 +82,13 @@ namespace GameRes.Formats.Artemis
index_offset += 8;
dir.Add (entry);
}
if (version != 8 && version != 9 && version != 4 && version != 5)
return new ArcFile (file, this, dir);
// key calculated for archive versions 4, 5, 8 and 9
using (var sha1 = SHA1.Create())
{
var key = sha1.ComputeHash (index); // calculated for archive versions 4, 5, 8 and 9
var key = sha1.ComputeHash (index);
return new PfsArchive (file, this, dir, key);
}
}

View File

@@ -2,7 +2,7 @@
//! \date Thu Jun 16 13:48:04 2016
//! \brief Tinker Bell resource archive.
//
// Copyright (C) 2016 by morkt
// Copyright (C) 2016-2017 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
@@ -272,8 +272,12 @@ namespace GameRes.Formats.Cyberworks
try
{
var reader = DecryptImage (input, barc.Scheme);
if (BlendOverlayImages && reader is AImageReader)
reader = BlendAImage (barc, entry, reader as AImageReader);
if (BlendOverlayImages)
{
var overlay = reader as AImageReader;
if (overlay != null)
overlay.ReadBaseline (barc, entry);
}
return reader;
}
catch
@@ -294,55 +298,18 @@ namespace GameRes.Formats.Cyberworks
input = BinaryStream.FromStream (new StreamRegion (input.AsStream, 5, img_size), input.Name);
}
}
else if (scheme != null && 'a' == type && input.Length > 21)
else if (scheme != null && ('a' == type || 'd' == type) && input.Length > 21)
{
int id = input.ReadByte();
if (id == scheme.Value2)
{
return new AImageReader (input, scheme);
return new AImageReader (input, scheme, type);
}
}
input.Position = 0;
return new ImageFormatDecoder (input);
}
IImageDecoder BlendAImage (BellArchive arc, Entry entry, AImageReader overlay)
{
var header = overlay.ReadHeader();
if (header[0] != 1)
return overlay;
var scheme = arc.Scheme;
var dir = (List<Entry>)arc.Dir;
int i = dir.IndexOf (entry);
while (--i >= 0 && "image" == dir[i].Type)
{
using (var input = OpenEntry (arc, dir[i]))
{
int type = input.ReadByte();
if (type != 'a')
break;
int id = input.ReadByte();
if (id != scheme.Value2)
break;
using (var bin = new BinaryStream (input, dir[i].Name))
using (var base_image = new AImageReader (bin, scheme))
{
var base_header = base_image.ReadHeader();
if (1 == base_header[0])
continue;
// check if image width/height are the same
if (base_header[3] == header[3] && base_header[4] == header[4])
{
base_image.Unpack();
overlay.Baseline = base_image.Data;
}
break;
}
}
}
return overlay;
}
internal AImageScheme QueryScheme (string arc_name)
{
var title = FormatCatalog.Instance.LookupGame (arc_name);

View File

@@ -2,7 +2,7 @@
//! \date Fri Jun 17 18:49:04 2016
//! \brief Tinker Bell encrypted image file.
//
// Copyright (C) 2016 by morkt
// Copyright (C) 2016-2017 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
@@ -24,9 +24,10 @@
//
using System;
using System.ComponentModel.Composition;
using System.Collections.Generic;
using System.IO;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace GameRes.Formats.Cyberworks
{
@@ -52,11 +53,13 @@ namespace GameRes.Formats.Cyberworks
AImageScheme m_scheme;
ImageData m_image;
int[] m_header;
int m_type;
public Stream Source { get { m_input.Position = 0; return m_input.AsStream; } }
public ImageFormat SourceFormat { get { return null; } }
public ImageMetaData Info { get { return m_info; } }
public byte[] Baseline { get; set; }
public int Type { get { return m_type; } }
public ImageData Image
{
@@ -77,10 +80,11 @@ namespace GameRes.Formats.Cyberworks
public byte[] Data { get { return m_output; } }
public AImageReader (IBinaryStream input, AImageScheme scheme)
public AImageReader (IBinaryStream input, AImageScheme scheme, int type = 'a')
{
m_input = input;
m_scheme = scheme;
m_type = type;
}
internal int[] ReadHeader ()
@@ -99,6 +103,65 @@ namespace GameRes.Formats.Cyberworks
return m_header;
}
/// <summary>
/// Search archive <paramref name="arc"/> for baseline image.
/// </summary>
internal void ReadBaseline (BellArchive arc, Entry entry)
{
var header = ReadHeader();
if (!((header[0] & 1) == 1 && 'd' == this.Type
|| header[0] == 1 && 'a' == this.Type))
return;
var scheme = arc.Scheme;
var dir = (List<Entry>)arc.Dir;
int i = dir.IndexOf (entry);
while (--i >= 0 && "image" == dir[i].Type)
{
using (var input = arc.OpenEntry (dir[i]))
{
int type = input.ReadByte();
if ('d' == type)
continue;
if ('a' == type)
{
int id = input.ReadByte();
if (id != scheme.Value2)
break;
using (var bin = new BinaryStream (input, dir[i].Name))
using (var base_image = new AImageReader (bin, scheme))
{
var base_header = base_image.ReadHeader();
if (1 == base_header[0])
continue;
// check if image width/height are the same
if (base_header[3] == header[3] && base_header[4] == header[4])
{
base_image.Unpack();
Baseline = base_image.Data;
}
}
}
else if ('b' == type || 'c' == type)
{
var size_buf = new byte[4];
input.Read (size_buf, 0 , 4);
var decoder = new PngBitmapDecoder (input, BitmapCreateOptions.None,
BitmapCacheOption.OnLoad);
BitmapSource frame = decoder.Frames[0];
Info.Width = (uint)frame.PixelWidth;
Info.Height = (uint)frame.PixelHeight;
if (frame.Format.BitsPerPixel != 32)
frame = new FormatConvertedBitmap (frame, PixelFormats.Bgra32, null, 0);
int stride = frame.PixelWidth * 4;
var pixels = new byte[stride * frame.PixelHeight];
frame.CopyPixels (pixels, stride, 0);
Baseline = pixels;
}
break;
}
}
}
public void Unpack ()
{
var header = ReadHeader();
@@ -118,6 +181,8 @@ namespace GameRes.Formats.Cyberworks
{
if (0 == bits_size)
CopyV6 (unpacked_size, header[6]);
else if (1 == (flags & 1) && 'd' == m_type && Baseline != null)
UnpackV6d (bits_size, bits_size + header[6]);
else
UnpackV6 (bits_size, data_offset, data_offset + header[6]);
}
@@ -306,6 +371,34 @@ namespace GameRes.Formats.Cyberworks
}
}
void UnpackV6d (int bits_size, int rgb_offset)
{
Info.BPP = 32;
var rgb_map = m_input.ReadBytes (bits_size);
var alpha = m_input.ReadBytes (rgb_offset - bits_size);
int plane_size = Math.Min (Baseline.Length, bits_size*8);
m_output = Baseline;
int bit = 1;
int bit_src = 0;
int alpha_src = 0;
int dst = 0;
for (int i = 0; i < plane_size; ++i)
{
if ((bit & rgb_map[bit_src]) != 0)
{
m_input.Read (m_output, dst, 3);
m_output[dst+3] = alpha[alpha_src++];
}
dst += 4;
bit <<= 1;
if (0x100 == bit)
{
++bit_src;
bit = 1;
}
}
}
int GetInt ()
{
byte a = m_input.ReadUInt8();

View File

@@ -2,7 +2,7 @@
//! \date Thu Oct 08 00:18:56 2015
//! \brief DxLib engine archives with 'DX' signature.
//
// Copyright (C) 2015-2016 by morkt
// Copyright (C) 2015-2017 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,6 +28,7 @@ using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Text;
using GameRes.Utility;
namespace GameRes.Formats.DxLib
@@ -35,11 +36,13 @@ namespace GameRes.Formats.DxLib
internal class DxArchive : ArcFile
{
public readonly byte[] Key;
public readonly int Version;
public DxArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, byte[] key)
public DxArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, byte[] key, int version)
: base (arc, impl, dir)
{
Key = key;
Version = version;
}
}
@@ -60,8 +63,8 @@ namespace GameRes.Formats.DxLib
public DxOpener ()
{
Extensions = new string[] { "dxa", "hud", "usi", "med", "dat" };
Signatures = new uint[] { 0x19EF8ED4, 0xA9FCCEDD, 0x0AEE0FD3, 0x5523F211, 0x5524F211, 0 };
Extensions = new string[] { "dxa", "hud", "usi", "med", "dat", "bin" };
Signatures = new uint[] { 0x19EF8ED4, 0xA9FCCEDD, 0x0AEE0FD3, 0x5523F211, 0x5524F211, 0x69FC5FE4, 0 };
}
public static IList<byte[]> KnownKeys = new List<byte[]>();
@@ -76,7 +79,7 @@ namespace GameRes.Formats.DxLib
uint sig_key = LittleEndian.ToUInt32 (key, 0);
uint sig_test = signature ^ sig_key;
int version = (int)(sig_test >> 16);
if (0x5844 == (sig_test & 0xFFFF) && version <= 4) // 'DX'
if (0x5844 == (sig_test & 0xFFFF) && version <= 6) // 'DX'
{
var dir = ReadIndex (file, version, key);
if (null != dir)
@@ -87,7 +90,7 @@ namespace GameRes.Formats.DxLib
KnownKeys.Remove (key);
KnownKeys.Insert (0, key);
}
return new DxArchive (file, this, dir, key);
return new DxArchive (file, this, dir, key, version);
}
return null;
}
@@ -124,7 +127,7 @@ namespace GameRes.Formats.DxLib
if (null != dir)
{
KnownKeys.Insert (0, key);
return new DxArchive (file, this, dir, key);
return new DxArchive (file, this, dir, key, version);
}
}
catch { /* ignore parse errors */ }
@@ -138,9 +141,14 @@ namespace GameRes.Formats.DxLib
var dx_arc = arc as DxArchive;
if (null == dx_arc)
return input;
input = new EncryptedStream (input, entry.Offset, dx_arc.Key);
var dx_ent = entry as PackedEntry;
if (null == dx_ent || !dx_ent.IsPacked)
var dx_ent = (PackedEntry)entry;
long dec_offset = entry.Offset;
if (dx_arc.Version > 5)
{
dec_offset = dx_ent.UnpackedSize;
}
input = new EncryptedStream (input, dec_offset, dx_arc.Key);
if (!dx_ent.IsPacked)
return input;
using (input)
{
@@ -214,26 +222,53 @@ namespace GameRes.Formats.DxLib
}
List<Entry> ReadIndex (ArcView file, int version, byte[] key)
{
DxHeader dx = null;
if (version <= 4)
dx = ReadArcHeaderV4 (file, version, key);
else if (6 == version)
dx = ReadArcHeaderV6 (file, version, key);
if (null == dx || dx.DirTable >= dx.IndexSize || dx.FileTable >= dx.IndexSize)
return null;
using (var encrypted = file.CreateStream (dx.IndexOffset, dx.IndexSize))
using (var index = new EncryptedStream (encrypted, 6 == version ? 0 : dx.IndexOffset, key))
using (var reader = IndexReader.Create (dx, version, index))
{
return reader.Read();
}
}
DxHeader ReadArcHeaderV4 (ArcView file, int version, byte[] key)
{
var header = file.View.ReadBytes (4, 0x18);
if (0x18 != header.Length)
return null;
Decrypt (header, 0, header.Length, 4, key);
var dx = new DxHeader {
return new DxHeader {
IndexSize = LittleEndian.ToUInt32 (header, 0),
BaseOffset = LittleEndian.ToUInt32 (header, 4),
IndexOffset = LittleEndian.ToUInt32 (header, 8),
FileTable = LittleEndian.ToUInt32 (header, 0x0c),
FileTable = LittleEndian.ToUInt32 (header, 0x0C),
DirTable = LittleEndian.ToUInt32 (header, 0x10),
CodePage = 932,
};
if (dx.DirTable >= dx.IndexSize || dx.FileTable >= dx.IndexSize)
}
DxHeader ReadArcHeaderV6 (ArcView file, int version, byte[] key)
{
var header = file.View.ReadBytes (4, 0x2C);
if (0x2C != header.Length)
return null;
using (var encrypted = file.CreateStream (dx.IndexOffset, dx.IndexSize))
using (var index = new EncryptedStream (encrypted, dx.IndexOffset, key))
using (var reader = new IndexReader (dx, version, index))
{
return reader.Read();
}
Decrypt (header, 0, header.Length, 4, key);
Dump.Write (header);
return new DxHeader {
IndexSize = LittleEndian.ToUInt32 (header, 0),
BaseOffset = LittleEndian.ToInt64 (header, 4),
IndexOffset = LittleEndian.ToInt64 (header, 0x0C),
FileTable = (uint)LittleEndian.ToInt64 (header, 0x14),
DirTable = (uint)LittleEndian.ToInt64 (header, 0x1C),
CodePage = LittleEndian.ToInt32 (header, 0x24),
};
}
internal static void Decrypt (byte[] data, int index, int count, long offset, byte[] key)
@@ -291,32 +326,33 @@ namespace GameRes.Formats.DxLib
public uint IndexSize;
public uint FileTable;
public uint DirTable;
public int CodePage;
}
internal class DxDirectory
internal abstract class IndexReader : IDisposable
{
public int DirOffset;
public int ParentDirOffset;
public int FileCount;
public int FileTable;
}
protected readonly int m_version;
protected DxHeader m_header;
protected BinaryStream m_input;
protected Encoding m_encoding;
protected List<Entry> m_dir = new List<Entry>();
internal sealed class IndexReader : IDisposable
{
readonly int m_version;
readonly int m_entry_size;
DxHeader m_header;
BinaryReader m_input;
List<Entry> m_dir = new List<Entry>();
public List<Entry> Dir { get { return m_dir; } }
public IndexReader (DxHeader header, int version, Stream input)
protected IndexReader (DxHeader header, int version, Stream input)
{
m_version = version;
m_entry_size = m_version >= 2 ? 0x2C : 0x28;
m_header = header;
m_input = new ArcView.Reader (input);
m_version = version;
m_input = new BinaryStream (input, "");
m_encoding = Encoding.GetEncoding (header.CodePage);
}
public static IndexReader Create (DxHeader header, int version, Stream input)
{
if (version <= 4)
return new IndexReaderV2 (header, version, input);
else if (6 == version)
return new IndexReaderV6 (header, version, input);
else
throw new InvalidFormatException ("Not supported DX archive version.");
}
public List<Entry> Read ()
@@ -325,6 +361,46 @@ namespace GameRes.Formats.DxLib
return m_dir;
}
protected abstract void ReadFileTable (string root, long table_offset);
protected string ExtractFileName (long table_offset)
{
m_input.Position = table_offset;
int name_offset = m_input.ReadUInt16() * 4 + 4;
m_input.Position = table_offset + name_offset;
return m_input.ReadCString (m_encoding);
}
#region IDisposable Members
bool disposed = false;
public void Dispose ()
{
if (!disposed)
{
m_input.Dispose();
disposed = true;
}
}
#endregion
}
internal sealed class IndexReaderV2 : IndexReader
{
readonly int m_entry_size;
public IndexReaderV2 (DxHeader header, int version, Stream input) : base (header, version, input)
{
m_entry_size = m_version >= 2 ? 0x2C : 0x28;
}
private class DxDirectory
{
public int DirOffset;
public int ParentDirOffset;
public int FileCount;
public int FileTable;
}
DxDirectory ReadDirEntry ()
{
var dir = new DxDirectory();
@@ -335,22 +411,22 @@ namespace GameRes.Formats.DxLib
return dir;
}
void ReadFileTable (string root, uint table_offset)
protected override void ReadFileTable (string root, long table_offset)
{
m_input.BaseStream.Position = m_header.DirTable + table_offset;
m_input.Position = m_header.DirTable + table_offset;
var dir = ReadDirEntry();
if (dir.DirOffset != -1 && dir.ParentDirOffset != -1)
{
m_input.BaseStream.Position = m_header.FileTable + dir.DirOffset;
m_input.Position = m_header.FileTable + dir.DirOffset;
root = Path.Combine (root, ExtractFileName (m_input.ReadUInt32()));
}
long current_pos = m_header.FileTable + dir.FileTable;
for (int i = 0; i < dir.FileCount; ++i)
{
m_input.BaseStream.Position = current_pos;
m_input.Position = current_pos;
uint name_offset = m_input.ReadUInt32();
uint attr = m_input.ReadUInt32();
m_input.BaseStream.Seek (0x18, SeekOrigin.Current);
m_input.Seek (0x18, SeekOrigin.Current);
uint offset = m_input.ReadUInt32();
if (0 != (attr & 0x10)) // FILE_ATTRIBUTE_DIRECTORY
{
@@ -377,26 +453,75 @@ namespace GameRes.Formats.DxLib
current_pos += m_entry_size;
}
}
}
string ExtractFileName (uint table_offset)
internal sealed class IndexReaderV6 : IndexReader
{
readonly int m_entry_size;
public IndexReaderV6 (DxHeader header, int version, Stream input) : base (header, version, input)
{
m_input.BaseStream.Position = table_offset;
int name_offset = m_input.ReadUInt16() * 4 + 4;
m_input.BaseStream.Position = table_offset + name_offset;
return m_input.BaseStream.ReadCString();
m_entry_size = 0x40;
}
#region IDisposable Members
bool disposed = false;
public void Dispose ()
private class DxDirectory
{
if (!disposed)
public long DirOffset;
public long ParentDirOffset;
public int FileCount;
public long FileTable;
}
DxDirectory ReadDirEntry ()
{
var dir = new DxDirectory();
dir.DirOffset = m_input.ReadInt64();
dir.ParentDirOffset = m_input.ReadInt64();
dir.FileCount = (int)m_input.ReadInt64();
dir.FileTable = m_input.ReadInt64();
return dir;
}
protected override void ReadFileTable (string root, long table_offset)
{
m_input.Position = m_header.DirTable + table_offset;
var dir = ReadDirEntry();
if (dir.DirOffset != -1 && dir.ParentDirOffset != -1)
{
m_input.Dispose();
disposed = true;
m_input.Position = m_header.FileTable + dir.DirOffset;
root = Path.Combine (root, ExtractFileName (m_input.ReadInt64()));
}
long current_pos = m_header.FileTable + dir.FileTable;
for (int i = 0; i < dir.FileCount; ++i)
{
m_input.Position = current_pos;
var name_offset = m_input.ReadInt64();
uint attr = (uint)m_input.ReadInt64();
m_input.Seek (0x18, SeekOrigin.Current);
var offset = m_input.ReadInt64();
if (0 != (attr & 0x10)) // FILE_ATTRIBUTE_DIRECTORY
{
if (0 == offset || table_offset == offset)
throw new InvalidFormatException ("Infinite recursion in DXA directory index");
ReadFileTable (root, offset);
}
else
{
var size = m_input.ReadInt64();
var packed_size = m_input.ReadInt64();
var entry = FormatCatalog.Instance.Create<PackedEntry> (Path.Combine (root, ExtractFileName (name_offset)));
entry.Offset = m_header.BaseOffset + offset;
entry.UnpackedSize = (uint)size;
entry.IsPacked = -1 != packed_size;
if (entry.IsPacked)
entry.Size = (uint)packed_size;
else
entry.Size = (uint)size;
m_dir.Add (entry);
}
current_pos += m_entry_size;
}
}
#endregion
}
internal class EncryptedStream : ProxyStream

View File

@@ -693,6 +693,8 @@ namespace GameRes.Formats.Emote
ReadRgba4444 (pixels, stride);
else if ("RL" == m_info.TexType)
ReadRle (pixels, stride);
else if ("DXT5" == m_info.TexType)
pixels = ReadDxt5();
else
throw new NotImplementedException (string.Format ("PSB texture format '{0}' not implemented", m_info.TexType));
return ImageData.Create (m_info, PixelFormats.Bgra32, null, pixels, stride);
@@ -799,5 +801,12 @@ namespace GameRes.Formats.Emote
}
}
}
byte[] ReadDxt5 ()
{
var packed = m_input.ReadBytes ((int)m_input.Length);
var dxt = new DirectDraw.DxtDecoder (packed, m_info);
return dxt.UnpackDXT5();
}
}
}

View File

@@ -0,0 +1,251 @@
//! \file ArcAN21.cs
//! \date Sun Apr 30 21:04:25 2017
//! \brief KaGuYa script engine animation resource.
//
// Copyright (C) 2017 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 System.Windows.Media;
namespace GameRes.Formats.Kaguya
{
class An21Entry : PackedEntry
{
public int FrameIndex;
public int RleStep;
}
[Export(typeof(ArchiveFormat))]
public class An21Opener : ArchiveFormat
{
public override string Tag { get { return "AN21/KAGUYA"; } }
public override string Description { get { return "KaGuYa script engine animation resource"; } }
public override uint Signature { get { return 0x31324E41; } } // 'AN21'
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
public An21Opener ()
{
Extensions = new string[] { "anm" };
}
public override ArcFile TryOpen (ArcView file)
{
int table_count = file.View.ReadUInt16 (4);
uint current_offset = 8;
for (int i = 0; i < table_count; ++i)
{
switch (file.View.ReadByte (current_offset++))
{
case 0: break;
case 1: current_offset += 8; break;
case 2:
case 3:
case 4:
case 5: current_offset += 4; break;
default: return null;
}
}
current_offset += 2 + file.View.ReadUInt16 (current_offset) * 8u;
if (!file.View.AsciiEqual (current_offset, "[PIC]10"))
return null;
current_offset += 7;
int frame_count = file.View.ReadInt16 (current_offset);
if (!IsSaneCount (frame_count))
return null;
current_offset += 0x12;
string base_name = Path.GetFileNameWithoutExtension (file.Name);
var dir = new List<Entry> (frame_count);
var info = new ImageMetaData
{
OffsetX = file.View.ReadInt32 (current_offset),
OffsetY = file.View.ReadInt32 (current_offset+4),
Width = file.View.ReadUInt32 (current_offset+8),
Height = file.View.ReadUInt32 (current_offset+12),
};
int channels = file.View.ReadInt32 (current_offset+0x10);
info.BPP = channels * 8;
current_offset += 0x14;
var entry = new An21Entry
{
FrameIndex = 0,
Name = string.Format ("{0}#{1:D2}", base_name, 0),
Type = "image",
Offset = current_offset,
Size = (uint)channels * info.Width * info.Height,
IsPacked = false,
};
dir.Add (entry);
current_offset += entry.Size;
for (int i = 1; i < frame_count; ++i)
{
int step = file.View.ReadByte (current_offset++);
if (0 == step)
return null;
uint packed_size = file.View.ReadUInt32 (current_offset);
uint unpacked_size = (uint)(channels * (info.OffsetX + (int)info.Width)
* (info.OffsetY + (int)info.Height));
current_offset += 4;
entry = new An21Entry
{
FrameIndex = i,
Name = string.Format ("{0}#{1:D2}", base_name, i),
Type = "image",
Offset = current_offset,
Size = packed_size,
UnpackedSize = unpacked_size,
IsPacked = true,
RleStep = step,
};
dir.Add (entry);
current_offset += packed_size;
}
return new An21Archive (file, this, dir, info);
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
var anent = entry as An21Entry;
var input = arc.File.CreateStream (entry.Offset, entry.Size);
if (null == anent || !anent.IsPacked)
return input;
using (input)
{
var data = DecompressRLE (input, anent.UnpackedSize, anent.RleStep);
return new BinMemoryStream (data);
}
}
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
{
var anarc = (An21Archive)arc;
var anent = (An21Entry)entry;
var pixels = anarc.GetFrame (anent.FrameIndex);
return new BitmapDecoder (pixels, anarc.ImageInfo);
}
internal static byte[] DecompressRLE (IBinaryStream input, uint unpacked_size, int rle_step)
{
var output = new byte[unpacked_size];
for (int i = 0; i < rle_step; ++i)
{
byte v1 = input.ReadUInt8();
output[i] = v1;
int dst = i + rle_step;
while (dst < output.Length)
{
byte v2 = input.ReadUInt8();
output[dst] = v2;
dst += rle_step;
if (v2 == v1)
{
int count = input.ReadUInt8();
if (0 != (count & 0x80))
count = input.ReadUInt8() + ((count & 0x7F) << 8) + 128;
while (count --> 0 && dst < output.Length)
{
output[dst] = v2;
dst += rle_step;
}
if (dst < output.Length)
{
v2 = input.ReadUInt8();
output[dst] = v2;
dst += rle_step;
}
}
v1 = v2;
}
}
return output;
}
}
class An21Archive : AnmArchive
{
byte[][] Frames;
public An21Archive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, ImageMetaData base_info)
: base (arc, impl, dir, base_info)
{
Frames = new byte[dir.Count][];
}
public byte[] GetFrame (int index)
{
if (index >= Frames.Length)
throw new ArgumentException ("index");
if (null != Frames[index])
return Frames[index];
var entry = Dir.ElementAt (index);
byte[] pixels;
using (var stream = OpenEntry (entry))
{
pixels = new byte[stream.Length];
stream.Read (pixels, 0, pixels.Length);
}
if (index > 0)
{
var prev_frame = GetFrame (index-1);
for (int i = 0; i < pixels.Length; ++i)
pixels[i] += prev_frame[i];
}
Frames[index] = pixels;
return pixels;
}
}
class BitmapDecoder : IImageDecoder
{
public Stream Source { get { return null; } }
public ImageFormat SourceFormat { get { return null; } }
public ImageMetaData Info { get; private set; }
public ImageData Image { get; private set; }
public BitmapDecoder (byte[] pixels, ImageMetaData info)
{
Info = info;
int stride = (int)info.Width * info.BPP / 8;
Image = ImageData.CreateFlipped (info, GetFormat(), null, pixels, stride);
}
PixelFormat GetFormat ()
{
switch (Info.BPP)
{
case 8: return PixelFormats.Gray8;
case 24: return PixelFormats.Bgr24;
case 32: return PixelFormats.Bgra32;
default: throw new InvalidFormatException();
}
}
public void Dispose ()
{
}
}
}

View File

@@ -2,7 +2,7 @@
//! \date Fri Jan 22 18:44:56 2016
//! \brief KaGuYa archive format.
//
// Copyright (C) 2016 by morkt
// Copyright (C) 2016-2017 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
@@ -436,13 +436,14 @@ namespace GameRes.Formats.Kaguya
public static ParamsDeserializer Create (IBinaryStream input)
{
var header = input.ReadHeader (0x11);
if (!header.AsciiEqual ("[SCR-PARAMS]"))
return null;
if (header.AsciiEqual (12, "v02"))
return new ParamsV2Deserializer (input);
else if (header.AsciiEqual (12, "v05.6"))
return new ParamsV5Deserializer (input);
return null;
if (header.AsciiEqual ("[SCR-PARAMS]"))
{
if (header.AsciiEqual (12, "v02"))
return new ParamsV2Deserializer (input);
else if (header.AsciiEqual (12, "v05.5") || header.AsciiEqual (12, "v05.6"))
return new ParamsV5Deserializer (input);
}
throw new UnknownEncryptionScheme();
}
public virtual LinkEncryption GetEncryption ()
@@ -642,7 +643,10 @@ namespace GameRes.Formats.Kaguya
new Tuple<string, Decryptor> ("AP", (a, e) => DecryptImage (a, e, 0xC)),
};
if (anm_encrypted)
{
table.Add (new Tuple<string, Decryptor> ("AN00", (a, e) => DecryptAn00 (a, e)));
table.Add (new Tuple<string, Decryptor> ("AN21", (a, e) => DecryptAn21 (a, e)));
}
m_type_table = table.ToArray();
}
@@ -683,6 +687,34 @@ namespace GameRes.Formats.Kaguya
return new BinMemoryStream (data, entry.Name);
}
Stream DecryptAn21 (LinkArchive arc, LinkEntry entry)
{
var data = arc.File.View.ReadBytes (entry.Offset, entry.Size);
int count = data.ToUInt16 (4);
int offset = 8;
for (int i = 0; i < count; ++i)
{
switch (data[offset++])
{
case 0: break;
case 1: offset += 8; break;
case 2:
case 3:
case 4:
case 5: offset += 4; break;
default: return new BinMemoryStream (data, entry.Name);
}
}
count = data.ToUInt16 (offset);
offset += 2 + count * 8 + 0x21;
int w = data.ToInt32 (offset);
int h = data.ToInt32 (offset+4);
int channels = data.ToInt32 (offset+8);
offset += 12;
DecryptData (data, offset, channels * w * h);
return new BinMemoryStream (data, entry.Name);
}
void DecryptData (byte[] data, int index, int length)
{
while (length > 0)

View File

@@ -184,17 +184,16 @@ namespace GameRes.Formats.KiriKiri
entry.Size = (uint)packed_size;
entry.UnpackedSize = (uint)file_size;
int name_size = header.ReadInt16();
if (name_size > 0x100 || name_size <= 0)
{
goto NextEntry;
}
if (entry.IsEncrypted || ForceEncryptionQuery)
entry.Cipher = crypt_algorithm.Value;
else
entry.Cipher = NoCryptAlgorithm;
var name = new string (header.ReadChars (name_size));
var name = entry.Cipher.ReadName (header);
if (null == name)
{
goto NextEntry;
}
if (entry.Cipher.ObfuscatedIndex && ObfuscatedPathRe.IsMatch (name))
{
goto NextEntry;

View File

@@ -71,6 +71,18 @@ namespace GameRes.Formats.KiriKiri
public virtual void Init (ArcFile arc)
{
}
/// <summary>
/// Read entry name from archive index.
/// </summary>
public virtual string ReadName (BinaryReader header)
{
int name_size = header.ReadInt16();
if (name_size > 0 && name_size <= 0x100)
return new string (header.ReadChars (name_size));
else
return null;
}
}
[Serializable]
@@ -976,4 +988,142 @@ namespace GameRes.Formats.KiriKiri
Decrypt (entry, offset, data, pos, count);
}
}
[Serializable]
public class PuCaCrypt : ICrypt
{
public uint[] HashTable;
public byte[] KeyTable;
public override void Decrypt (Xp3Entry entry, long offset, byte[] buffer, int pos, int count)
{
if (HashTable != null)
{
int i = Array.IndexOf (HashTable, entry.Hash);
if (i != -1)
{
for (int j = 0; j < count; ++j)
buffer[pos+j] ^= KeyTable[i];
return;
}
}
var hash_table = new byte[32];
uint hash = entry.Hash;
for (int k = 0; k < 32; k += 4)
{
if (0 != (hash & 1))
hash |= 0x80000000;
else
hash &= 0x7FFFFFFF;
LittleEndian.Pack (hash, hash_table, k);
hash >>= 1;
}
var key_table = new byte[0x400];
for (int l = 0; l < 32; ++l)
{
for (int m = 0; m < 32; ++m)
key_table[32 * l + m] = (byte)(~hash_table[l] ^ hash_table[m]);
}
for (int n = 0; n < count; ++n)
buffer[pos+n] ^= key_table[(offset + n) & 0x3FF];
}
public override void Encrypt (Xp3Entry entry, long offset, byte[] buffer, int pos, int count)
{
Decrypt (entry, offset, buffer, pos, count);
}
}
[Serializable]
public class RhapsodyCrypt : ICrypt
{
public string FileListName { get; set; }
public override void Decrypt (Xp3Entry entry, long offset, byte[] buffer, int pos, int count)
{
var key = new byte[12];
LittleEndian.Pack (entry.Hash, key, 0);
LittleEndian.Pack (0x6E1DA9B2u, key, 4);
LittleEndian.Pack (0x0040C800u, key, 8);
int k = (int)(offset % 12);
for (int i = 0; i < count; ++i)
{
buffer[pos+i] ^= key[k++];
if (12 == k)
k = 0;
}
}
public override void Encrypt (Xp3Entry entry, long offset, byte[] buffer, int pos, int count)
{
Decrypt (entry, offset, buffer, pos, count);
}
public override string ReadName (BinaryReader header)
{
if (null == KnownNames)
ReadNames();
uint key = header.ReadUInt32();
uint name_hash = header.ReadUInt32() ^ key;
string name;
if (KnownNames.TryGetValue (name_hash, out name))
return name;
uint ext_hash = header.ReadUInt32() ^ key;
name = name_hash.ToString ("X8");
switch (ext_hash)
{
case 0x01854675: name += ".png"; break; // GetNameHash (".png")
case 0x03D435DE: name += ".map"; break; // GetNameHash (".map")
case 0x2D1F13E0: name += ".asd"; break; // GetNameHash (".asd")
case 0x482F4319: name += ".tjs"; break; // GetNameHash (".tjs")
case 0x58924012: name += ".txt"; break; // GetNameHash (".txt")
case 0xB01C48CA: name += ".ks"; break; // GetNameHash (".ks")
case 0xC0F7DFB2: name += ".wav"; break; // GetNameHash (".wav")
case 0xE3A31D19: name += ".jpg"; break; // GetNameHash (".jpg")
case 0xE7F3FEEB: name += ".ogg"; break; // GetNameHash (".ogg")
default: name += ext_hash.ToString ("X8"); break;
}
return name;
}
static uint GetNameHash (string name)
{
uint hash = 0;
for (int i = 0; i < name.Length; ++i)
{
int c = char.ToLowerInvariant (name[i]);
hash = 0x1000193u * hash ^ (byte)c;
hash = 0x1000193u * hash ^ (byte)(c >> 8);
}
return hash;
}
void ReadNames ()
{
var dir = FormatCatalog.Instance.DataDirectory;
var names_file = Path.Combine (dir, FileListName);
var names = new Dictionary<uint, string>();
try
{
using (var reader = new StreamReader (names_file))
{
for (;;)
{
var name = reader.ReadLine();
if (null == name)
break;
names[GetNameHash (name)] = name;
}
}
}
catch (Exception X)
{
System.Diagnostics.Trace.WriteLine (X.Message, "[RhapsodyCrypt]");
}
KnownNames = names;
}
[NonSerialized]
Dictionary<uint, string> KnownNames = null;
}
}

View File

@@ -27,8 +27,6 @@ using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Text;
using GameRes.Cryptography;
using GameRes.Utility;
namespace GameRes.Formats.Malie
@@ -115,46 +113,15 @@ namespace GameRes.Formats.Malie
public class MalieArchive : ArcFile
{
public readonly Camellia Encryption;
public readonly IMalieDecryptor Decryptor;
public MalieArchive (ArcView file, ArchiveFormat format, ICollection<Entry> dir, Camellia encryption)
public MalieArchive (ArcView file, ArchiveFormat format, ICollection<Entry> dir, IMalieDecryptor decr)
: base (file, format, dir)
{
Encryption = encryption;
Decryptor = decr;
}
}
[Serializable]
public class LibScheme
{
public uint DataAlign;
public uint[] Key;
public LibScheme (uint[] key) : this (0x1000, key)
{
}
public LibScheme (uint align, uint[] key)
{
DataAlign = align;
Key = key;
}
public LibScheme (string key) : this (Camellia.GenerateKey (key))
{
}
public LibScheme (uint align, string key) : this (align, Camellia.GenerateKey (key))
{
}
}
[Serializable]
public class MalieScheme : ResourceScheme
{
public Dictionary<string, LibScheme> KnownSchemes;
}
[Export(typeof(ArchiveFormat))]
public class DatOpener : ArchiveFormat
{
@@ -167,6 +134,7 @@ namespace GameRes.Formats.Malie
public DatOpener ()
{
Extensions = new string[] { "lib", "dat" };
Signatures = new uint[] { 0, 0x3F503FB1 };
}
public override ArcFile TryOpen (ArcView file)
@@ -176,19 +144,19 @@ namespace GameRes.Formats.Malie
var header = new byte[0x10];
foreach (var scheme in KnownSchemes.Values)
{
var encryption = new Camellia (scheme.Key);
ReadEncrypted (file.View, encryption, 0, header, 0, 0x10);
var decryptor = scheme.CreateDecryptor();
ReadEncrypted (file.View, decryptor, 0, header, 0, 0x10);
ILibIndexReader reader;
if (Binary.AsciiEqual (header, 0, "LIBP"))
reader = new LibPReader (file, encryption, header, scheme);
reader = new LibPReader (file, decryptor, header, scheme);
else if (Binary.AsciiEqual (header, 0, "LIBU"))
reader = LibUReader.Create (file, encryption);
reader = LibUReader.Create (file, decryptor);
else
continue;
using (reader)
{
if (reader.ReadIndex())
return new MalieArchive (file, this, reader.Dir, encryption);
return new MalieArchive (file, this, reader.Dir, decryptor);
}
}
return null;
@@ -199,25 +167,25 @@ namespace GameRes.Formats.Malie
var march = arc as MalieArchive;
if (null == march)
return arc.File.CreateStream (entry.Offset, entry.Size);
var input = new EncryptedStream (march.File, march.Encryption);
var input = new EncryptedStream (march.File, march.Decryptor);
return new StreamRegion (input, entry.Offset, entry.Size);
}
internal abstract class LibIndexReader : ILibIndexReader
{
protected ArcView.Frame m_view;
protected readonly long m_max_offset;
protected Camellia m_enc;
protected List<Entry> m_dir = new List<Entry>();
protected byte[] m_header;
protected ArcView.Frame m_view;
protected readonly long m_max_offset;
protected IMalieDecryptor m_dec;
protected List<Entry> m_dir = new List<Entry>();
protected byte[] m_header;
public List<Entry> Dir { get { return m_dir; } }
protected LibIndexReader (ArcView file, Camellia encryption, byte[] header)
protected LibIndexReader (ArcView file, IMalieDecryptor decryptor, byte[] header)
{
m_view = file.View;
m_max_offset = file.MaxOffset;
m_enc = encryption;
m_dec = decryptor;
m_header = header;
}
@@ -238,16 +206,16 @@ namespace GameRes.Formats.Malie
internal class LibPReader : LibIndexReader
{
byte[] m_index;
long m_base_offset;
long m_data_align;
uint[] m_offset_table;
byte[] m_index;
long m_base_offset;
uint[] m_offset_table;
LibScheme m_scheme;
public LibPReader (ArcView file, Camellia encryption, byte[] header, LibScheme scheme)
: base (file, encryption, header)
public LibPReader (ArcView file, IMalieDecryptor decryptor, byte[] header, LibScheme scheme)
: base (file, decryptor, header)
{
m_base_offset = 0;
m_data_align = scheme.DataAlign - 1;
m_scheme = scheme;
}
public override bool ReadIndex ()
@@ -261,16 +229,16 @@ namespace GameRes.Formats.Malie
var offsets = new byte[4 * offset_count];
m_base_offset += 0x10;
if (m_index.Length != ReadEncrypted (m_view, m_enc, m_base_offset, m_index, 0, m_index.Length))
if (m_index.Length != ReadEncrypted (m_view, m_dec, m_base_offset, m_index, 0, m_index.Length))
return false;
m_base_offset += m_index.Length;
if (offsets.Length != ReadEncrypted (m_view, m_enc, m_base_offset, offsets, 0, offsets.Length))
if (offsets.Length != ReadEncrypted (m_view, m_dec, m_base_offset, offsets, 0, offsets.Length))
return false;
m_offset_table = new uint[offset_count];
Buffer.BlockCopy (offsets, 0, m_offset_table, 0, offsets.Length);
m_base_offset += offsets.Length;
m_base_offset = (m_base_offset + m_data_align) & ~m_data_align;
m_base_offset = m_scheme.GetAlignedOffset (m_base_offset);
m_dir.Capacity = offset_count;
ReadDir ("", 0, 1);
@@ -306,7 +274,7 @@ namespace GameRes.Formats.Malie
}
}
private static int ReadEncrypted (ArcView.Frame view, Camellia enc, long offset, byte[] buffer, int index, int length)
private static int ReadEncrypted (ArcView.Frame view, IMalieDecryptor dec, long offset, byte[] buffer, int index, int length)
{
int offset_pad = (int)offset & 0xF;
int aligned_len = (offset_pad + length + 0xF) & ~0xF;
@@ -328,7 +296,7 @@ namespace GameRes.Formats.Malie
for (int block_count = aligned_len / 0x10; block_count > 0; --block_count)
{
enc.DecryptBlock (offset, aligned_buf, block);
dec.DecryptBlock (offset, aligned_buf, block);
block += 0x10;
offset += 0x10;
}
@@ -345,110 +313,4 @@ namespace GameRes.Formats.Malie
set { KnownSchemes = ((MalieScheme)value).KnownSchemes; }
}
}
internal class EncryptedStream : Stream
{
ArcView.Frame m_view;
Camellia m_enc;
long m_max_offset;
long m_position = 0;
byte[] m_current_block = new byte[BlockLength];
int m_current_block_length = 0;
long m_current_block_position = 0;
public const int BlockLength = 0x1000;
public Camellia Encryption { get { return m_enc; } }
public EncryptedStream (ArcView mmap, Camellia encryption)
{
m_view = mmap.CreateFrame();
m_enc = encryption;
m_max_offset = mmap.MaxOffset;
}
public override int Read (byte[] buf, int index, int count)
{
int total_read = 0;
bool refill_buffer = !(m_position >= m_current_block_position && m_position < m_current_block_position + m_current_block_length);
while (count > 0 && m_position < m_max_offset)
{
if (refill_buffer)
{
m_current_block_position = m_position & ~((long)BlockLength-1);
FillBuffer();
}
int src_offset = (int)m_position & (BlockLength-1);
int available = Math.Min (count, m_current_block_length - src_offset);
Buffer.BlockCopy (m_current_block, src_offset, buf, index, available);
m_position += available;
total_read += available;
index += available;
count -= available;
refill_buffer = true;
}
return total_read;
}
private void FillBuffer ()
{
m_current_block_length = m_view.Read (m_current_block_position, m_current_block, 0, (uint)BlockLength);
for (int offset = 0; offset < m_current_block_length; offset += 0x10)
{
m_enc.DecryptBlock (m_current_block_position+offset, m_current_block, offset);
}
}
#region IO.Stream methods
public override bool CanRead { get { return !m_disposed; } }
public override bool CanWrite { get { return false; } }
public override bool CanSeek { get { return !m_disposed; } }
public override long Length { get { return m_max_offset; } }
public override long Position
{
get { return m_position; }
set { m_position = value; }
}
public override long Seek (long pos, SeekOrigin whence)
{
if (SeekOrigin.Current == whence)
m_position += pos;
else if (SeekOrigin.End == whence)
m_position = m_max_offset + pos;
else
m_position = pos;
return m_position;
}
public override void Write (byte[] buf, int index, int count)
{
throw new NotSupportedException();
}
public override void SetLength (long length)
{
throw new NotSupportedException();
}
public override void Flush ()
{
}
#endregion
#region IDisposable methods
bool m_disposed = false;
protected override void Dispose (bool disposing)
{
if (!m_disposed)
{
if (disposing)
m_view.Dispose();
m_disposed = true;
base.Dispose();
}
}
#endregion
}
}

View File

@@ -77,9 +77,9 @@ namespace GameRes.Formats.Malie
return new LibUReader (input);
}
public static LibUReader Create (ArcView file, Camellia encryption)
public static LibUReader Create (ArcView file, IMalieDecryptor decryptor)
{
var input = new EncryptedStream (file, encryption);
var input = new EncryptedStream (file, decryptor);
return new LibUReader (input);
}

View File

@@ -0,0 +1,100 @@
//! \file LibScheme.cs
//! \date Tue Jun 06 22:47:22 2017
//! \brief Malie encryption schemes.
//
// Copyright (C) 2017 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 GameRes.Cryptography;
namespace GameRes.Formats.Malie
{
[Serializable]
public abstract class LibScheme
{
uint DataAlign;
public LibScheme (uint align)
{
DataAlign = align;
}
public abstract IMalieDecryptor CreateDecryptor ();
public virtual long GetAlignedOffset (long offset)
{
long align = DataAlign - 1;
return (offset + align) & ~align;
}
}
[Serializable]
public class LibCamelliaScheme : LibScheme
{
public uint[] Key { get; set; }
public LibCamelliaScheme (uint[] key) : this (0x1000, key)
{
}
public LibCamelliaScheme (uint align, uint[] key) : base (align)
{
Key = key;
}
public LibCamelliaScheme (string key) : this (Camellia.GenerateKey (key))
{
}
public LibCamelliaScheme (uint align, string key) : this (align, Camellia.GenerateKey (key))
{
}
public override IMalieDecryptor CreateDecryptor ()
{
return new CamelliaDecryptor (Key);
}
}
[Serializable]
public class LibCfiScheme : LibScheme
{
public byte[] Key { get; set; }
public LibCfiScheme (uint align, byte[] key) : base (align)
{
Key = key;
}
public override IMalieDecryptor CreateDecryptor ()
{
return new CfiDecryptor (Key);
}
}
[Serializable]
public class MalieScheme : ResourceScheme
{
public Dictionary<string, LibScheme> KnownSchemes;
}
}

View File

@@ -0,0 +1,200 @@
//! \file MalieEncryption.cs
//! \date Tue Jun 06 20:38:57 2017
//! \brief Malie System encryption implementation.
//
// Copyright (C) 2017 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.IO;
using GameRes.Cryptography;
using GameRes.Utility;
namespace GameRes.Formats.Malie
{
public interface IMalieDecryptor
{
void DecryptBlock (long block_offset, byte[] buffer, int index);
}
public class CamelliaDecryptor : IMalieDecryptor
{
Camellia m_enc;
public CamelliaDecryptor (uint[] key)
{
m_enc = new Camellia (key);
}
public void DecryptBlock (long block_offset, byte[] buffer, int index)
{
m_enc.DecryptBlock (block_offset, buffer, index);
}
}
public class CfiDecryptor : IMalieDecryptor
{
byte[] m_key;
public CfiDecryptor (byte[] key)
{
m_key = key;
}
public void DecryptBlock (long block_offset, byte[] data, int index)
{
if (null == data)
throw new ArgumentNullException ("data");
if (index < 0 || index + 0x10 > data.Length)
throw new ArgumentOutOfRangeException ("index");
int offset = (int)block_offset;
int o = offset & 0xF;
byte first = data[index+o];
for (int i = 0; i < 0x10; ++i)
{
if (o != i)
data[index+i] ^= first;
}
offset >>= 4;
unsafe
{
fixed (byte* data8 = &data[index])
{
uint* data32 = (uint*)data8;
uint k = Binary.RotR (0x39653542, m_key[offset & 0x1F] ^ 0xA5);
data32[0] = Binary.RotR (data32[0] ^ k, m_key[(offset + 12) & 0x1F] ^ 0xA5);
k = Binary.RotL (0x76706367, m_key[(offset + 3) & 0x1F] ^ 0xA5);
data32[1] = Binary.RotL (data32[1] ^ k, m_key[(offset + 15) & 0x1F] ^ 0xA5);
k = Binary.RotR (0x69454462, m_key[(offset + 6) & 0x1F] ^ 0xA5);
data32[2] = Binary.RotR (data32[2] ^ k, m_key[(offset - 14) & 0x1F] ^ 0xA5);
k = Binary.RotL (0x71334334, m_key[(offset + 9) & 0x1F] ^ 0xA5);
data32[3] = Binary.RotL (data32[3] ^ k, m_key[(offset - 11) & 0x1F] ^ 0xA5);
}
}
}
}
internal class EncryptedStream : Stream
{
ArcView.Frame m_view;
IMalieDecryptor m_dec;
long m_max_offset;
long m_position = 0;
byte[] m_current_block = new byte[BlockLength];
int m_current_block_length = 0;
long m_current_block_position = 0;
public const int BlockLength = 0x1000;
public IMalieDecryptor Decryptor { get { return m_dec; } }
public EncryptedStream (ArcView mmap, IMalieDecryptor decryptor)
{
m_view = mmap.CreateFrame();
m_dec = decryptor;
m_max_offset = mmap.MaxOffset;
}
public override int Read (byte[] buf, int index, int count)
{
int total_read = 0;
bool refill_buffer = !(m_position >= m_current_block_position && m_position < m_current_block_position + m_current_block_length);
while (count > 0 && m_position < m_max_offset)
{
if (refill_buffer)
{
m_current_block_position = m_position & ~((long)BlockLength-1);
FillBuffer();
}
int src_offset = (int)m_position & (BlockLength-1);
int available = Math.Min (count, m_current_block_length - src_offset);
Buffer.BlockCopy (m_current_block, src_offset, buf, index, available);
m_position += available;
total_read += available;
index += available;
count -= available;
refill_buffer = true;
}
return total_read;
}
private void FillBuffer ()
{
m_current_block_length = m_view.Read (m_current_block_position, m_current_block, 0, (uint)BlockLength);
for (int offset = 0; offset < m_current_block_length; offset += 0x10)
{
m_dec.DecryptBlock (m_current_block_position+offset, m_current_block, offset);
}
}
#region IO.Stream methods
public override bool CanRead { get { return !m_disposed; } }
public override bool CanWrite { get { return false; } }
public override bool CanSeek { get { return !m_disposed; } }
public override long Length { get { return m_max_offset; } }
public override long Position
{
get { return m_position; }
set { m_position = value; }
}
public override long Seek (long pos, SeekOrigin whence)
{
if (SeekOrigin.Current == whence)
m_position += pos;
else if (SeekOrigin.End == whence)
m_position = m_max_offset + pos;
else
m_position = pos;
return m_position;
}
public override void Write (byte[] buf, int index, int count)
{
throw new NotSupportedException();
}
public override void SetLength (long length)
{
throw new NotSupportedException();
}
public override void Flush ()
{
}
#endregion
#region IDisposable methods
bool m_disposed = false;
protected override void Dispose (bool disposing)
{
if (!m_disposed)
{
if (disposing)
m_view.Dispose();
m_disposed = true;
base.Dispose();
}
}
#endregion
}
}

View File

@@ -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.2.32.1373")]
[assembly: AssemblyFileVersion ("1.2.32.1373")]
[assembly: AssemblyVersion ("1.2.34.1396")]
[assembly: AssemblyFileVersion ("1.2.34.1396")]

View File

Binary file not shown.

View File

@@ -57,6 +57,7 @@ namespace GameRes.Formats.Will
ArcOpener ()
{
Extensions = new string[] { "arc" };
Signatures = new uint[] { 1, 0 };
}
public override ArcFile TryOpen (ArcView file)

View File

@@ -0,0 +1,204 @@
//! \file ArcHibiki.cs
//! \date Fri May 12 19:06:19 2017
//! \brief Hibiki Works resource archive.
//
// Copyright (C) 2017 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.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using GameRes.Utility;
namespace GameRes.Formats.YaneSDK
{
[Export(typeof(ArchiveFormat))]
public class HDatOpener : ArchiveFormat
{
public override string Tag { get { return "DAT/hibiki"; } }
public override string Description { get { return "YaneSDK engine resource archive"; } }
public override uint Signature { get { return 0; } }
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
public static readonly string SchemeFileName = "hibiki_works.dat";
public override ArcFile TryOpen (ArcView file)
{
if (!file.Name.HasExtension (".dat"))
return null;
int count = (short)(file.View.ReadUInt16 (0) ^ 0x8080);
if (!IsSaneCount (count))
return null;
var scheme = QueryScheme (file.Name);
if (null == scheme)
return null;
var dat_name = Path.GetFileName (file.Name).ToLowerInvariant();
IList<HibikiTocRecord> toc_table = null;
if (scheme.ArcMap != null && scheme.ArcMap.TryGetValue (dat_name, out toc_table))
{
if (toc_table.Count != count)
toc_table = null;
}
var lst_name = Path.ChangeExtension (file.Name, ".lst");
Stream input;
if (VFS.FileExists (lst_name))
input = VFS.OpenStream (lst_name);
else
input = file.CreateStream();
using (var dec = new XoredStream (input, 0x80))
using (var index = new BinaryReader (dec))
{
const int name_length = 0x100;
index.BaseStream.Position = 2;
Func<int, Entry> read_entry;
if (null == toc_table)
{
var name_buf = new byte[name_length];
read_entry = i => {
if (name_length != index.Read (name_buf, 0, name_length))
return null;
var name = Binary.GetCString (name_buf, 0);
var entry = FormatCatalog.Instance.Create<Entry> (name);
index.ReadUInt16();
entry.Size = index.ReadUInt32();
entry.Offset = index.ReadUInt32();
return entry;
};
}
else
{
read_entry = i => {
index.BaseStream.Seek (name_length + 6, SeekOrigin.Current);
index.ReadUInt32(); // throws in case of EOF
var toc_entry = toc_table[i];
var entry = FormatCatalog.Instance.Create<Entry> (toc_entry.Name);
entry.Offset = toc_entry.Offset;
entry.Size = toc_entry.Size;
return entry;
};
}
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
var entry = read_entry (i);
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
}
return new HibikiArchive (file, this, dir, scheme.ContentKey);
}
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
var harc = arc as HibikiArchive;
if (null == harc)
return base.OpenEntry (arc, entry);
var key = harc.Key;
uint encrypted = Math.Min (entry.Size, (uint)key.Length);
var header = arc.File.View.ReadBytes (entry.Offset, encrypted);
for (int i = 0; i < header.Length; ++i)
header[i] ^= key[i];
if (encrypted == entry.Size)
return new BinMemoryStream (header);
var rest = arc.File.CreateStream (entry.Offset + encrypted, entry.Size - encrypted);
return new PrefixStream (header, rest);
}
HibikiDatScheme QueryScheme (string arc_name)
{
if (null == KnownSchemes)
return null;
// XXX add GUI widget to select scheme
return KnownSchemes.Values.FirstOrDefault();
}
static Lazy<HibikiScheme> s_Scheme = new Lazy<HibikiScheme> (DeserializeScheme);
internal IDictionary<string, HibikiDatScheme> KnownSchemes {
get { return s_Scheme.Value.KnownSchemes; }
}
static HibikiScheme DeserializeScheme ()
{
try
{
var dir = FormatCatalog.Instance.DataDirectory;
var scheme_file = Path.Combine (dir, SchemeFileName);
using (var input = File.OpenRead (scheme_file))
{
var bin = new BinaryFormatter();
return (HibikiScheme)bin.Deserialize (input);
}
}
catch (Exception X)
{
Trace.WriteLine (X.Message, "hibiki_works scheme deserialization failed");
return new HibikiScheme();
}
}
}
internal class HibikiArchive : ArcFile
{
public readonly byte[] Key;
public HibikiArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, byte[] key)
: base (arc, impl, dir)
{
Key = key;
}
}
[Serializable]
public class HibikiScheme : ResourceScheme
{
public IDictionary<string, HibikiDatScheme> KnownSchemes;
}
[Serializable]
public class HibikiDatScheme
{
public byte[] ContentKey;
public IDictionary<string, IList<HibikiTocRecord>> ArcMap;
}
[Serializable]
public class HibikiTocRecord
{
public string Name;
public uint Offset;
public uint Size;
public HibikiTocRecord (string name, uint offset, uint size)
{
Name = name;
Offset = offset;
Size = size;
}
}
}

View File

@@ -12,6 +12,7 @@
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<NuGetPackageImportStamp>9af0b529</NuGetPackageImportStamp>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -110,13 +111,6 @@
<PreBuildEvent>perl "$(SolutionDir)inc-revision.pl" "$(ProjectPath)" $(ConfigurationName)
exit 0</PreBuildEvent>
</PropertyGroup>
<Import Project="..\packages\System.Data.SQLite.Core.1.0.105.0\build\net45\System.Data.SQLite.Core.targets" Condition="Exists('..\packages\System.Data.SQLite.Core.1.0.105.0\build\net45\System.Data.SQLite.Core.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\System.Data.SQLite.Core.1.0.105.0\build\net45\System.Data.SQLite.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.Data.SQLite.Core.1.0.105.0\build\net45\System.Data.SQLite.Core.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@@ -30,6 +30,7 @@
<UseApplicationTrust>false</UseApplicationTrust>
<PublishWizardCompleted>true</PublishWizardCompleted>
<BootstrapperEnabled>true</BootstrapperEnabled>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>

View File

@@ -51,5 +51,5 @@ using System.Windows;
// 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.4.28.1865")]
[assembly: AssemblyFileVersion ("1.4.28.1865")]
[assembly: AssemblyVersion ("1.4.30.1888")]
[assembly: AssemblyFileVersion ("1.4.30.1888")]

View File

@@ -12,6 +12,7 @@
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>

View File

@@ -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.4.29.252")]
[assembly: AssemblyFileVersion ("1.4.29.252")]
[assembly: AssemblyVersion ("1.4.31.255")]
[assembly: AssemblyFileVersion ("1.4.31.255")]

View File

@@ -52,6 +52,7 @@ Remember11<br/>
Chou Dengeki Stryker<br/>
Chou Jikuu Bakuren Monogatari ~door pi chu~<br/>
Eiken Kikaku<br/>
Fluorite Memories<br/>
H2O -Footprints in the Sand-<br/>
Kanojo*Step<br/>
Melty Moment<br/>
@@ -216,6 +217,7 @@ Baldr Sky DiveX<br/>
Fossette ~Cafe au Le Ciel Bleu~<br/>
Jinki Extend Re:Vision<br/>
Maji de Watashi ni Koishinasai!<br/>
Platinum<br/>
Xross Scramble<br/>
</td></tr>
<tr><td>*.grp</td><td><tt>GR3</tt></td><td></td></tr>
@@ -277,7 +279,9 @@ Akazukin to Mayoi no Mori<br/>
Altered Pink ~Tokumu Sentai Duel Ranger~<br/>
Amairo*Islenauts<br/>
Aozora Gakko no Sensei-kun<br/>
Aqua<br/>
Bishuu ~Chigyaku no Mesu Dorei~<br/>
Boku to Koi Suru Ponkotsu Akuma.<br/>
Boku to Koi Suru Ponkotsu Akuma. Suggoi Ecchi!<br/>
Cafe Sourire<br/>
Clover Day's<br/>
@@ -292,8 +296,10 @@ Fair Child<br/>
Fantasical<br/>
Fate/stay night<br/>
Fate/hollow ataraxia<br/>
Fate/Knight Rhapsody ACT 2<br/>
G-senjou no Maou<br/>
Gakuen Butou no Folklore<br/>
Grisaia: Phantom Trigger Vol.2<br/>
Hachukano<br/>
Hanafubuki ~Sennen no Koi o Shimashita~<br/>
Haruiro ☆ Communication ♪<br/>
@@ -328,6 +334,8 @@ Ore no Saimin Fantasia<br/>
Otome*Domain<br/>
Ouka Ryouran<br/>
Oyako Ninjutsu Kunoichi PonPon!!<br/>
PRETTY x CATION<br/>
PURELY x CATION<br/>
Rakugaki Overheart<br/>
RGH ~Koi to Hero to Gakuen to~<br/>
Riajuu Saimin<br/>
@@ -348,11 +356,13 @@ Specialite!<br/>
Suiheisen made Nan Mile?<br/>
Swan Song<br/>
Teakamamire no Tenshi<br/>
Toaru Jukujo no Hentai Choukyou<br/>
Towazugatari ~Shoujo Ryoujoku Hishou~<br/>
Ushinawareta Mirai o Motomete<br/>
With Ribbon<br/>
Yome Sagashi ga Hakadori Sugite Yabai.<br/>
Yomibito Shirazu ~Amai Meshibe no Seichoushi~<br/>
Yotsunoha<br/>
Yuugu ~Nyomitsu Gangu Adesugata~<br/>
Yuugu 2 ~Dai Ni Kinsho no Nozomu Mono~<br/>
Zecchou Spiral!!<br/>
@@ -489,6 +499,7 @@ Bitch Nee-chan ga Seijun na Hazu ga Nai! <span class="footnote">ShiinaRio v2.50<
Bitch Shimai ga Seijun na Hazu ga Nai!! <span class="footnote">ShiinaRio v2.50</span><br/>
Calendar Girl <span class="footnote">ShiinaRio v2.02</span><br/>
Can Fes! ~Itazura Majo to Naisho no Gakuensai~ <span class="footnote">ShiinaRio v2.47</span><br/>
Chiccha na Hanayome ~Mada Mada Tsubomi da mon~ <span class="footnote">ShiinaRio v2.50</span><br/>
Chikan Circle <span class="footnote">ShiinaRio v2.46</span><br/>
Chikan Circle 2 <span class="footnote">ShiinaRio v2.47</span><br/>
Chuuchuu Nurse <span class="footnote">ShiinaRio v2.45</span><br/>
@@ -765,6 +776,7 @@ Kimon Youitan<br/>
Unbalance<br/>
</td></tr>
<tr class="odd"><td>*.lib<br/>*.dat</td><td><tt>LIB</tt><br/><tt>LIBP</tt><br/><tt>LIBU</tt><span class="footnote">encrypted</span></td><td></td><td rowspan="3">Malie</td><td rowspan="3">
Aki Uso -The only neat thing to do-<br/>
Angel Crown<br/>
Deep Love Diary<br/>
Dies irae<br/>
@@ -926,6 +938,7 @@ Soukai no Valkyria <br/>
<tr><td>*.dxa<br/>*.usi<br/>*.hud<br/>*.dat</td><td><tt>DX</tt><span class="footnote">encrypted</span></td><td></td><td rowspan="2">DxLib</td><td rowspan="2">
Ashita wa Kitto, Haremasu you ni<br/>
Cross Quartz<br/>
Detective Seven<br/>
Hyakki Yakou<br/>
Mahou Senshi Extra Stage 2 ~Gakuen Kangoku~<br/>
Saikyou Goshujin-sama! -Mighty My Master-<br/>
@@ -980,6 +993,7 @@ Moshimo Ashita ga Harenaraba<br/>
<tr><td>*.gyu</td><td><tt>GYU\x1a</tt></td><td></td><td rowspan="2">ExHIBIT</td><td rowspan="2">
Eve ~New Generation X~<br/>
Fuyu no Rondo<br/>
Imouto Paradise! 2<br/>
Natural Another One 2nd -Belladonna-<br/>
Oshiete Ecchi Na Recipe -Anata to Watashi no Ama~i Seikatsu!-<br/>
</td></tr>
@@ -1123,13 +1137,14 @@ Dokidoki Onee-san<br/>
Harami Tama<br/>
Mahokoi ~Ecchi na Mahou de Koi x Koi Shichau~<br/>
Mainichi ga M!<br/>
Meidokissa<br/>
Ningyou no Yakata<br/>
Osananajimi to Ama~ku Ecchi ni Sugosu Houhou<br/>
Toshishita Gentei Nuki x2 Share-house<br/>
</td></tr>
<tr class="odd"><td>*.alp</td><td><tt>AP-0</tt><br/><tt>AP-2</tt></td><td></td></tr>
<tr class="odd"><td>*.ap3</td><td><tt>\x04APS3</tt></td><td></td></tr>
<tr class="odd last"><td>*.anm</td><td><tt>AN00</tt></td><td></td></tr>
<tr class="odd last"><td>*.anm</td><td><tt>AN00</tt><br/><tt>AN21</tt></td><td></td></tr>
<tr><td>*.pcs</td><td><tt>PCCS</tt></td><td></td><td>C's ware</td><td>
Kuro to Kuro to Kuro no Saidan ~Kodoku~<br/>
Mikan<br/>
@@ -1171,7 +1186,6 @@ Angenehm Platz -Kleiner Garten Sie Erstellen-<br/>
</td></tr>
<tr class="odd"><td>*.arc</td><td><tt>ARCC</tt></td><td></td></tr>
<tr class="odd last"><td>*.bin</td><td><tt>ODIO</tt></td><td></td></tr>
</td></tr>
<tr><td>*.eme</td><td><tt>RREDATA</tt></td><td></td><td>Emon Engine</td><td>
Ase Nure Shoujo Misaki "Anata no Nioi de Icchau!"<br/>
D-spray Biyaku de Motemote Kachou Dairi Hosa<br/>
@@ -1323,6 +1337,7 @@ Roshutsu Hentai Yuugi<br/>
Ryou Seibai! ~Gakuen Bishoujo Seisai Hiroku~<br/>
Samayoi Inmu Kousha ~Konna H na Jugyou, Arienai!~<br/>
Sarai no Me<br/>
Shinyaku In'youchuu<br/>
Shukubo no Uzuki ~Hitozuma Miboujin no Nareta Karada to Amai Toiki~<br/>
Shukubo no Uzuki 2 ~Nareta Hitozuma kara Tadayou "Onna" no Iroka~<br/>
Shukujo no Tsuyagoto<br/>
@@ -1360,6 +1375,7 @@ Shokusou Tenshi Serika 2<br/>
</td></tr>
<tr class="odd"><td>*.dat</td><td>-</td><td></td><td>NekoSDK</td><td>
Elevator Panic ~Misshitsu no Inkou~<br/>
Yuuguu Settai #<br/>
</td></tr>
<tr><td>*.dat</td><td><tt>MK2.0</tt></td><td></td><td>MAIKA</td><td>
Inka Gakuen Taisen<br/>
@@ -1492,8 +1508,9 @@ Doreijou<br/>
<tr class="odd"><td>*.kgf</td><td><tt>KGF</tt></td><td></td></tr>
<tr class="odd"><td>*.arc</td><td><tt>DAF\x1A</tt></td><td></td></tr>
<tr class="odd last"><td>*.cgf</td><td><tt>CGF\x1A</tt></td><td></td></tr>
<tr><td>*.pfs</td><td><tt>pf2</tt><br/><tt>pf8</tt></td><td></td><td rowspan="2">Artemis Engine</td><td rowspan="2">
<tr><td>*.pfs</td><td><tt>pf2</tt><br/><tt>pf6</tt><br/><tt>pf8</tt></td><td></td><td rowspan="2">Artemis Engine</td><td rowspan="2">
Boku no Elf Onee-san<br/>
Memory's Dogma CODE:01<br/>
Tsugou no Ii Kanojo<br/>
Tsugou no Ii Kazoku<br/>
</td></tr>
@@ -1584,6 +1601,9 @@ Seisai no Resonance<br/>
Sengoku Hime 7<br/>
</td></tr>
<tr class="last"><td>*.ogg<br/>*.wav</td><td><tt>FSB5</tt></td><td></td></tr>
<tr class="odd"><td>*.dat</td><td>-</td><td></td><td>YaneSDK?</td><td>
Niizuma Lovely x Cation<br/>
</td></tr>
</table>
<p><a name="note-1" class="footnote">1</a> Non-encrypted only</p>
</body>

View File

@@ -1,21 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<GARbro>
<Release>
<Version>1.4.28</Version>
<Version>1.4.30</Version>
<Url>https://github.com/morkt/GARbro/releases/latest</Url>
<Notes>Updated Chinese translation.
New formats:
- 'UnityFS' archives and FSB5 audio
- GRP audio archives
- more KiriKiri and ShiinaRio encryption schemes
<Notes>New formats:
- 'pf6' archives
- DxLib version 6 archives (Detective Seven)
- updated encryption schemes for malie LIBP archives
- added more KiriKiri and ShiinaRio encryption schemes
</Notes>
</Release>
<FormatsData>
<FileVersion>63</FileVersion>
<FileVersion>72</FileVersion>
<Url>https://github.com/morkt/GARbro/raw/master/ArcFormats/Resources/Formats.dat</Url>
<Requires>
<Assembly Name="ArcFormats" Version="1.2.31.1367"/>
<Assembly Name="ArcFormats" Version="1.2.33.1392"/>
<Assembly Name="GameRes" Version="1.4.26.238"/>
</Requires>
</FormatsData>

View File

Binary file not shown.

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<repositories>
<repository path="..\ArcFormats\packages.config" />
<repository path="..\Experimental\packages.config" />
<repository path="..\GameRes\packages.config" />
<repository path="..\GUI\packages.config" />
</repositories>