Compare commits

...

20 Commits

Author SHA1 Message Date
morkt
afb13290f5 released v1.4.31 2017-08-15 16:20:05 +04:00
morkt
d6e441fe64 (KiriKiri): added variation of CX encryption scheme. 2017-08-15 16:07:54 +04:00
morkt
651aad92a2 (LibCfiScheme): added 'rotate key' scheme parameter. 2017-08-15 16:04:53 +04:00
morkt
0b80548a81 updated formats. 2017-08-14 15:08:24 +04:00
morkt
fd55204172 (TCD): assign "image" type to *.spd files. 2017-08-14 15:05:59 +04:00
morkt
532f58625c implemented SPD archives. 2017-08-14 15:05:24 +04:00
morkt
4b0506c9d0 (YPF): lookup game by executable name. 2017-08-14 15:04:25 +04:00
morkt
f40226866c (QLIE): updated 'FilePackVer3.1' archives support (#84) 2017-07-29 02:13:22 +04:00
morkt
8724e8131c (INT): added scheme lookup by executable name. 2017-07-28 16:53:34 +04:00
morkt
c499d4c707 updated formats. 2017-07-28 16:50:32 +04:00
morkt
b574d2fb96 updated formats. 2017-07-24 14:33:26 +04:00
morkt
c00747a38b (GUI): added CP936 to text encodings dropdown list (#82). 2017-07-19 07:19:53 +04:00
morkt
e1bfe9cb09 fixed cast. 2017-07-19 07:01:13 +04:00
morkt
40575baacb (MG2): alternative encryption (#81) 2017-07-17 11:07:21 +04:00
morkt
ad16b754ae (PngFormat.ReadMetaData): additional sanity checks. 2017-07-17 11:04:39 +04:00
morkt
3ad1c60dde updated formats. 2017-07-15 06:11:11 +04:00
morkt
b4ec3d229e (GBC): format variation. 2017-07-15 06:08:51 +04:00
morkt
cbab8b200a (PCF): alternative encryption scheme. 2017-07-15 06:03:04 +04:00
morkt
37539cc083 -debug. 2017-07-15 06:00:33 +04:00
morkt
eb53552bbc implemented PSM images (#80) 2017-07-07 19:47:14 +04:00
26 changed files with 692 additions and 126 deletions

View File

@@ -107,6 +107,7 @@
<Compile Include="Foster\ImageC24.cs" />
<Compile Include="GameSystem\ArcPureMail.cs" />
<Compile Include="GameSystem\AudioADP4.cs" />
<Compile Include="ImagePSM.cs" />
<Compile Include="Kaas\ArcPB.cs" />
<Compile Include="Kaas\AudioKAAS.cs" />
<Compile Include="Kaguya\ArcAN21.cs" />
@@ -124,6 +125,7 @@
<Compile Include="Qlie\Encryption.cs" />
<Compile Include="RealLive\ArcKOE.cs" />
<Compile Include="rUGP\AudioRHA.cs" />
<Compile Include="Slg\ArcSPD.cs" />
<Compile Include="Software House Parsley\ArcCG.cs" />
<Compile Include="Artemis\ArcPFS.cs" />
<Compile Include="AudioWMA.cs" />

View File

@@ -128,7 +128,7 @@ namespace GameRes.Formats.CatSystem
return null;
if (file.View.AsciiEqual (8, "__key__.dat\x00"))
{
uint? key = QueryEncryptionInfo();
uint? key = QueryEncryptionInfo (file.Name);
if (null == key)
throw new UnknownEncryptionScheme();
return OpenEncrypted (file, entry_count, key.Value);
@@ -274,8 +274,11 @@ namespace GameRes.Formats.CatSystem
return new GUI.CreateINTWidget();
}
uint? QueryEncryptionInfo ()
uint? QueryEncryptionInfo (string arc_name)
{
var title = FormatCatalog.Instance.LookupGame (arc_name);
if (!string.IsNullOrEmpty (title) && KnownSchemes.ContainsKey (title))
return KnownSchemes[title].Key;
var options = Query<IntOptions> (arcStrings.INTNotice);
return options.EncryptionInfo.GetKey();
}

View File

@@ -86,7 +86,7 @@ namespace GameRes.Formats.GUI
CheckPathExists = true,
Multiselect = false,
Title = arcStrings.INTChooseExe,
Filter = arcStrings.INTExeFiles+"|*.exe",
Filter = arcStrings.INTExeFiles+"|*.exe;*.bin",
FilterIndex = 1,
InitialDirectory = Directory.GetCurrentDirectory(),
};

View File

@@ -260,7 +260,6 @@ namespace GameRes.Formats.DxLib
if (0x2C != header.Length)
return null;
Decrypt (header, 0, header.Length, 4, key);
Dump.Write (header);
return new DxHeader {
IndexSize = LittleEndian.ToUInt32 (header, 0),
BaseOffset = LittleEndian.ToInt64 (header, 4),

63
ArcFormats/ImagePSM.cs Normal file
View File

@@ -0,0 +1,63 @@
//! \file ImagePSM.cs
//! \date Fri Jul 07 19:16:57 2017
//! \brief Obfuscated PNG image.
//
// 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.ComponentModel.Composition;
using System.IO;
namespace GameRes.Formats.Misc
{
[Export(typeof(ImageFormat))]
public class PsmFormat : PngFormat
{
public override string Tag { get { return "PSM"; } }
public override string Description { get { return "Obfuscated PNG image"; } }
public override uint Signature { get { return 0x474E50ED; } }
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
using (var png = DeobfuscateStream (file))
return base.Read (png, info);
}
IBinaryStream DeobfuscateStream (IBinaryStream file)
{
var header = file.ReadHeader (4).ToArray();
header[0] = 0x89;
var body = new StreamRegion (file.AsStream, 4, true);
var png = new PrefixStream (header, body);
return new BinaryStream (png, file.Name);
}
public override void Write (Stream file, ImageData image)
{
var start_pos = file.Position;
base.Write (file, image);
var end_pos = file.Position;
file.Position = start_pos;
file.WriteByte (0xED);
file.Position = end_pos;
}
}
}

View File

@@ -967,6 +967,22 @@ namespace GameRes.Formats.KiriKiri
Dictionary<string, string> KnownNames = null;
}
[Serializable]
public class NanaCxCrypt : SenrenCxCrypt
{
uint m_random_seed;
public NanaCxCrypt (CxScheme scheme, uint seed) : base (scheme)
{
m_random_seed = seed;
}
internal override CxProgram NewProgram (uint seed)
{
return new CxProgramNana (seed, m_random_seed, ControlBlock);
}
}
[Serializable]
public class KissCrypt : ICrypt
{

View File

@@ -217,7 +217,7 @@ namespace GameRes.Formats.KiriKiri
CxProgram GenerateProgram (uint seed)
{
var program = new CxProgram (seed, ControlBlock);
var program = NewProgram (seed);
for (int stage = 5; stage > 0; --stage)
{
if (EmitCode (program, stage))
@@ -228,6 +228,11 @@ namespace GameRes.Formats.KiriKiri
throw new CxProgramException ("Overly large CxEncryption bytecode");
}
internal virtual CxProgram NewProgram (uint seed)
{
return new CxProgram (seed, ControlBlock);
}
bool EmitCode (CxProgram program, int stage)
{
return program.EmitNop (5) // 0x57 0x56 0x53 0x51 0x52
@@ -437,7 +442,7 @@ namespace GameRes.Formats.KiriKiri
private List<uint> m_code = new List<uint> (LengthLimit);
private uint[] m_ControlBlock;
private int m_length;
private uint m_seed;
protected uint m_seed;
class Context
{
@@ -562,7 +567,7 @@ namespace GameRes.Formats.KiriKiri
return EmitUInt32 (GetRandom());
}
public uint GetRandom ()
public virtual uint GetRandom ()
{
uint seed = m_seed;
m_seed = 1103515245 * seed + 12345;
@@ -570,6 +575,27 @@ namespace GameRes.Formats.KiriKiri
}
}
internal class CxProgramNana : CxProgram
{
protected uint m_random_seed;
public CxProgramNana (uint seed, uint random_seed, uint[] control_block) : base (seed, control_block)
{
m_random_seed = random_seed;
}
public override uint GetRandom ()
{
uint s = m_seed ^ (m_seed << 17);
s ^= (s << 18) | (s >> 15);
m_seed = ~s;
uint r = m_random_seed ^ (m_random_seed << 13);
r ^= r >> 17;
m_random_seed = r ^ (r << 5);
return m_seed ^ m_random_seed;
}
}
/* CxEncryption base branch order
OddBranchOrder
{

View File

@@ -80,15 +80,17 @@ namespace GameRes.Formats.Malie
public class LibCfiScheme : LibScheme
{
public byte[] Key { get; set; }
public uint[] RotateKey { get; set; }
public LibCfiScheme (uint align, byte[] key) : base (align)
public LibCfiScheme (uint align, byte[] key, uint[] rot_key) : base (align)
{
Key = key;
RotateKey = rot_key;
}
public override IMalieDecryptor CreateDecryptor ()
{
return new CfiDecryptor (Key);
return new CfiDecryptor (Key, RotateKey);
}
}

View File

@@ -53,10 +53,12 @@ namespace GameRes.Formats.Malie
public class CfiDecryptor : IMalieDecryptor
{
byte[] m_key;
uint[] m_rotate_key;
public CfiDecryptor (byte[] key)
public CfiDecryptor (byte[] key, uint[] rotate_key)
{
m_key = key;
m_rotate_key = rotate_key;
}
public void DecryptBlock (long block_offset, byte[] data, int index)
@@ -79,13 +81,13 @@ namespace GameRes.Formats.Malie
fixed (byte* data8 = &data[index])
{
uint* data32 = (uint*)data8;
uint k = Binary.RotR (0x39653542, m_key[offset & 0x1F] ^ 0xA5);
uint k = Binary.RotR (m_rotate_key[0], 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);
k = Binary.RotL (m_rotate_key[1], 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);
k = Binary.RotR (m_rotate_key[2], 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);
k = Binary.RotL (m_rotate_key[3], m_key[(offset + 9) & 0x1F] ^ 0xA5);
data32[3] = Binary.RotL (data32[3] ^ k, m_key[(offset - 11) & 0x1F] ^ 0xA5);
}
}

View File

@@ -82,7 +82,7 @@ namespace GameRes.Formats.Pajamas
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
var meta = (EpaMetaData)info as EpaMetaData;
var meta = (EpaMetaData)info;
file.Position = 2 == meta.Mode ? 0x18 : 0x10;
var reader = new Reader (file.AsStream, meta);
reader.Unpack();

View File

@@ -2,7 +2,7 @@
//! \date Fri Sep 30 10:37:28 2016
//! \brief Primel the Adventure System 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
@@ -35,8 +35,19 @@ namespace GameRes.Formats.Primel
{
internal class PcfEntry : PackedEntry
{
public uint Flags;
public IEnumerable<byte> Key;
public uint Flags;
public byte[] Key;
}
internal class PcfArchive : ArcFile
{
public readonly PrimelScheme Scheme;
public PcfArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, PrimelScheme scheme)
: base (arc, impl, dir)
{
Scheme = scheme;
}
}
[Export(typeof(ArchiveFormat))]
@@ -55,49 +66,23 @@ namespace GameRes.Formats.Primel
int count = file.View.ReadInt32 (8);
if (!IsSaneCount (count))
return null;
long data_size = file.View.ReadInt64 (0x10);
long index_offset = file.View.ReadInt64 (0x28);
if (data_size >= file.MaxOffset || index_offset >= file.MaxOffset)
var reader = new PcfIndexReader (file, count);
var dir = reader.Read();
if (null == dir)
return null;
uint index_size = file.View.ReadUInt32 (0x30);
uint flags = file.View.ReadUInt32 (0x38);
var key = file.View.ReadBytes (0x58, 8);
long base_offset = file.MaxOffset - data_size;
using (var stream = file.CreateStream (base_offset + index_offset, index_size))
using (var index = ReadFile (stream, key, flags))
{
var buffer = new byte[0x80];
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
if (buffer.Length != index.Read (buffer, 0, buffer.Length))
break;
var name = Binary.GetCString (buffer, 0, 0x50);
var entry = FormatCatalog.Instance.Create<PcfEntry> (name);
entry.Offset = LittleEndian.ToInt64 (buffer, 0x50) + base_offset;
entry.UnpackedSize = LittleEndian.ToUInt32 (buffer, 0x58);
entry.Size = LittleEndian.ToUInt32 (buffer, 0x60);
if (!entry.CheckPlacement (file.MaxOffset))
return null;
entry.Flags = LittleEndian.ToUInt32 (buffer, 0x68);
entry.Key = new ArraySegment<byte> (buffer, 0x78, 8).ToArray();
entry.IsPacked = entry.UnpackedSize != entry.Size;
dir.Add (entry);
}
return new ArcFile (file, this, dir);
}
return new PcfArchive (file, this, dir, reader.Scheme);
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
var parc = arc as PcfArchive;
var pent = entry as PcfEntry;
if (null == pent)
if (null == pent || null == parc)
return base.OpenEntry (arc, entry);
Stream input = arc.File.CreateStream (entry.Offset, entry.Size);
try
{
input = ReadFile (input, pent.Key, pent.Flags);
input = parc.Scheme.TransformStream (input, pent.Key, pent.Flags);
if (pent.IsPacked)
input = new LimitStream (input, pent.UnpackedSize);
return input;
@@ -108,8 +93,85 @@ namespace GameRes.Formats.Primel
throw;
}
}
}
Stream ReadFile (Stream input, IEnumerable<byte> key, uint flags)
internal sealed class PcfIndexReader
{
ArcView m_file;
int m_count;
long m_base_offset;
List<Entry> m_dir;
public PrimelScheme Scheme { get; set; }
public PcfIndexReader (ArcView file, int count)
{
m_file = file;
m_count = count;
m_dir = new List<Entry> (m_count);
}
static readonly PrimelScheme[] KnownSchemes = {
new PrimelScheme(), new PrimelSchemeV2()
};
public List<Entry> Read ()
{
long data_size = m_file.View.ReadInt64 (0x10);
long index_offset = m_file.View.ReadInt64 (0x28);
if (data_size >= m_file.MaxOffset || index_offset >= m_file.MaxOffset)
return null;
uint index_size = m_file.View.ReadUInt32 (0x30);
uint flags = m_file.View.ReadUInt32 (0x38);
var key = m_file.View.ReadBytes (0x58, 8);
m_base_offset = m_file.MaxOffset - data_size;
foreach (var scheme in KnownSchemes)
{
m_dir.Clear();
try
{
using (var stream = m_file.CreateStream (m_base_offset + index_offset, index_size))
using (var index = scheme.TransformStream (stream, key, flags))
{
if (ReadIndex (index))
{
this.Scheme = scheme;
return m_dir;
}
}
}
catch { /* invalid scheme, retry */ }
}
return null;
}
byte[] m_buffer = new byte[0x80];
bool ReadIndex (Stream index)
{
for (int i = 0; i < m_count; ++i)
{
if (m_buffer.Length != index.Read (m_buffer, 0, m_buffer.Length))
break;
var name = Binary.GetCString (m_buffer, 0, 0x50);
var entry = FormatCatalog.Instance.Create<PcfEntry> (name);
entry.Offset = LittleEndian.ToInt64 (m_buffer, 0x50) + m_base_offset;
entry.UnpackedSize = LittleEndian.ToUInt32 (m_buffer, 0x58);
entry.Size = LittleEndian.ToUInt32 (m_buffer, 0x60);
if (!entry.CheckPlacement (m_file.MaxOffset))
return false;
entry.Flags = LittleEndian.ToUInt32 (m_buffer, 0x68);
entry.Key = new ArraySegment<byte> (m_buffer, 0x78, 8).ToArray();
entry.IsPacked = entry.UnpackedSize != entry.Size;
m_dir.Add (entry);
}
return m_dir.Count > 0;
}
}
internal class PrimelScheme
{
public Stream TransformStream (Stream input, byte[] key, uint flags)
{
var key1 = GenerateKey (key);
var iv = GenerateKey (key1);
@@ -168,10 +230,9 @@ namespace GameRes.Formats.Primel
}
}
byte[] GenerateKey (IEnumerable<byte> seed)
byte[] GenerateKey (byte[] seed)
{
var sha = new Primel.SHA256();
var hash = sha.ComputeHash (seed.ToArray());
var hash = ComputeHash (seed);
var key = new byte[0x10];
for (int i = 0; i < hash.Length; ++i)
{
@@ -179,5 +240,20 @@ namespace GameRes.Formats.Primel
}
return key;
}
protected virtual byte[] ComputeHash (byte[] seed)
{
var sha = new Primel.SHA256();
return sha.ComputeHash (seed);
}
}
internal class PrimelSchemeV2 : PrimelScheme
{
protected override byte[] ComputeHash (byte[] seed)
{
using (var sha = System.Security.Cryptography.SHA256.Create())
return sha.ComputeHash (seed);
}
}
}

