private Dictionary <string, FPakEntry> GetOldFiles(EPakLoader mode) { var diff = new Dictionary <string, FPakEntry>(); var ofd = new OpenFileDialog() { Title = Properties.Resources.SelectFile, InitialDirectory = Properties.Settings.Default.OutputPath + "\\Backups\\", Filter = Properties.Resources.FbkpFilter, Multiselect = false }; if ((bool)ofd.ShowDialog()) { string n = Path.GetFileName(ofd.FileName); StatusBarVm.statusBarViewModel.Set(string.Format(Properties.Resources.Analyzing, n), Properties.Resources.Processing); DebugHelper.WriteLine("{0} {1} {2} {3}", "[FModel]", "[PakMenuItemViewModel]", "[Loader]", $"Backup file is {n}"); var oldFilesTemp = new Dictionary <string, FPakEntry>(); using FileStream fileStream = new FileStream(ofd.FileName, FileMode.Open); BinaryReader checkReader = new BinaryReader(fileStream); bool isLz4 = checkReader.ReadUInt32() == 0x184D2204u; fileStream.Seek(0, SeekOrigin.Begin); var target = new MemoryStream(); if (isLz4) { using LZ4DecoderStream compressionStream = LZ4Stream.Decode(fileStream); compressionStream.CopyTo(target); } else { fileStream.CopyTo(target); } using (target) { target.Position = 0; using BinaryReader reader = new BinaryReader(target); while (reader.BaseStream.Position < reader.BaseStream.Length) { // we must follow this order long offset = reader.ReadInt64(); long size = reader.ReadInt64(); long uncompressedSize = reader.ReadInt64(); bool encrypted = reader.ReadBoolean(); long structSize = reader.ReadInt32(); string name = reader.ReadString(); int compressionMethodIndex = reader.ReadInt32(); // we only need name and uncompressedSize to compare FPakEntry entry = new FPakEntry("CatsWillDominateTheWorld.pak", name, offset, size, uncompressedSize, new byte[20], null, 0, (uint)compressionMethodIndex, 0); oldFilesTemp[entry.Name] = entry; } } var newFiles = new Dictionary <string, FPakEntry>(); foreach (var fileReader in Globals.CachedPakFiles) { foreach (var files in fileReader.Value) { newFiles.Add(files.Key, files.Value); } } Paks.Merge(oldFilesTemp, out var oldFiles, string.Empty); switch (mode) { case EPakLoader.New: foreach (var kvp in newFiles) { if (!oldFiles.TryGetValue(kvp.Key, out var entry)) { diff.Add(kvp.Key, kvp.Value); } } break; case EPakLoader.Modified: foreach (var kvp in newFiles) { if (oldFiles.TryGetValue(kvp.Key, out var entry)) { if (entry.UncompressedSize != kvp.Value.UncompressedSize) { diff.Add(kvp.Key, kvp.Value); } } } break; case EPakLoader.NewModified: foreach (var kvp in newFiles) { if (oldFiles.TryGetValue(kvp.Key, out var entry)) { if (entry.UncompressedSize != kvp.Value.UncompressedSize) { diff.Add(kvp.Key, kvp.Value); } } else { diff.Add(kvp.Key, kvp.Value); } } break; } var deleted = oldFiles.Where(kvp => !newFiles.TryGetValue(kvp.Key, out var _) && kvp.Key.StartsWith("/FortniteGame/Content/Athena/Items/Cosmetics/")).ToDictionary(x => x.Key, x => x.Value); if (deleted.Count > 0) { FConsole.AppendText(Properties.Resources.RemovedRenamedCosmetics, FColors.Red, true); foreach (var kvp in deleted) { FConsole.AppendText($" - {kvp.Value.Name.Substring(1)}", FColors.LightGray, true); } } } return(diff); }
void ReadIndexInternal(byte[] key, PakFilter filter, out Exception exc) { if (Initialized) { exc = new InvalidOperationException("Index is already initialized"); return; } if (Info.bEncryptedIndex && key == null) { exc = new ArgumentException("Index is encrypted but no key was provided", nameof(key)); return; } Stream.Position = Info.IndexOffset; BinaryReader IndexReader; if (Info.bEncryptedIndex) { IndexReader = new BinaryReader(new MemoryStream(AESDecryptor.DecryptAES(Reader.ReadBytes((int)Info.IndexSize), key))); int stringLen = IndexReader.ReadInt32(); if (stringLen > 512 || stringLen < -512) { exc = new ArgumentException("The provided key is invalid", nameof(key)); return; } if (stringLen < 0) { IndexReader.BaseStream.Position += (stringLen - 1) * 2; if (IndexReader.ReadUInt16() != 0) { exc = new ArgumentException("The provided key is invalid", nameof(key)); return; } } else { IndexReader.BaseStream.Position += stringLen - 1; if (IndexReader.ReadByte() != 0) { exc = new ArgumentException("The provided key is invalid", nameof(key)); return; } } IndexReader.BaseStream.Position = 0; } else { IndexReader = Reader; } Dictionary <string, FPakEntry> tempFiles; if (Info.Version >= EPakVersion.PATH_HASH_INDEX) { ReadIndexUpdated(IndexReader, key, out tempFiles, filter); } else { // https://github.com/EpicGames/UnrealEngine/blob/bf95c2cbc703123e08ab54e3ceccdd47e48d224a/Engine/Source/Runtime/PakFile/Private/IPlatformFilePak.cpp#L4509 MountPoint = IndexReader.ReadFString(); if (MountPoint.StartsWith("../../..")) { MountPoint = MountPoint.Substring(8); } else { // Weird mount point location... MountPoint = "/"; } if (!CaseSensitive) { MountPoint = MountPoint.ToLowerInvariant(); } var NumEntries = IndexReader.ReadInt32(); tempFiles = new Dictionary <string, FPakEntry>(NumEntries); for (int i = 0; i < NumEntries; i++) { var entry = new FPakEntry(IndexReader, Info.Version, CaseSensitive, FileName); // if there is no filter OR the filter passes if (filter == null || filter.CheckFilter(MountPoint + entry.Name, CaseSensitive)) { // Filename is without the MountPoint concatenated to save memory tempFiles[entry.Name] = entry; } } } Paks.Merge(tempFiles, out var files, MountPoint); Entries = files; DebugHelper.WriteLine("{0} {1} {2} {3}", "[FModel]", "[PakFileReader]", "[ReadIndexInternal]", $"{FileName} contains {Entries.Count} files, mount point: \"{this.MountPoint}\", version: {(int)this.Info.Version}"); if (Info.bEncryptedIndex) { // underlying stream is a MemoryStream of the decrypted index, might improve performance with a crypto stream of some sort IndexReader.Dispose(); } Reader.Dispose(); Initialized = true; exc = null; }