/// <summary> /// Gets the KIFINT lookup with the specified known type in the collection. /// </summary> /// <param name="type">The type of the KIFINT lookup to get.</param> /// <returns>The KIFINT lookup with the specified type in the collection.</returns> /// /// <exception cref="ArgumentException"> /// <paramref name="type"/> is not defined or is <see cref="KifintType.Unknown"/>. /// </exception> /// <exception cref="KeyNotFoundException"> /// A known KIFINT lookup of <paramref name="type"/> is not present in the collection. /// </exception> public KifintLookup this[KifintType type] { get { ThrowIfUndefinedOrUnknown(type); return(knownLookups[type]); } set { ThrowIfUndefinedOrUnknown(type); if (knownLookups.TryGetValue(type, out KifintLookup lookup)) { lookups.Remove(lookup); } if (value == null) { knownLookups.Remove(type); } else { value.Update = Update; lookups.Add(value); knownLookups[type] = value; } if (type == KifintType.Update) { AssignUpdateLookup(value); } else if (type == KifintType.Image) { Image = value; } } }
/// <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); }
public static void RestoreEncryptedArchives(KifintType type, string installDir) { if (type == KifintType.Unknown) { throw new ArgumentException($"{nameof(type)} cannot be {nameof(KifintType.Unknown)}!", nameof(type)); } string backupDir = Path.Combine(installDir, "intbackup"); if (!Directory.Exists(backupDir)) { return; } var files = Enumerable.Empty <string>(); var wildcardAttr = EnumInfo.GetAttribute <KifintType, KifintWildcardAttribute>(type); foreach (string wildcard in wildcardAttr.Wildcards) { files = files.Concat(Directory.GetFiles(installDir, wildcard)); } //string[] files = Directory.GetFiles(backupDir, wildcard); foreach (string kifintBackup in files) { string kifintPath = Path.Combine(installDir, Path.GetFileName(kifintBackup)); if (File.Exists(kifintBackup) && File.Exists(kifintPath)) { File.Delete(kifintPath); File.Move(kifintBackup, kifintPath); } } }
/// <summary> /// Loads and decrypts the KIFINT archive entries using the V_CODE2.<para/> /// Using this will initialize with the specified KIFINT archive type. /// </summary> /// <param name="kifintPath">The path to the KIFINT archive to decrypt.</param> /// <param name="type">The type of archive to create.</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 loading.</param> /// <returns>The <see cref="KifintArchive"/> with all of the loaded entries.</returns> /// /// <exception cref="ArgumentNullException"> /// <paramref name="kifintPath"/> or <paramref name="vcode2"/> is null. /// </exception> public static KifintArchive LoadArchive(string kifintPath, KifintType type, string vcode2, KifintProgressCallback callback = null) { if (kifintPath == null) { throw new ArgumentNullException(nameof(kifintPath)); } if (vcode2 == null) { throw new ArgumentNullException(nameof(vcode2)); } KifintArchive archive; KifintProgressArgs progress = new KifintProgressArgs { ArchiveType = type, ArchiveIndex = 0, ArchiveCount = 1, }; progress.ArchiveName = Path.GetFileName(kifintPath); using (Stream stream = File.OpenRead(kifintPath)) archive = LoadLookup(type, stream, kifintPath, vcode2, progress, callback); progress.ArchiveIndex++; progress.EntryName = null; progress.EntryIndex = 0; progress.EntryCount = 0; callback?.Invoke(progress); return(archive); }
/*public static KifintLookupCollection DecryptLookups(KifintType type, string installDir, string vcode2, * KifintProgressCallback callback = null) * { * * }*/ #endregion #region DecryptArchive public static void DecryptArchives(string wildcard, string installDir, string vcode2, KifintProgressCallback callback = null) { if (vcode2 == null) { throw new ArgumentNullException(nameof(vcode2)); } KifintType type = KifintType.Unknown; 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); string kifintBackup = Path.Combine(Path.GetDirectoryName(installDir) + "intbackups"); using (Stream stream = File.OpenRead(kifintPath)) DecryptArchive(stream, kifintPath, kifintBackup, vcode2, false, progress, callback); progress.ArchiveIndex++; } progress.EntryName = null; progress.EntryIndex = 0; progress.EntryCount = 0; callback?.Invoke(progress); }
/// <summary> /// Decrypts the KIFINT archives using the wildcard search, install directory, and name of executable with the /// V_CODE2 used to decrypt.<para/> /// Using this will initialize with <see cref="KifintType.Unknown"/>. /// </summary> /// <param name="wildcard">The wildcard name of the files to look for and merge.</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="wildcard"/>, <paramref name="installDir"/>, or <paramref name="vcode2"/> is null. /// </exception> public static KifintLookup LoadLookup(string wildcard, string installDir, string vcode2, KifintProgressCallback callback = null) { if (vcode2 == null) { throw new ArgumentNullException(nameof(vcode2)); } KifintType type = KifintType.Unknown; KifintLookup lookup = new KifintLookup(type); 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(LoadLookup(type, stream, kifintPath, vcode2, progress, callback)); progress.ArchiveIndex++; } progress.EntryName = null; progress.EntryIndex = 0; progress.EntryCount = 0; callback?.Invoke(progress); return(lookup); }
/// <summary> /// Constructs a cached KIFINT archive with the specified path, entries, and file key. /// </summary> /// <param name="kifintPath">The absolute path to the KIFINT archive.</param> /// <param name="kifEntries">The array of unobfuscated KIFENTRIES inside the KIFINT.</param> /// <param name="decrypt">True if the file key is required.</param> /// <param name="fileKey">The file key when <paramref name="decrypt"/> is true.</param> private Kifint(string kifintPath, Kifint.KIFENTRY[] kifEntries, bool decrypt, uint fileKey, KifintType type) { FilePath = kifintPath; IsEncrypted = decrypt; if (IsEncrypted) { FileKey = fileKey; } ArchiveType = type; Dictionary <string, KifintEntry> entries = new Dictionary <string, KifintEntry>(kifEntries.Length); foreach (var kifEntry in kifEntries) { string fileName = kifEntry.FileName; if (fileName != "__key__.dat") { entries.Add(fileName, new KifintEntry(fileName, kifEntry, this)); } } Entries = new ReadOnlyDictionary <string, KifintEntry>(entries); }
public static void DecryptArchives(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)); } var files = Enumerable.Empty <string>(); var wildcardAttr = EnumInfo.GetAttribute <KifintType, KifintWildcardAttribute>(type); foreach (string wildcard in wildcardAttr.Wildcards) { files = files.Concat(Directory.GetFiles(installDir, wildcard)); } //string wildcard = EnumInfo.GetAttribute<KifintType, KifintWildcardAttribute>(type).Wildcard; //string[] files = Directory.GetFiles(installDir, wildcard); KifintProgressArgs progress = new KifintProgressArgs { ArchiveType = type, ArchiveIndex = 0, ArchiveCount = files.Count(), }; foreach (string kifintPath in files) { progress.ArchiveName = Path.GetFileName(kifintPath); string kifintBackup = Path.Combine(Path.GetDirectoryName(installDir) + "intbackups"); using (Stream stream = File.OpenRead(kifintPath)) DecryptArchive(stream, kifintPath, kifintBackup, vcode2, false, progress, callback); progress.ArchiveIndex++; } progress.EntryName = null; progress.EntryIndex = 0; progress.EntryCount = 0; callback?.Invoke(progress); }
/// <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)); }
/// <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 KifintArchive LoadLookup(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>(); UnexpectedFileTypeException.ThrowIfInvalid(hdr.Signature, KIFHDR.ExpectedSignature); KIFENTRY[] entries = reader.ReadUnmanagedArray <KIFENTRY>(hdr.EntryCount); progress.EntryName = null; progress.EntryIndex = 0; progress.EntryCount = entries.Length; // Table of contents seed uint tocSeed = GenerateTocSeed(vcode2); uint fileKey = 0; int fileKeyIndex = -1; Blowfish blowfish = null; // Obtain the decryption file key if one exists for (int i = 0; i < hdr.EntryCount; i++) { if (entries[i].FileName == KeyFileName) { fileKey = MersenneTwister.GenRand(entries[i].Length); blowfish = CatDebug.NewBlowfish(fileKey); fileKeyIndex = i; break; } } const int ProgressThreshold = 500; // Decrypt the KIFINT entries using the file key if (fileKeyIndex != -1) { for (uint i = 0; i < hdr.EntryCount; i++, progress.EntryIndex++) { if (unchecked ((int)i) == fileKeyIndex) { continue; } // Give the entry the correct name UnobfuscateFileName(entries[i].FileNameRaw, unchecked (tocSeed + i)); // Apply the extra offset before decryption entries[i].Offset += i; // Decrypt the entry's offset and length blowfish.Decrypt(ref entries[i].Info); progress.EntryName = entries[i].FileName; if (i % ProgressThreshold == 0 || i + 1 == hdr.EntryCount) { callback?.Invoke(progress); } } } return(new KifintArchive(kifintPath, entries, fileKeyIndex != -1, fileKey, type, blowfish)); }
/// <summary> /// Reads the KIFINT archive from the stream. For use with <see cref="KifintLookup"/>. /// </summary> /// <param name="reader">The reader for the current stream.</param> /// <param name="version">The current version being read.</param> /// <param name="installDir">The installation directory where the archive is located.</param> /// <returns>The loaded cached KIFINT archive.</returns> internal static Kifint Read(BinaryReader reader, int version, string installDir, KifintType type) { Kifint kifint = new Kifint { FilePath = Path.Combine(installDir, reader.ReadString()), ArchiveType = type, }; kifint.IsEncrypted = reader.ReadBoolean(); kifint.FileKey = reader.ReadUInt32(); if (!kifint.IsEncrypted) { kifint.FileKey = 0; } int count = reader.ReadInt32(); Dictionary <string, KifintEntry> entries = new Dictionary <string, KifintEntry>(count); for (int i = 0; i < count; i++) { KifintEntry entry = KifintEntry.Read(reader, version, kifint); entries.Add(entry.FileName, entry); } kifint.Entries = new ReadOnlyDictionary <string, KifintEntry>(entries); return(kifint); }