View File

@@ -2,7 +2,7 @@
//! \date Mon Oct 03 04:16:11 2016
//! \brief Primel the Adventure System 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
@@ -97,6 +97,14 @@ namespace GameRes.Formats.Primel
}
public void Unpack ()
{
if (0x800 == (m_info.Flags & 0xFF00))
UnpackV2();
else
UnpackV1();
}
void UnpackV1 ()
{
m_input.Input.Position = 0x30;
int pixel_size = m_info.BPP / 8;
@@ -148,6 +156,63 @@ namespace GameRes.Formats.Primel
}
}
void UnpackV2 ()
{
m_input.Input.Position = 0x30;
int pixel_size = m_info.BPP / 8;
int blocks_w = (int)(m_info.Width + 7) / 8;
int blocks_h = (int)(m_info.Height + 7) / 8;
short[,] block = new short[pixel_size, 64];
for (int by = 0; by < blocks_h; ++by)
{
int dst_line = by * 8 * m_stride;
for (int bx = 0; bx < blocks_w; ++bx)
{
int dst_block = dst_line + bx * 8 * pixel_size;
for (int i = 0; i < pixel_size; ++i)
{
for (int j = 0; j < 64; ++j)
block[i,j] = 0;
RestoreBlockV2 (block, i);
}
for (int y = 0; y < 8; ++y)
{
if (by + 1 == blocks_h && (by * 8 + y) >= m_info.Height)
break;
int src = y * 8;
int dst = dst_block + y * m_stride;
for (int x = 0; x < 8; ++x)
{
if (bx + 1 == blocks_w && (bx * 8 + x) >= m_info.Width)
break;
if (4 == pixel_size)
{
m_output[dst + x * 4 + 2] = (byte)block[0, src+x];
m_output[dst + x * 4 + 1] = (byte)block[1, src+x];
m_output[dst + x * 4] = (byte)block[2, src+x];
m_output[dst + x * 4 + 3] = (byte)block[3, src+x];
}
else if (3 == pixel_size)
{
m_output[dst + x * 3 + 2] = (byte)block[0, src+x];
m_output[dst + x * 3 + 1] = (byte)block[1, src+x];
m_output[dst + x * 3] = (byte)block[2, src+x];
}
else
{
var val = block[0, src+x];
m_output[dst + x] = (byte)val;
}
}
}
}
}
}
void RestoreBlock (short[,] block, int n)
{
int row = 8;
@@ -168,6 +233,31 @@ namespace GameRes.Formats.Primel
}
}
void RestoreBlockV2 (short[,] block, int plane)
{
int skip;
for (int i = 0; i < 64; ++i)
{
int n = GetIntV2 (out skip);
if (n != 0)
block[plane, ZigzagOrder[i]] = (short)n;
else if (0 == skip)
break;
else
i += skip - 1;
}
for (int row = 0; row < 64; row += 8)
for (int x = 1; x < 8; ++x)
{
block[plane, row+x] += block[plane, row+x-1];
}
for (int row = 8; row < 64; row += 8)
for (int x = 0; x < 8; ++x)
{
block[plane, row+x] += block[plane, row-8+x];
}
}
int GetInt ()
{
int count = m_input.GetBits (4);
@@ -194,6 +284,40 @@ namespace GameRes.Formats.Primel
}
}
int GetIntV2 (out int repeat)
{
int count = m_input.GetBits (4);
repeat = 0;
switch (count)
{
case 0:
repeat = 1;
while (repeat < 16 && 1 == m_input.GetNextBit())
++repeat;
if (16 == repeat)
repeat = 0;
return 0;
case 1: return 1;
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
return m_input.GetBits (count - 1) + (1 << (count - 1));
case 8: return -1;
case 9: return -2;
default:
return m_input.GetBits (count - 9) - (2 << (count - 9));
case -1: throw new EndOfStreamException();
}
}
bool _disposed = false;
public void Dispose ()
{

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.34.1396")]
[assembly: AssemblyFileVersion ("1.2.34.1396")]
[assembly: AssemblyVersion ("1.2.35.1414")]
[assembly: AssemblyFileVersion ("1.2.35.1414")]

View File

@@ -38,10 +38,12 @@ namespace GameRes.Formats.Qlie
{
internal class QlieEntry : PackedEntry
{
public bool IsEncrypted;
public int EncryptionMethod;
public uint Hash;
public byte[] RawName;
public bool IsEncrypted { get { return EncryptionMethod != 0; } }
/// <summary>
/// Data from a separate key file "key.fkey" that comes with installed game.
/// null if not used.
@@ -153,7 +155,7 @@ namespace GameRes.Formats.Qlie
return null;
entry.UnpackedSize = index.ReadUInt32(); // [+0C]
entry.IsPacked = 0 != index.ReadInt32(); // [+10]
entry.IsEncrypted = 0 != index.ReadInt32(); // [+14]
entry.EncryptionMethod = index.ReadInt32(); // [+14]
entry.Hash = index.ReadUInt32(); // [+18]
entry.KeyFile = key_file;
if (3 == pack_version.Major && use_pack_keyfile && entry.Name.Contains ("pack_keyfile"))
@@ -330,6 +332,16 @@ namespace GameRes.Formats.Qlie
return File.ReadAllBytes (name);
}
}
var pattern = VFS.CombinePath (dir_name, @"..\*.exe");
foreach (var exe_file in VFS.GetFiles (pattern))
{
using (var exe = new ExeFile.ResourceAccessor (exe_file.Name))
{
var reskey = exe.GetResource ("RESKEY", "#10");
if (reskey != null)
return reskey;
}
}
return null;
}

