public CpzArchive(ArcView arc, ArchiveFormat impl, ICollection <Entry> dir, CpzHeader header, Cpz5Decoder decoder, ArchiveKey key) : base(arc, impl, dir) { Header = header; Decoder = decoder; Key = key; }
public override ArcFile TryOpen(ArcView file) { if (null == KnownSchemes) { throw new OperationCanceledException("Outdated encryption schemes database"); } var cpz = CpzHeader.Parse(file); if (null == cpz) { return(null); } var index = file.View.ReadBytes(cpz.IndexOffset, cpz.IndexSize); if (!cpz.VerifyIndex(index)) { return(null); } int file_table_size = cpz.DirEntriesSize + cpz.FileEntriesSize; if (cpz.IndexKeySize > 24) { var index_key = UnpackIndexKey(index, file_table_size, cpz.IndexKeySize); for (int i = 0; i < file_table_size; ++i) { index[i] ^= index_key[(i + 3) % 0x3FF]; } } ArchiveKey arc_key = null; if (cpz.Version > 6) { arc_key = FindArchiveKey(file.Name); } if (null == arc_key) { arc_key = new ArchiveKey(); } var index_copy = new CowArray <byte> (index, 0, file_table_size).ToArray(); var cmvs_md5 = cpz.CmvsMd5.Clone() as uint[]; foreach (var scheme in KnownSchemes.Values.Where(s => s.Version == cpz.Version)) { var arc = ReadIndex(file, scheme, cpz, index, arc_key); if (null != arc) { return(arc); } // both CmvsMd5 and index was altered by ReadIndex in decryption attempt Array.Copy(cmvs_md5, cpz.CmvsMd5, 4); Array.Copy(index, index_copy, file_table_size); } throw new UnknownEncryptionScheme(); }
internal ArcFile ReadIndex(ArcView file, CmvsScheme scheme, CpzHeader cpz, byte[] index, ArchiveKey arc_key) { 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, arc_key.IndexDirKey); decoder.Init(cpz.MasterKey, cpz.CmvsMd5[2]); uint base_offset = cpz.IndexOffset + cpz.IndexSize; 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, arc_key.IndexEntryKey); 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 <= cpz.EntryNameOffset) { return(null); } int name_offset = cur_offset + cpz.EntryNameOffset; var name = Binary.GetCString(index, name_offset, cur_entries_end - name_offset); 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); int key_offset = cur_offset + 0x10; if (cpz.IsLongSize) { key_offset += 4; } entry.CheckSum = LittleEndian.ToUInt32(index, key_offset); entry.Key = LittleEndian.ToUInt32(index, key_offset + 4) + 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, arc_key)); }