From 28aed8df3757c53ed871c7768c903b6153d16aec Mon Sep 17 00:00:00 2001 From: morkt Date: Tue, 19 Jun 2018 13:05:31 +0400 Subject: [PATCH] (QLIE): better handling of version 1 archives. --- ArcFormats/Qlie/ArcQLIE.cs | 11 +++++--- ArcFormats/Qlie/Encryption.cs | 53 ++++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/ArcFormats/Qlie/ArcQLIE.cs b/ArcFormats/Qlie/ArcQLIE.cs index ef824ca3..f62b8a4d 100644 --- a/ArcFormats/Qlie/ArcQLIE.cs +++ b/ArcFormats/Qlie/ArcQLIE.cs @@ -91,12 +91,14 @@ namespace GameRes.Formats.Qlie /// static readonly string[] KeyLocations = { ".", "..", @"..\DLL", "DLL" }; - public static Dictionary KnownKeys = new Dictionary(); + static QlieScheme DefaultScheme = new QlieScheme { KnownKeys = new Dictionary() }; + + public static Dictionary KnownKeys { get { return DefaultScheme.KnownKeys; } } public override ResourceScheme Scheme { - get { return new QlieScheme { KnownKeys = KnownKeys }; } - set { KnownKeys = ((QlieScheme)value).KnownKeys; } + get { return DefaultScheme; } + set { DefaultScheme = (QlieScheme)value; } } public override ArcFile TryOpen (ArcView file) @@ -156,7 +158,8 @@ namespace GameRes.Formats.Qlie entry.UnpackedSize = index.ReadUInt32(); // [+0C] entry.IsPacked = 0 != index.ReadInt32(); // [+10] entry.EncryptionMethod = index.ReadInt32(); // [+14] - entry.Hash = index.ReadUInt32(); // [+18] + if (pack_version.Major > 1) + entry.Hash = index.ReadUInt32(); // [+18] entry.KeyFile = key_file; if (read_pack_keyfile && entry.Name.Contains ("pack_keyfile")) { diff --git a/ArcFormats/Qlie/Encryption.cs b/ArcFormats/Qlie/Encryption.cs index dadb390d..1c1557bc 100644 --- a/ArcFormats/Qlie/Encryption.cs +++ b/ArcFormats/Qlie/Encryption.cs @@ -56,7 +56,9 @@ namespace GameRes.Formats.Qlie public static IEncryption Create (ArcView file, Version version, byte[] arc_key) { - if (2 == version.Major || 1 == version.Major) + if (1 == version.Major) + return new EncryptionV1(); + else if (2 == version.Major) return new EncryptionV2(); else if (3 == version.Major && 1 == version.Minor) return new EncryptionV3_1 (file); @@ -73,6 +75,55 @@ namespace GameRes.Formats.Qlie public abstract void DecryptEntry (byte[] data, int offset, int length, QlieEntry entry); } + internal class EncryptionV1 : QlieEncryption + { + public EncryptionV1 () + { + NameKey = 0xC4; + ArcKey = 0; + } + + public override uint CalculateHash (byte[] data, int length) + { + return 0; // not implemented + } + + public override string DecryptName (byte[] name, int name_length) + { + int key = NameKey ^ 0x3E; + for (int k = 0; k < name_length; ++k) + name[k] ^= (byte)(((k + 1) ^ key) + k + 1); + + return Encodings.cp932.GetString (name, 0, name_length); + } + + public override void DecryptEntry (byte[] data, int offset, int length, QlieEntry entry) + { + if (offset < 0) + throw new ArgumentOutOfRangeException ("offset"); + if (length > data.Length || offset > data.Length - length) + throw new ArgumentOutOfRangeException ("length"); + uint arc_key = ArcKey; + + ulong hash = 0xA73C5F9DA73C5F9Dul; + ulong xor = arc_key ^ 0xFEC9753Eu; + xor |= xor << 32; + unsafe + { + fixed (byte* raw = data) + { + ulong* encoded = (ulong*)(raw + offset); + for (int i = 0; i < length / 8; ++i) + { + hash = MMX.PAddD (hash, 0xCE24F523CE24F523ul) ^ xor; + xor = *encoded ^ hash; + *encoded++ = xor; + } + } + } + } + } + internal class EncryptionV2 : QlieEncryption { public EncryptionV2 ()