View File

@@ -25,6 +25,7 @@
using System;
using System.Text;
using GameRes.Utility;
namespace GameRes.Formats.Qlie
{
@@ -285,13 +286,28 @@ namespace GameRes.Formats.Qlie
public override void DecryptEntry (byte[] data, int offset, int length, QlieEntry entry)
{
if (0 == entry.EncryptionMethod)
return;
if (offset < 0)
throw new ArgumentOutOfRangeException ("offset");
if (length > data.Length || offset > data.Length - length)
throw new ArgumentOutOfRangeException ("length");
if (length < 8)
return;
unsafe
{
fixed (byte* raw_data = &data[offset])
{
if (1 == entry.EncryptionMethod)
DecryptV1 (raw_data, length, entry);
else
DecryptV2 (raw_data, length, entry);
}
}
}
unsafe void DecryptV1 (byte* data, int length, QlieEntry entry)
{
var file_name = entry.Name;
uint hash = 0x85F532;
uint seed = 0x33F641;
@@ -306,34 +322,66 @@ namespace GameRes.Formats.Qlie
+ hash + (hash ^ (uint)length ^ 0x8F32DCu));
seed = 9 * (seed & 0xFFFFFF);
var table = GenerateTable (0x20, seed); // originally 0x2C, 12 dwords not used
unsafe
ulong* data64 = (ulong*)data;
int qword_length = length / 8;
uint k = 2 * (table[0xD] & 0xF);
ulong hash64 = table[6] | (ulong)table[7] << 32;
for (int i = 0; i < qword_length; ++i)
{
fixed (byte* raw_data = &data[offset])
{
ulong* data64 = (ulong*)raw_data;
int qword_length = length / 8;
uint k = 2 * (table[0xD] & 0xF);
ulong hash64 = table[6] | (ulong)table[7] << 32;
for (int i = 0; i < qword_length; ++i)
{
ulong t = table[k] | (ulong)table[k+1] << 32;
hash64 = MMX.PAddD (hash64 ^ t, t);
ulong t = table[k] | (ulong)table[k+1] << 32;
hash64 = MMX.PAddD (hash64 ^ t, t);
ulong d = data64[i] ^ hash64;
data64[i] = d;
ulong d = data64[i] ^ hash64;
data64[i] = d;
hash64 = MMX.PAddB (hash64, d) ^ d;
hash64 = MMX.PAddW (MMX.PSllD (hash64, 1), d);
hash64 = MMX.PAddB (hash64, d) ^ d;
hash64 = MMX.PAddW (MMX.PSllD (hash64, 1), d);
k = (k + 2) & 0x1F;
}
}
k = (k + 2) & 0x1F;
}
}
static uint[] GenerateTable (int length, uint seed)
unsafe void DecryptV2 (byte* data, int length, QlieEntry entry)
{
var file_name = entry.Name;
uint hash = 0x86F7E2;
uint seed = 0x4437F1;
for (int i = 0; i < file_name.Length; i++)
{
hash += (uint)(file_name[i] << (i & 7));
seed ^= hash;
}
seed += ArcKey ^ (13 * ((uint)length & 0xFFFFFF) + (uint)length
+ hash + (hash ^ (uint)length ^ 0x56E213u));
seed = 13 * (seed & 0xFFFFFF);
var table = GenerateTable (0x20, seed, 0x8A77F473u); // originally 0x40
var key_data = GenerateKeyData (entry.KeyFile);
ulong* data64 = (ulong*)data;
int qword_length = length / 8;
int k = (8 * ((int)table[8] & 0xD)) & 0x7F;
ulong hash64 = table[6] | (ulong)table[7] << 32;
for (int i = 0; i < qword_length; ++i)
{
int t_index = 2 * (k & 0xF);
ulong t = table[t_index] | (ulong)table[t_index + 1] << 32;
t ^= LittleEndian.ToUInt64 (key_data, 8 * k);
hash64 = MMX.PAddD (hash64 ^ t, t);
ulong d = data64[i] ^ hash64;
data64[i] = d;
hash64 = MMX.PAddB (hash64, d) ^ d;
hash64 = MMX.PAddW (MMX.PSllD (hash64, 1), d);
k = (k + 1) & 0x7F;
}
}
static uint[] GenerateTable (int length, uint seed, uint key = 0x8DF21431u)
{
const uint key = 0x8DF21431u;
var table = new uint[length];
for (int i = 0; i < length; ++i)
{
@@ -343,5 +391,30 @@ namespace GameRes.Formats.Qlie
}
return table;
}
byte[] GenerateKeyData (byte[] key_file)
{
var key_data = new byte[0x400];
for (int i = 0; i < 0x100; ++i)
{
int hash;
if (0 != (i % 3))
hash = (i + 7) * -(i + 3);
else
hash = (i + 7) * (i + 3);
LittleEndian.Pack (hash, key_data, i * 4);
}
if (key_file != null && key_file.Length >= 128)
{
int k = key_file[49] % 73 + 128;
int l = key_file[79] % 7 + 7;
for (int i = 0; i < key_data.Length; ++i)
{
k = (k + l) % key_file.Length;
key_data[i] ^= key_file[k];
}
}
return key_data;
}
}
}

