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; } } } }
private void DecryptV3(byte[] data, int offset, int length, byte[] file_name, uint arc_hash, byte[] key_file, byte[] game_key) { // 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; uint hash = 0x85F532; uint seed = 0x33F641; for (uint i = 0; i < file_name.Length; i++) { hash += (i & 0xFF) * file_name[i]; seed ^= hash; } seed += arc_hash ^ (7 * ((uint)data.Length & 0xFFFFFF) + (uint)data.Length + hash + (hash ^ (uint)data.Length ^ 0x8F32DCu)); seed = 9 * (seed & 0xFFFFFF); if (game_key != null) seed ^= 0x453A; var mt = new QlieMersenneTwister (seed); if (key_file != null) mt.XorState (key_file); if (game_key != null) mt.XorState (game_key); // 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; } } } }