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 = ((uint)length + 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; } } } }
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 void Decrypt(uint hash, byte[] buf, int offset, int length) { if (offset < 0 || offset > buf.Length) { throw new ArgumentException("offset"); } int count = Math.Min(length, buf.Length - offset) / 8; if (0 == count) { return; } ulong key = KeyFromHash(hash); unsafe { fixed(byte *data = buf) { ulong *first = (ulong *)(data + offset); ulong *last = first + count; while (first != last) { ulong v = *first ^ key; key = MMX.PAddW(key, v); *first++ = v; } } } }
private void Decrypt(byte[] buffer, int offset, int length, uint key) { if (offset < 0) { throw new ArgumentOutOfRangeException("offset"); } if (length > buffer.Length || offset > buffer.Length - length) { throw new ArgumentOutOfRangeException("length"); } ulong hash = 0xA73C5F9DA73C5F9Dul; ulong xor = ((uint)length + key) ^ 0xFEC9753Eu; xor |= xor << 32; unsafe { fixed(byte *raw = buffer) { ulong *encoded = (ulong *)(raw + offset); for (int i = 0; i < length / 8; ++i) { hash = MMX.PAddD(hash, 0xCE24F523CE24F523ul) ^ xor; xor = *encoded ^ hash; *encoded++ = xor; } } } }
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; } } } }
uint GenerateKey(byte[] key_data) { ulong hash = 0; ulong key = 0; unsafe { fixed(byte *data = key_data) { ulong *data64 = (ulong *)data; for (int i = key_data.Length / 8; i > 0; --i) { hash = MMX.PAddW(hash, 0x0307030703070307); key = MMX.PAddW(key, *data64++ ^ hash); } } } return((uint)(key ^ (key >> 32))); }
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 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, 0x0307030703070307); key = MMX.PAddW(key, *data64++ ^ hash); } return((uint)(key ^ (key >> 32))); } } }
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; } } } }
void psubd(int i) { mm[0] = MMX.PSubD(mm[0], mm[i]); }
void psubw(int i) { mm[0] = MMX.PSubW(mm[0], mm[i]); }
void psubb(int i) { mm[0] = MMX.PSubB(mm[0], mm[i]); }
void paddd(int i) { mm[0] = MMX.PAddD(mm[0], mm[i]); }
void paddw(int i) { mm[0] = MMX.PAddW(mm[0], mm[i]); }
void paddb(int i) { mm[0] = MMX.PAddB(mm[0], mm[i]); }