View File

Binary file not shown.

80
ArcFormats/Slg/ArcSPD.cs Normal file
View File

@@ -0,0 +1,80 @@
//! \file ArcSPD.cs
//! \date 2017 Aug 13
//! \brief SLG system audio 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.IO;
namespace GameRes.Formats.Slg
{
[Export(typeof(ArchiveFormat))]
public class SpdOpener : ArchiveFormat
{
public override string Tag { get { return "SPD/SLG"; } }
public override string Description { get { return "SLG system audio archive"; } }
public override uint Signature { get { return 0x504653; } } // 'SFP'
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
public override ArcFile TryOpen (ArcView file)
{
if (file.Name.HasExtension (".SPL"))
return null;
var index_name = Path.ChangeExtension (file.Name, ".SPL");
if (!VFS.FileExists (index_name))
return null;
using (var idx = VFS.OpenView (index_name))
{
if (!idx.View.AsciiEqual (0, "SFP\0"))
return null;
uint align = idx.View.ReadUInt32 (0xC);
uint index_offset = 0x20;
uint names_offset = idx.View.ReadUInt32 (index_offset);
if (names_offset > idx.MaxOffset || names_offset <= index_offset)
return null;
int count = (int)(names_offset - index_offset) / 0x10;
if (!IsSaneCount (count))
return null;
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
uint name_offset = idx.View.ReadUInt32 (index_offset);
var name = idx.View.ReadString (name_offset, (uint)(idx.MaxOffset - name_offset));
if (string.IsNullOrWhiteSpace (name))
return null;
var entry = FormatCatalog.Instance.Create<Entry> (name);
entry.Size = idx.View.ReadUInt32 (index_offset+4);
entry.Offset = (long)idx.View.ReadUInt32 (index_offset+8) * align;
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
index_offset += 0x10;
}
return new ArcFile (file, this, dir);
}
}
}
}

