(QLIE): updated 'FilePackVer3.1' archives support (#84)

This commit is contained in:
morkt 2017-07-29 02:13:22 +04:00
parent 8724e8131c
commit f40226866c
5 changed files with 109 additions and 23 deletions

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

Binary file not shown.

View File

@ -744,6 +744,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/>

View File

@ -11,7 +11,7 @@
</Notes>
</Release>
<FormatsData>
<FileVersion>75</FileVersion>
<FileVersion>76</FileVersion>
<Url>https://github.com/morkt/GARbro/raw/master/ArcFormats/Resources/Formats.dat</Url>
<Requires>
<Assembly Name="ArcFormats" Version="1.2.33.1392"/>