/// <summary> /// Decrypts the KIFINT archives using the known archive type, install directory, and name of executable with /// the V_CODE2 used to decrypt. /// </summary> /// <param name="type">The type of archive to look for and decrypt.</param> /// <param name="installDir">The installation directory for both the archives and executable.</param> /// <param name="vcode2">The V_CODE2 key obtained from the exe, used to decrypt the file names.</param> /// <param name="callback">The optional callback for progress made during decryption.</param> /// <returns>The <see cref="KifintLookup"/> merged with all loaded archives.</returns> /// /// <exception cref="ArgumentNullException"> /// <paramref name="installDir"/> or <paramref name="vcode2"/> is null. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="type"/> is <see cref="KifintType.Unknown"/>. /// </exception> public static KifintLookup Decrypt(KifintType type, string installDir, string vcode2, KifintProgressCallback callback = null) { if (vcode2 == null) { throw new ArgumentNullException(nameof(vcode2)); } if (type == KifintType.Unknown) { throw new ArgumentException($"{nameof(type)} cannot be {nameof(KifintType.Unknown)}!", nameof(type)); } KifintLookup lookup = new KifintLookup(type); string wildcard = EnumInfo <KifintType> .GetAttribute <KifintWildcardAttribute>(type).Wildcard; string[] files = Directory.GetFiles(installDir, wildcard); KifintProgressArgs progress = new KifintProgressArgs { ArchiveType = type, ArchiveIndex = 0, ArchiveCount = files.Length, }; foreach (string kifintPath in files) { progress.ArchiveName = Path.GetFileName(kifintPath); using (Stream stream = File.OpenRead(kifintPath)) lookup.Merge(Decrypt(type, stream, kifintPath, vcode2, progress, callback)); progress.ArchiveIndex++; } progress.EntryIndex = 0; progress.EntryCount = 0; callback?.Invoke(progress); return(lookup); }
/// <summary> /// Decrypts the KIFINT archives using the known archive type, install directory, and name of executable with /// the V_CODE2 used to decrypt. /// </summary> /// <param name="type">The type of archive to look for and decrypt.</param> /// <param name="stream">The stream to the open KIFINT archive.</param> /// <param name="installDir">The installation directory for both the archives and executable.</param> /// <param name="exePath">The path to the executable to extract the V_CODE2 key from.</param> /// <returns>The <see cref="KifintLookup"/> merged with all loaded archives.</returns> /// /// <exception cref="ArgumentNullException"> /// <paramref name="stream"/>, <paramref name="kifintPath"/>, or <paramref name="exePath"/> is null. /// </exception> /// <exception cref="ObjectDisposedException"> /// The <paramref name="stream"/> is closed. /// </exception> private static Kifint Decrypt(KifintType type, Stream stream, string kifintPath, string vcode2, KifintProgressArgs progress, KifintProgressCallback callback) { if (kifintPath == null) { throw new ArgumentNullException(nameof(kifintPath)); } if (vcode2 == null) { throw new ArgumentNullException(nameof(vcode2)); } BinaryReader reader = new BinaryReader(stream); KIFHDR hdr = reader.ReadUnmanaged <KIFHDR>(); if (hdr.Signature != "KIF") // It's really a KIF INT file { throw new UnexpectedFileTypeException(kifintPath, "KIF"); } KIFENTRY[] entries = reader.ReadUnmanagedArray <KIFENTRY>(hdr.EntryCount); progress.EntryIndex = 0; progress.EntryCount = entries.Length; uint tocSeed = GenTocSeed(vcode2); uint fileKey = 0; bool decrypt = false; // Obtain the decryption file key if one exists for (int i = 0; i < hdr.EntryCount; i++) { if (entries[i].FileName == "__key__.dat") { fileKey = MersenneTwister.GenRand(entries[i].Length); decrypt = true; break; } } const int ProgressThreshold = 500; // Decrypt the KIFINT entries using the file key if (decrypt) { for (uint i = 0; i < hdr.EntryCount; i++) { if (entries[i].FileName == "__key__.dat") { continue; } progress.EntryIndex++; if (i % ProgressThreshold == 0) { callback?.Invoke(progress); } // Give the entry the correct name UnobfuscateFileName(entries[i].FileNameRaw, tocSeed + i); // Give apply the extra offset to be decrypted entries[i].Offset += i; // Decrypt the entry's length and offset DecryptEntry(ref entries[i].Info, fileKey); } } return(new Kifint(kifintPath, entries, decrypt, fileKey, type)); }