View File

@@ -320,6 +320,8 @@ namespace GameRes.Formats.TopCat
name = Path.Combine (dir_name, name);
name = Path.ChangeExtension (name, section.Extension);
var entry = FormatCatalog.Instance.Create<TcdEntry> (name);
if (name.HasExtension (".SPD"))
entry.Type = "image";
entry.Offset = offsets[index];
entry.Size = offsets[index+1] - offsets[index];
entry.Index = index;

View File

@@ -23,6 +23,7 @@
// IN THE SOFTWARE.
//
using System;
using System.ComponentModel.Composition;
using System.IO;
using System.Windows.Media;
@@ -32,8 +33,15 @@ namespace GameRes.Formats.Valkyria
{
internal class Mg2MetaData : ImageMetaData
{
public int ImageLength;
public int AlphaLength;
public int ImageLength;
public int AlphaLength;
public IMg2Scheme Scheme;
}
internal interface IMg2Scheme
{
Mg2EncryptedStream CreateStream (Stream main, int offset, int length);
ImageData CreateImage (BitmapSource bitmap, ImageMetaData info);
}
[Export(typeof(ImageFormat))]
@@ -43,44 +51,54 @@ namespace GameRes.Formats.Valkyria
public override string Description { get { return "Valkyria image format"; } }
public override uint Signature { get { return 0x4F43494D; } } // 'MICO'
static readonly IMg2Scheme[] KnownSchemes = { new Mg2SchemeV1(), new Mg2SchemeV2() };
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
var header = file.ReadHeader (0x10);
if (!header.AsciiEqual (4, "CG01"))
return null;
int length = header.ToInt32 (8);
using (var input = new Mg2EncryptedStream (file.AsStream, 0x10, length))
using (var png = new BinaryStream (input, file.Name))
foreach (var scheme in KnownSchemes)
{
var info = Png.ReadMetaData (png);
if (null == info)
return null;
return new Mg2MetaData
using (var input = scheme.CreateStream (file.AsStream, 0x10, length))
using (var png = new BinaryStream (input, file.Name))
{
Width = info.Width,
Height = info.Height,
OffsetX = info.OffsetX,
OffsetY = info.OffsetY,
BPP = info.BPP,
ImageLength = length,
AlphaLength = header.ToInt32 (12)
};
var info = Png.ReadMetaData (png);
if (null == info)
continue;
return new Mg2MetaData
{
Width = info.Width,
Height = info.Height,
OffsetX = info.OffsetX,
OffsetY = info.OffsetY,
BPP = info.BPP,
ImageLength = length,
AlphaLength = header.ToInt32 (12),
Scheme = scheme,
};
}
}
return null;
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
var meta = (Mg2MetaData)info;
var frame = ReadBitmapSource (file.AsStream, meta);
return meta.Scheme.CreateImage (frame, meta);
}
BitmapSource ReadBitmapSource (Stream file, Mg2MetaData meta)
{
BitmapSource frame;
using (var input = new Mg2EncryptedStream (file.AsStream, 0x10, meta.ImageLength))
using (var input = meta.Scheme.CreateStream (file, 0x10, meta.ImageLength))
{
var decoder = new PngBitmapDecoder (input, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
frame = decoder.Frames[0];
if (0 == meta.AlphaLength)
{
frame.Freeze();
return new ImageData (frame, info);
}
return frame;
}
if (frame.Format.BitsPerPixel != 32)
frame = new FormatConvertedBitmap (frame, PixelFormats.Bgr32, null, 0);
@@ -88,12 +106,14 @@ namespace GameRes.Formats.Valkyria
var pixels = new byte[stride * (int)meta.Height];
frame.CopyPixels (pixels, stride, 0);
using (var input = new Mg2EncryptedStream (file.AsStream, 0x10+meta.ImageLength, meta.AlphaLength))
using (var input = meta.Scheme.CreateStream (file, 0x10+meta.ImageLength, meta.AlphaLength))
{
var decoder = new PngBitmapDecoder (input, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
BitmapSource alpha_frame = decoder.Frames[0];
if (alpha_frame.PixelWidth != frame.PixelWidth || alpha_frame.PixelHeight != frame.PixelHeight)
return ImageData.Create (info, PixelFormats.Bgr32, null, pixels, stride);
return BitmapSource.Create ((int)meta.Width, (int)meta.Height,
ImageData.DefaultDpiX, ImageData.DefaultDpiY,
PixelFormats.Bgr32, null, pixels, stride);
alpha_frame = new FormatConvertedBitmap (alpha_frame, PixelFormats.Gray8, null, 0);
var alpha = new byte[alpha_frame.PixelWidth * alpha_frame.PixelHeight];
@@ -104,7 +124,9 @@ namespace GameRes.Formats.Valkyria
{
pixels[dst] = alpha[src++];
}
return ImageData.Create (info, PixelFormats.Bgra32, null, pixels, stride);
return BitmapSource.Create ((int)meta.Width, (int)meta.Height,
ImageData.DefaultDpiX, ImageData.DefaultDpiY,
PixelFormats.Bgra32, null, pixels, stride);
}
}
@@ -117,11 +139,23 @@ namespace GameRes.Formats.Valkyria
internal class Mg2EncryptedStream : StreamRegion
{
readonly int m_threshold;
readonly byte m_key;
public Mg2EncryptedStream (Stream main, int offset, int length)
protected Mg2EncryptedStream (Stream main, int offset, int length, int threshold, byte key)
: base (main, offset, length, true)
{
m_threshold = length / 5;
m_threshold = threshold;
m_key = key;
}
public static Mg2EncryptedStream CreateV1 (Stream main, int offset, int length)
{
return new Mg2EncryptedStream (main, offset, length, length / 5, 0);
}
public static Mg2EncryptedStream CreateV2 (Stream main, int offset, int length)
{
return new Mg2EncryptedStream (main, offset, length, Math.Min (25, length), (byte)length);
}
public override int Read (byte[] buffer, int offset, int count)
@@ -129,7 +163,7 @@ namespace GameRes.Formats.Valkyria
int pos = (int)Position;
int read = base.Read (buffer, offset, count);
for (int i = 0; i < read && pos < m_threshold; ++i)
buffer[offset+i] ^= (byte)pos++;
buffer[offset+i] ^= (byte)(m_key + pos++);
return read;
}
@@ -138,8 +172,37 @@ namespace GameRes.Formats.Valkyria
long pos = Position;
int b = base.ReadByte();
if (b != -1 && pos < m_threshold)
b ^= (byte)pos;
b ^= (byte)(m_key + pos);
return b;
}
}
internal class Mg2SchemeV1 : IMg2Scheme
{
public Mg2EncryptedStream CreateStream (Stream main, int offset, int length)
{
return Mg2EncryptedStream.CreateV1 (main, offset, length);
}
public ImageData CreateImage (BitmapSource frame, ImageMetaData info)
{
frame.Freeze();
return new ImageData (frame, info);
}
}
internal class Mg2SchemeV2 : IMg2Scheme
{
public Mg2EncryptedStream CreateStream (Stream main, int offset, int length)
{
return Mg2EncryptedStream.CreateV2 (main, offset, length);
}
public ImageData CreateImage (BitmapSource frame, ImageMetaData info)
{
frame = new TransformedBitmap (frame, new ScaleTransform { ScaleY = -1 });
frame.Freeze();
return new ImageData (frame, info);
}
}
}

