void DecryptIndexStage1(byte[] data, uint key, CmvsScheme scheme) { var secret = scheme.Cpz5Secret; var secret_key = new uint[24]; int secret_length = Math.Min(24, secret.Length); for (int i = 0; i < secret_length; ++i) { secret_key[i] = secret[i] - key; } int shift = (int)(((key >> 24) ^ (key >> 16) ^ (key >> 8) ^ key ^ 0xB) & 0xF) + 7; unsafe { fixed(byte *raw = data) { uint *data32 = (uint *)raw; int i = 5; for (int n = data.Length / 4; n > 0; --n) { *data32 = Binary.RotR((secret_key[i] ^ *data32) + scheme.IndexAddend, shift) + 0x01010101; ++data32; i = (i + 1) % 24; } byte *data8 = (byte *)data32; for (int n = data.Length & 3; n > 0; --n) { *data8 = (byte)((*data8 ^ (secret_key[i] >> (n * 4))) - scheme.IndexSubtrahend); ++data8; i = (i + 1) % 24; } } } }
public Cpz5Decoder(CmvsScheme scheme, uint key, uint summand) { m_scheme = scheme; Init(key, summand); }
public Cpz5Decoder(CmvsScheme scheme, uint key, uint summand) { m_scheme = scheme; Init (key, summand); }
ArcFile ReadIndex(ArcView file, CmvsScheme scheme, CpzHeader cpz, byte[] index) { var cmvs_md5 = Cmvs.MD5.Create(scheme.Md5Variant); cmvs_md5.Compute(cpz.CmvsMd5); DecryptIndexStage1(index, cpz.MasterKey ^ 0x3795B39A, scheme); var decoder = new Cpz5Decoder(scheme, cpz.MasterKey, cpz.CmvsMd5[1]); decoder.Decode(index, 0, cpz.DirEntriesSize, 0x3A); var key = new uint[4]; key[0] = cpz.CmvsMd5[0] ^ (cpz.MasterKey + 0x76A3BF29); key[1] = cpz.CmvsMd5[1] ^ cpz.MasterKey; key[2] = cpz.CmvsMd5[2] ^ (cpz.MasterKey + 0x10000000); key[3] = cpz.CmvsMd5[3] ^ cpz.MasterKey; DecryptIndexDirectory(index, cpz.DirEntriesSize, key); decoder.Init(cpz.MasterKey, cpz.CmvsMd5[2]); int base_offset = 0x40 + index.Length; int dir_offset = 0; var dir = new List <Entry>(); for (int i = 0; i < cpz.DirCount; ++i) { int dir_size = LittleEndian.ToInt32(index, dir_offset); if (dir_size <= 0x10 || dir_size > index.Length) { return(null); } int file_count = LittleEndian.ToInt32(index, dir_offset + 4); if (file_count >= 0x10000) { return(null); } int entries_offset = LittleEndian.ToInt32(index, dir_offset + 8); uint dir_key = LittleEndian.ToUInt32(index, dir_offset + 0xC); var dir_name = Binary.GetCString(index, dir_offset + 0x10, dir_size - 0x10); int next_entries_offset; if (i + 1 == cpz.DirCount) { next_entries_offset = cpz.FileEntriesSize; } else { next_entries_offset = LittleEndian.ToInt32(index, dir_offset + dir_size + 8); } int cur_entries_size = next_entries_offset - entries_offset; if (cur_entries_size <= 0) { return(null); } int cur_offset = cpz.DirEntriesSize + entries_offset; int cur_entries_end = cur_offset + cur_entries_size; decoder.Decode(index, cur_offset, cur_entries_size, 0x7E); for (int j = 0; j < 4; ++j) { key[j] = cpz.CmvsMd5[j] ^ (dir_key + scheme.DirKeyAddend[j]); } DecryptIndexEntry(index, cur_offset, cur_entries_size, key, scheme.IndexSeed); bool is_root_dir = dir_name == "root"; dir.Capacity = dir.Count + file_count; for (int j = 0; j < file_count; ++j) { int entry_size = LittleEndian.ToInt32(index, cur_offset); if (entry_size > index.Length || entry_size <= 0x18) { return(null); } var name = Binary.GetCString(index, cur_offset + 0x18, cur_entries_end - (cur_offset + 0x18)); if (!is_root_dir) { name = Path.Combine(dir_name, name); } var entry = FormatCatalog.Instance.Create <CpzEntry> (name); entry.Offset = LittleEndian.ToInt64(index, cur_offset + 4) + base_offset; entry.Size = LittleEndian.ToUInt32(index, cur_offset + 0xC); entry.CheckSum = LittleEndian.ToUInt32(index, cur_offset + 0x10); entry.Key = LittleEndian.ToUInt32(index, cur_offset + 0x14) + dir_key; if (!entry.CheckPlacement(file.MaxOffset)) { return(null); } dir.Add(entry); cur_offset += entry_size; } dir_offset += dir_size; } if (cpz.IsEncrypted) { decoder.Init(cpz.CmvsMd5[3], cpz.MasterKey); } return(new CpzArchive(file, this, dir, cpz, decoder)); }
ArcFile ReadIndex(ArcView file, CmvsScheme scheme, CpzHeader cpz, byte[] index) { var cmvs_md5 = Cmvs.MD5.Create (scheme.Md5Variant); cmvs_md5.Compute (cpz.CmvsMd5); DecryptIndexStage1 (index, cpz.MasterKey ^ 0x3795B39A, scheme.Cpz5Secret); var decoder = new Cpz5Decoder (scheme, cpz.MasterKey, cpz.CmvsMd5[1]); decoder.Decode (index, 0, cpz.DirEntriesSize, 0x3A); var key = new uint[4]; key[0] = cpz.CmvsMd5[0] ^ (cpz.MasterKey + 0x76A3BF29); key[1] = cpz.CmvsMd5[1] ^ cpz.MasterKey; key[2] = cpz.CmvsMd5[2] ^ (cpz.MasterKey + 0x10000000); key[3] = cpz.CmvsMd5[3] ^ cpz.MasterKey; DecryptIndexDirectory (index, cpz.DirEntriesSize, key); decoder.Init (cpz.MasterKey, cpz.CmvsMd5[2]); int base_offset = 0x40 + index.Length; int dir_offset = 0; var dir = new List<Entry>(); for (int i = 0; i < cpz.DirCount; ++i) { int dir_size = LittleEndian.ToInt32 (index, dir_offset); if (dir_size <= 0x10 || dir_size > index.Length) return null; int file_count = LittleEndian.ToInt32 (index, dir_offset+4); if (file_count >= 0x10000) return null; int entries_offset = LittleEndian.ToInt32 (index, dir_offset+8); uint dir_key = LittleEndian.ToUInt32 (index, dir_offset+0xC); var dir_name = Binary.GetCString (index, dir_offset+0x10, dir_size-0x10); int next_entries_offset; if (i + 1 == cpz.DirCount) next_entries_offset = cpz.FileEntriesSize; else next_entries_offset = LittleEndian.ToInt32 (index, dir_offset + dir_size + 8); int cur_entries_size = next_entries_offset - entries_offset; if (cur_entries_size <= 0) return null; int cur_offset = cpz.DirEntriesSize + entries_offset; int cur_entries_end = cur_offset + cur_entries_size; decoder.Decode (index, cur_offset, cur_entries_size, 0x7E); key[0] = cpz.CmvsMd5[0] ^ dir_key; key[1] = cpz.CmvsMd5[1] ^ (dir_key + 0x112233); key[2] = cpz.CmvsMd5[2] ^ dir_key; key[3] = cpz.CmvsMd5[3] ^ (dir_key + 0x34258765); DecryptIndexEntry (index, cur_offset, cur_entries_size, key); bool is_root_dir = dir_name == "root"; dir.Capacity = dir.Count + file_count; for (int j = 0; j < file_count; ++j) { int entry_size = LittleEndian.ToInt32 (index, cur_offset); if (entry_size > index.Length || entry_size <= 0x18) return null; var name = Binary.GetCString (index, cur_offset+0x18, cur_entries_end-(cur_offset+0x18)); if (!is_root_dir) name = Path.Combine (dir_name, name); var entry = FormatCatalog.Instance.Create<CpzEntry> (name); entry.Offset = LittleEndian.ToInt64 (index, cur_offset+4) + base_offset; entry.Size = LittleEndian.ToUInt32 (index, cur_offset+0xC); entry.CheckSum = LittleEndian.ToUInt32 (index, cur_offset+0x10); entry.Key = LittleEndian.ToUInt32 (index, cur_offset+0x14); entry.DirKey = dir_key; if (!entry.CheckPlacement (file.MaxOffset)) return null; dir.Add (entry); cur_offset += entry_size; } dir_offset += dir_size; } if (cpz.IsEncrypted) decoder.Init (cpz.CmvsMd5[3], cpz.MasterKey); return new CpzArchive (file, this, dir, cpz, decoder); }