public override uint CalculateHash(byte[] data, int length) { if (length > data.Length) { throw new ArgumentOutOfRangeException("length"); } unsafe { fixed(byte *data8 = data) { ulong hash = 0; ulong key = 0; ulong *data64 = (ulong *)data8; for (int i = length / 8; i > 0; --i) { hash = MMX.PAddW(hash, 0xA35793A7A35793A7ul); key = MMX.PAddW(key, *data64++ ^ hash); key = MMX.PSllD(key, 3) | MMX.PSrlD(key, 29); } // MMX.PMAddWD (key, key >> 32); return((uint)((short)key * (short)(key >> 32) + (short)(key >> 16) * (short)(key >> 48))); } } }
unsafe void DecryptV1(byte *data, int length, QlieEntry entry) { var file_name = entry.Name; uint hash = 0x85F532; uint seed = 0x33F641; for (int i = 0; i < file_name.Length; i++) { hash += (uint)(file_name[i] << (i & 7)); seed ^= hash; } seed += ArcKey ^ (7 * ((uint)length & 0xFFFFFF) + (uint)length + hash + (hash ^ (uint)length ^ 0x8F32DCu)); seed = 9 * (seed & 0xFFFFFF); var table = GenerateTable(0x20, seed); // originally 0x2C, 12 dwords not used 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) { ulong t = table[k] | (ulong)table[k + 1] << 32; 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 + 2) & 0x1F; } }
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"); } if (length < 8) { return; } var file_name = entry.Name; uint hash = 0x85F532; uint seed = 0x33F641; for (int i = 0; i < file_name.Length; i++) { hash += (uint)(file_name[i] << (i & 7)); seed ^= hash; } seed += ArcKey ^ (7 * ((uint)length & 0xFFFFFF) + (uint)length + hash + (hash ^ (uint)length ^ 0x8F32DCu)); seed = 9 * (seed & 0xFFFFFF); var table = GenerateTable(0x20, seed); // originally 0x2C, 12 dwords not used unsafe { 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 d = data64[i] ^ hash64; data64[i] = d; hash64 = MMX.PAddB(hash64, d) ^ d; hash64 = MMX.PAddW(MMX.PSllD(hash64, 1), d); k = (k + 2) & 0x1F; } } } }
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; } }
public override void DecryptEntry(byte[] data, int offset, int length, QlieEntry entry) { var key_file = entry.KeyFile; if (null == key_file) { base.DecryptEntry(data, offset, length, entry); return; } // play it safe with 'unsafe' sections if (offset < 0) { throw new ArgumentOutOfRangeException("offset"); } if (length > data.Length || offset > data.Length - length) { throw new ArgumentOutOfRangeException("length"); } if (length < 8) { return; } var file_name = entry.RawName; uint hash = 0x85F532; uint seed = 0x33F641; for (uint i = 0; i < file_name.Length; i++) { hash += (i & 0xFF) * file_name[i]; seed ^= hash; } seed += ArcKey ^ (7 * ((uint)length & 0xFFFFFF) + (uint)length + hash + (hash ^ (uint)length ^ 0x8F32DCu)); seed = 9 * (seed & 0xFFFFFF); if (GameKeyData != null) { seed ^= 0x453A; } var mt = new QlieMersenneTwister(seed); if (key_file != null) { mt.XorState(key_file); } if (GameKeyData != null) { mt.XorState(GameKeyData); } // game code fills dword[41] table, but only the first 16 qwords are used ulong[] table = new ulong[16]; for (int i = 0; i < table.Length; ++i) { table[i] = mt.Rand64(); } // compensate for 9 discarded dwords for (int i = 0; i < 9; ++i) { mt.Rand(); } ulong hash64 = mt.Rand64(); uint t = mt.Rand() & 0xF; unsafe { fixed(byte *raw_data = &data[offset]) { ulong *data64 = (ulong *)raw_data; int qword_length = length / 8; for (int i = 0; i < qword_length; ++i) { hash64 = MMX.PAddD(hash64 ^ table[t], table[t]); ulong d = data64[i] ^ hash64; data64[i] = d; hash64 = MMX.PAddB(hash64, d) ^ d; hash64 = MMX.PAddW(MMX.PSllD(hash64, 1), d); t++; t &= 0xF; } } } }