View File

@@ -123,7 +123,7 @@ namespace GameRes.Formats.YuRis
return null;
var parser = new Parser (file, version, count, dir_size);
var scheme = QueryEncryptionScheme (version);
var scheme = QueryEncryptionScheme (file.Name, version);
var dir = parser.ScanDir (scheme);
if (null == dir || 0 == dir.Count)
return null;
@@ -175,10 +175,13 @@ namespace GameRes.Formats.YuRis
return new GUI.CreateYPFWidget();
}
YpfScheme QueryEncryptionScheme (uint version)
YpfScheme QueryEncryptionScheme (string arc_name, uint version)
{
var options = Query<YpfOptions> (arcStrings.YPFNotice);
var title = FormatCatalog.Instance.LookupGame (arc_name);
YpfScheme scheme;
if (!string.IsNullOrEmpty (title) && KnownSchemes.TryGetValue (title, out scheme))
return scheme;
var options = Query<YpfOptions> (arcStrings.YPFNotice);
if (!KnownSchemes.TryGetValue (options.Scheme, out scheme) || null == scheme)
scheme = new YpfScheme {
SwapTable = GuessSwapTable (version),
@@ -354,7 +357,7 @@ namespace GameRes.Formats.YuRis
uint* header = (uint*)raw;
uint version = header[1];
int first_item, last_item;
if (version >= 0x1CE || 0x12C == version)
if (version >= 0x1CE || 0x12C == version || 0x19A == version)
{
first_item = 3;
last_item = 7;

View File

@@ -106,6 +106,7 @@ namespace GARbro.GUI
var oem = CultureInfo.CurrentCulture.TextInfo.OEMCodePage;
list.Add (Encoding.GetEncoding (oem));
list.Add (Encoding.GetEncoding (932));
list.Add (Encoding.GetEncoding (936));
list.Add (Encoding.UTF8);
list.Add (Encoding.Unicode);
list.Add (Encoding.BigEndianUnicode);

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.30.1888")]
[assembly: AssemblyFileVersion ("1.4.30.1888")]
[assembly: AssemblyVersion ("1.4.31.1908")]
[assembly: AssemblyFileVersion ("1.4.31.1908")]

View File

@@ -129,6 +129,8 @@ namespace GameRes
meta.Width = Binary.BigEndian (file.ReadUInt32());
meta.Height = Binary.BigEndian (file.ReadUInt32());
int bpp = file.ReadByte();
if (bpp != 1 && bpp != 2 && bpp != 4 && bpp != 8 && bpp != 16)
return null;
int color_type = file.ReadByte();
switch (color_type)
{
@@ -136,7 +138,8 @@ namespace GameRes
case 3: meta.BPP = 24; break;
case 4: meta.BPP = bpp*2; break;
case 6: meta.BPP = bpp*4; break;
default: meta.BPP = bpp; break;
case 0: meta.BPP = bpp; break;
default: return null;
}
SkipBytes (file, 7);

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.31.255")]
[assembly: AssemblyFileVersion ("1.4.31.255")]
[assembly: AssemblyVersion ("1.4.32.257")]
[assembly: AssemblyFileVersion ("1.4.32.257")]

View File

@@ -98,6 +98,7 @@ Grisaia no Kajitsu<br/>
Happiness! Re:Lucks<br/>
Kamikaze ☆ Explorer!<br/>
Koko kara Natsu no Innocence!<br/>
Labyrinth of Grisaia<br/>
Makai Tenshi Djibril -Episode 4-<br/>
Sakigake ⇒ Generation!<br/>
Sengoku Tenshi Djibril<br/>
@@ -303,6 +304,7 @@ Grisaia: Phantom Trigger Vol.2<br/>
Hachukano<br/>
Hanafubuki ~Sennen no Koi o Shimashita~<br/>
Haruiro ☆ Communication ♪<br/>
Haruoto Alice*Gram<br/>
Heliotrope -Sore wa Shi ni Itaru Kami no Ai-<br/>
Hime to Majin to Koi Suru Tamashii<br/>
Imouto Style<br/>
@@ -371,7 +373,9 @@ Zettai Karen! Ojou-sama<br/>
<tr class="last"><td>*.tlg</td><td><tt>TLG0.0</tt><br/><tt>TLG5.0</tt><br/><tt>TLG6.0</tt></td><td></td></tr>
<tr class="odd"><td>*.ypf</td><td><tt>YPF</tt></td><td><span class="supported"></span></td><td rowspan="2">YU-RIS</td><td rowspan="2">
77 (Sevens) ~And, Two Stars Meet Again~<br/>
Aikagi<br/>
Eroge! ~H mo Game mo Kaihatsu Zanmai~<br/>
Haramase Taiken Gakushuu ~Shizen no Naka de Seikyouiku!?~<br/>
Koi Mekuri Clover<br/>
Mamono Musume-tachi to no Rakuen ~Slime &amp; Scylla~<br/>
Mashou no Nie 3 ~Hakudaku no Umi ni Shizumu Injoku no Reiki~<br/>
@@ -531,6 +535,7 @@ Jukubo Gui ~Moto Guradoru no Okaa-san~ <span class="footnote">ShiinaRio v2.37</s
Kateinai Choukyou <span class="footnote">ShiinaRio v2.31</span><br/>
Kichiku Nakadashi Suieibu<span class="footnote">ShiinaRio v2.41</span><br/>
Last Waltz ~Hakudaku Mamire no Natsu Gasshuku~ <span class="footnote">ShiinaRio v2.47</span><br/>
Libra of the Vampire Princess <span class="footnote">ShiinaRio v2.50</span><br/>
Mahou Shoujo no Taisetsu na Koto <span class="footnote">ShiinaRio v2.47</span><br/>
Maki Fes! <span class="footnote">ShiinaRio v2.50</span><br/>
Mikoko <span class="footnote">ShiinaRio v2.46</span><br/>
@@ -549,6 +554,7 @@ Ren'ai Saimin ~Tsun na Kanojo ga dereru Saimin~ <span class="footnote">ShiinaRio
Rin×Sen <span class="footnote">ShiinaRio v2.47</span><br/>
Ryoumaden ~Houkago no Rakuen~ <span class="footnote">ShiinaRio v2.47</span><br/>
Sabae no Ou <span class="footnote">ShiinaRio v2.36</span><br/>
Saimin Seikatsu ~Kousoku Dakara Shikatanai!?~ <span class="footnote">ShiinaRio v2.47</span><br/>
Shinigami no Testament <span class="footnote">ShiinaRio v2.49</span><br/>
Shinseki no Oba-san ~Hanare no Netsujo, Honke no Gosai~ <span class="footnote">ShiinaRio v2.36</span><br/>
Shojo Mama<span class="footnote">ShiinaRio v2.49</span><br/>
@@ -630,6 +636,7 @@ WW&amp;F ~Taishou Teito Denkitan~<br/>
</td></tr>
<tr class="odd"><td>*.dat</td><td><tt>GAMEDAT PACK</tt><br/><tt>GAMEDAT PAC2</tt></td><td></td><td rowspan="2"> bootUP!<br/>Pajamas Soft<br/>Aries</td><td rowspan="2">
Aneimo 2 ~Second Stage~<br/>
Lilycle Rainbow Stage!!!<br/>
Momichupa Teacher!<br/>
Natsu no Owari no Nirvana<br/>
Patissier na Nyanko<br/>
@@ -741,6 +748,7 @@ Zetsuboushi<br/>
<tr><td>*.pack</td><td><tt>FilePackVer1.0</tt><br/><tt>FilePackVer2.0</tt><br/><tt>FilePackVer3.0</tt><br/><tt>FilePackVer3.1</tt></td><td></td><td rowspan="3">QLIE</td><td rowspan="3">
Amanatsu Adolesence Trial 2<br/>
Bishoujo Mangekyou -Kami ga Tsukuritamouta Shoujo-tachi-<br/>
Bishoujo Mangekyou -Tsumi to Batsu no Shoujo-<br/>
Harem Hospital<br/>
Hidamari Basket<br/>
Kikan Bakumatsu Ibun Last Cavalier<br/>
@@ -782,6 +790,7 @@ Deep Love Diary<br/>
Dies irae<br/>
Dies irae ~Amantes amentes~<br/>
Dokidoki Sister Paradise 2<br/>
Fuyu Uso -Snow World End-<br/>
Kajiri Kamui Kagura<br/>
Paradise Lost<br/>
Sacrifice ~Seifuku Gari~<br/>
@@ -1087,6 +1096,7 @@ AstralAir no Shiroki Towa<br/>
</td></tr>
<tr class="last"><td>*.hzc</td><td><tt>hzc1</tt></td><td></td></tr>
<tr class="odd"><td>*.bin</td><td><tt>ESC-ARC1</tt><br/><tt>ESC-ARC2</tt></td><td></td><td>Escu:de</td><td>
Hyakki Ryouran no Yakata<br/>
Otome Renshin Prister<br/>
Suisei Tenshi Primaveil Zwei<br/>
Verdia Gensoukyoku<br/>
@@ -1174,10 +1184,12 @@ Tokyo Necro<br/>
Thanatos no Koi ~In Ane Otouto Soukan~<br/>
</td></tr>
<tr class="odd last"><td>*.gps</td><td><tt>GPS</tt></td><td></td></tr>
<tr><td>*.szs</td><td><tt>SZS100__</tt></td><td></td><td rowspan="4">SLG system</td><td rowspan="4">
<tr><td>*.szs</td><td><tt>SZS100__</tt></td><td></td><td rowspan="5">SLG system</td><td rowspan="5">
Sangoku Hime 2<br/>
Sengoku Hime<br/>
Shihen 69 ~Shinen no Messiah~<br/>
</td></tr>
<tr><td>*.spd+*.spl</td><td><tt>SFP</tt></td><td></td></tr>
<tr><td>*.tig</td><td><tt>\x7C\xF3\xC2\x8B</tt></td><td></td></tr>
<tr><td>*.alb</td><td><tt>ALB1.21</tt></td><td></td></tr>
<tr class="last"><td>*.voi</td><td>-</td><td></td></tr>
@@ -1189,6 +1201,7 @@ Angenehm Platz -Kleiner Garten Sie Erstellen-<br/>
<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/>
D-spray 2 Biyaku de Motemote Kachou Dairi Yukemuri Ryojou Hen<br/>
Hitomi no Rakuin ~Inbaku no Mesu Dorei~<br/>
Jokujima<br/>
Saiin Haramase Keyword<br/>
@@ -1442,8 +1455,10 @@ Eden's Ritter - Inetsu no Seima Kishi Lucifer Hen<br/>
In Vitro Shoujo -Aragaishi Junshin Naru Shoujo-<br/>
Kyonyuu Fantasy - Digital Novel<br/>
Shirudaku Settai Okawari Ippaime<br/>
Zen'aku<br/>
</td></tr>
<tr class="odd"><td>*.pcf</td><td><tt>PackCode</tt></td><td></td><td rowspan="2">Symphony</td><td rowspan="2">
Fortuna Rhapsody<br/>
Tiara<br/>
</td></tr>
<tr class="odd last"><td>*.gbc</td><td><tt>GBCF</tt></td><td></td></tr>
@@ -1552,6 +1567,7 @@ Sutadoru!<br/>
</td></tr>
<tr class="last"><td>*.pcd</td><td>-</td><td></td></tr>
<tr class="odd"><td>*.tac<br/>*.stx</td><td><tt>TArc1.10</tt><br/><tt>TArc1.00</tt></td><td></td><td>TanukiSoft</td><td>
Onii-chan Daisuki!<br/>
Shoujo Kyouiku<br/>
Shoujo Ramune<br/>
Shoukoujo<br/>
@@ -1589,6 +1605,7 @@ Kannagi<br/>
</td></tr>
<tr class="odd last"><td>*.pak</td><td><tt>CCf"</tt></td><td></td></tr>
<tr><td>*.dat</td><td>-</td><td></td><td rowspan="3">Valkyria</td><td rowspan="3">
Gedou Mahou Shoujo Rinne ~Akuin Akka~<br/>
Kangokujou ~Ingyaku no Shihai~<br/>
</td></tr>
<tr><td>*.mg2</td><td><tt>MICOCG01</tt></td><td></td></tr>

View File

@@ -1,20 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<GARbro>
<Release>
<Version>1.4.30</Version>
<Version>1.4.31</Version>
<Url>https://github.com/morkt/GARbro/releases/latest</Url>
<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>Changes:
- updated INT, QLIE, YPF, PCF, GBC, MG2 formats support
- added more KiriKiri and Malie encryption schemes
- added CP936 encoding option to text viewer
</Notes>
</Release>
<FormatsData>
<FileVersion>72</FileVersion>
<FileVersion>78</FileVersion>
<Url>https://github.com/morkt/GARbro/raw/master/ArcFormats/Resources/Formats.dat</Url>
<Requires>
<Assembly Name="ArcFormats" Version="1.2.33.1392"/>
<Assembly Name="ArcFormats" Version="1.2.35.1414"/>
<Assembly Name="GameRes" Version="1.4.26.238"/>
</Requires>
</FormatsData>