public static void Merge(Dictionary <string, FPakEntry> tempFiles, out Dictionary <string, FPakEntry> files, string mount) { files = new Dictionary <string, FPakEntry>(); foreach (FPakEntry entry in tempFiles.Values) { if (files.ContainsKey(mount + entry.GetPathWithoutExtension()) || entry.GetExtension().Equals(".uptnl") || entry.GetExtension().Equals(".uexp") || entry.GetExtension().Equals(".ubulk")) { continue; } if (entry.IsUE4Package()) // if .uasset { if (!tempFiles.ContainsKey(Path.ChangeExtension(entry.Name, ".umap"))) // but not including a .umap { string e = Path.ChangeExtension(entry.Name, ".uexp"); FPakEntry uexp = tempFiles.ContainsKey(e) ? tempFiles[e] : null; // add its uexp if (uexp != null) { entry.Uexp = uexp; } string u = Path.ChangeExtension(entry.Name, ".ubulk"); FPakEntry ubulk = tempFiles.ContainsKey(u) ? tempFiles[u] : null; // add its ubulk if (ubulk != null) { entry.Ubulk = ubulk; } else { string f = Path.ChangeExtension(entry.Name, ".ufont"); FPakEntry ufont = tempFiles.ContainsKey(f) ? tempFiles[f] : null; // add its ufont if (ufont != null) { entry.Ubulk = ufont; } } } } else if (entry.IsUE4Map()) // if .umap { string e = Path.ChangeExtension(entry.Name, ".uexp"); string u = Path.ChangeExtension(entry.Name, ".ubulk"); FPakEntry uexp = tempFiles.ContainsKey(e) ? tempFiles[e] : null; // add its uexp if (uexp != null) { entry.Uexp = uexp; } FPakEntry ubulk = tempFiles.ContainsKey(u) ? tempFiles[u] : null; // add its ubulk if (ubulk != null) { entry.Ubulk = ubulk; } } files[mount + entry.GetPathWithoutExtension()] = entry; } }
private IReadOnlyDictionary <string, GameFile> ReadIndexLegacy(bool caseInsensitive) { Ar.Position = Info.IndexOffset; var index = new FByteArchive($"{Name} - Index", ReadAndDecrypt((int)Info.IndexSize)); string mountPoint; try { mountPoint = index.ReadFString(); } catch (Exception e) { throw new InvalidAesKeyException($"Given aes key '{AesKey?.KeyString}'is not working with '{Name}'", e); } ValidateMountPoint(ref mountPoint); MountPoint = mountPoint; var fileCount = index.Read <int>(); var files = new Dictionary <string, GameFile>(fileCount); for (var i = 0; i < fileCount; i++) { var path = string.Concat(mountPoint, index.ReadFString()); var entry = new FPakEntry(this, path, index); if (entry.IsDeleted && entry.Size == 0) { continue; } if (entry.IsEncrypted) { EncryptedFileCount++; } if (caseInsensitive) { files[path.ToLowerInvariant()] = entry; } else { files[path] = entry; } } return(Files = files); }
public static void Set(this AssetPropertiesViewModel vm, FPakEntry entry) { Application.Current.Dispatcher.Invoke(delegate { string ext = string.Join(" ", entry.GetExtension(), entry.Uexp?.GetExtension(), entry.Ubulk?.GetExtension()); string offsets = string.Join(" ", "0x" + (entry.Offset + entry.StructSize).ToString("X2"), entry.Uexp != null ? "0x" + (entry.Uexp.Offset + entry.StructSize).ToString("X2") : string.Empty, entry.Ubulk != null ? "0x" + (entry.Ubulk.Offset + entry.StructSize).ToString("X2") : string.Empty); string tSize = Strings.GetReadableSize(entry.Size + (entry.Uexp?.Size ?? 0) + (entry.Ubulk?.Size ?? 0)); vm.AssetName = entry.GetNameWithExtension(); vm.PartOf = entry.PakFileName; vm.IncludedExtensions = ext.TrimEnd(); vm.Offsets = offsets.TrimEnd(); vm.TotalSize = tSize; vm.IsEncrypted = entry.Encrypted ? Properties.Resources.Yes : Properties.Resources.No; vm.CompMethod = ((ECompressionFlags)entry.CompressionMethodIndex).ToString(); }); }
private void Populate(dynamic nodeList, string pathWithoutFile, string seqPath, FPakEntry entry) { string folder; int p = seqPath.IndexOf('/'); if (p == -1) { folder = seqPath; seqPath = string.Empty; } else { folder = seqPath.Substring(0, p); int p1 = p + 1; seqPath = seqPath[p1..];
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); }
public static string GetJsonProperties(FPakEntry entry, string mount, bool loadContent) { PakPackage p = GetPakPackage(entry, mount, loadContent); if (!p.Equals(default))
public static string GetJsonProperties(FPakEntry entry, string mount) => GetJsonProperties(entry, mount, false);
private static async Task LoadBackupFile(bool updateMode = false) { OpenFileDialog openFiledialog = new OpenFileDialog(); openFiledialog.Title = "Choose your Backup File"; openFiledialog.InitialDirectory = FProp.Default.FOutput_Path + "\\Backups\\"; openFiledialog.Multiselect = false; openFiledialog.Filter = "FBKP Files (*.fbkp)|*.fbkp|All Files (*.*)|*.*"; if (openFiledialog.ShowDialog() == true) { DebugHelper.WriteLine($".PAKs: Loading {openFiledialog.FileName} as the backup file"); new UpdateMyProcessEvents("Comparing Files", "Waiting").Update(); FPakEntry[] BackupEntries; using (FileStream fileStream = new FileStream(openFiledialog.FileName, FileMode.Open)) using (BinaryReader reader = new BinaryReader(fileStream)) { DebugHelper.WriteLine(".PAKs: Populating FPakEntry[] for the backup file"); List <FPakEntry> entries = new List <FPakEntry>(); 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(); long compressionMethodIndex = reader.ReadInt32(); // we actually only need name and uncompressedSize to compare FPakEntry entry = new FPakEntry(name, offset, size, uncompressedSize, new byte[20], null, 0, 0, 0); entries.Add(entry); } BackupEntries = entries.ToArray(); } if (BackupEntries.Any()) { DebugHelper.WriteLine(".PAKs: FPakEntry[] for the backup file is populated"); List <FPakEntry> LocalEntries = new List <FPakEntry>(); foreach (FPakEntry[] PAKsFileInfos in PAKEntries.PAKToDisplay.Values) { PAKsFileInfos.ToList().ForEach(x => LocalEntries.Add(x)); } PAKEntries.PAKToDisplay.Clear(); DebugHelper.WriteLine(".PAKs: FPakEntry[] for the local .PAKs is populated"); //FILTER WITH THE OVERRIDED EQUALS METHOD (CHECKING FILE NAME AND FILE UNCOMPRESSED SIZE) DebugHelper.WriteLine($".PAKs: Comparing...\t File Size Check: {FProp.Default.FDiffFileSize}"); IEnumerable <FPakEntry> newAssets = LocalEntries.Except(BackupEntries); DebugHelper.WriteLine(".PAKs: Compared"); //ADD TO TREE DebugHelper.WriteLine(".PAKs: Filling Treeview with differentiated assets"); foreach (FPakEntry entry in newAssets) { string onlyFolders = entry.Name.Substring(0, entry.Name.LastIndexOf('/')); await FWindow.FMain.Dispatcher.InvokeAsync(() => { TreeViewUtility.PopulateTreeView(srt, onlyFolders.Substring(1)); }); } //ONLY LOAD THE DIFFERENCE WHEN WE CLICK ON A FOLDER FWindow.FCurrentPAK = "ComparedPAK-WindowsClient.pak"; PAKEntries.PAKToDisplay.Add("ComparedPAK-WindowsClient.pak", newAssets.ToArray()); await FWindow.FMain.Dispatcher.InvokeAsync(() => { FWindow.FMain.ViewModel = srt; }); DebugHelper.WriteLine(".PAKs: Treeview filled"); await FWindow.FMain.Dispatcher.InvokeAsync(() => { FWindow.FMain.TreeView_Main.IsEnabled = true; FWindow.FMain.MI_LoadAllPAKs.IsEnabled = true; FWindow.FMain.MI_BackupPAKs.IsEnabled = true; FWindow.FMain.MI_DifferenceMode.IsEnabled = true; if (updateMode) { FWindow.FMain.MI_UpdateMode.IsEnabled = true; } }); //PRINT REMOVED IF NO FILE SIZE CHECK if (!FProp.Default.FDiffFileSize) { DebugHelper.WriteLine(".PAKs: Checking deleted items"); new UpdateMyProcessEvents("Checking deleted items", "Waiting").Update(); IEnumerable <FPakEntry> removedAssets = BackupEntries.Except(LocalEntries.ToArray()); List <string> removedItems = new List <string>(); foreach (FPakEntry entry in removedAssets) { if (entry.Name.StartsWith("/FortniteGame/Content/Athena/Items/Cosmetics/")) { removedItems.Add(entry.Name.Substring(0, entry.Name.LastIndexOf("."))); } } if (removedItems.Count > 0) { DebugHelper.WriteLine(".PAKs: Items Removed/Renamed:"); new UpdateMyConsole("Items Removed/Renamed:", CColors.Red, true).Append(); removedItems.Distinct().ToList().ForEach(e => { new UpdateMyConsole($" - {e.Substring(1)}", CColors.White, true).Append(); DebugHelper.WriteLine($" - {e.Substring(1)}"); }); } } DebugHelper.WriteLine(".PAKs: PAK files have been compared successfully"); new UpdateMyProcessEvents("All PAK files have been compared successfully", "Success").Update(); umIsOk = true; } else { DebugHelper.WriteLine(".PAKs: FPakEntry[] for the backup file is empty"); } } else { DebugHelper.WriteLine(".PAKs: User canceled when he got asked to select a backup file"); new UpdateMyConsole("You change your mind pretty fast but it's fine ", CColors.White).Append(); new UpdateMyConsole("all paks have been loaded instead", CColors.Blue, true).Append(); FillTreeView(true); await FWindow.FMain.Dispatcher.InvokeAsync(() => { FWindow.FMain.TreeView_Main.IsEnabled = true; FWindow.FMain.MI_LoadAllPAKs.IsEnabled = true; FWindow.FMain.MI_BackupPAKs.IsEnabled = true; FWindow.FMain.MI_DifferenceMode.IsEnabled = true; FWindow.FMain.MI_DifferenceMode.IsChecked = false; if (updateMode) { FWindow.FMain.MI_UpdateMode.IsEnabled = true; FWindow.FMain.MI_UpdateMode.IsChecked = false; } }); } }
private static async Task LoadBackupFile() { OpenFileDialog openFiledialog = new OpenFileDialog(); openFiledialog.Title = "Choose your Backup File"; openFiledialog.InitialDirectory = FProp.Default.FOutput_Path + "\\Backups\\"; openFiledialog.Multiselect = false; openFiledialog.Filter = "FBKP Files (*.fbkp)|*.fbkp|All Files (*.*)|*.*"; if (openFiledialog.ShowDialog() == true) { new UpdateMyProcessEvents("Comparing Files", "Waiting").Update(); FPakEntry[] BackupEntries; using (FileStream fileStream = new FileStream(openFiledialog.FileName, FileMode.Open)) using (BinaryReader reader = new BinaryReader(fileStream)) { List <FPakEntry> entries = new List <FPakEntry>(); while (reader.BaseStream.Position < reader.BaseStream.Length) { FPakEntry entry = new FPakEntry(); entry.Pos = reader.ReadInt64(); entry.Size = reader.ReadInt64(); entry.UncompressedSize = reader.ReadInt64(); entry.Encrypted = reader.ReadBoolean(); entry.StructSize = reader.ReadInt32(); entry.Name = reader.ReadString(); entry.CompressionMethod = reader.ReadInt32(); entries.Add(entry); } BackupEntries = entries.ToArray(); } if (BackupEntries.Any()) { List <FPakEntry> LocalEntries = new List <FPakEntry>(); foreach (FPakEntry[] PAKsFileInfos in PAKEntries.PAKToDisplay.Values) { PAKsFileInfos.ToList().ForEach(x => LocalEntries.Add(x)); } PAKEntries.PAKToDisplay.Clear(); //FILTER WITH THE OVERRIDED EQUALS METHOD (CHECKING FILE NAME AND FILE UNCOMPRESSED SIZE) IEnumerable <FPakEntry> newAssets = LocalEntries.ToArray().Except(BackupEntries); //ADD TO TREE foreach (FPakEntry entry in newAssets) { string onlyFolders = entry.Name.Substring(0, entry.Name.LastIndexOf('/')); await FWindow.FMain.Dispatcher.InvokeAsync(() => { TreeViewUtility.PopulateTreeView(srt, onlyFolders.Substring(1)); }); } //ONLY LOAD THE DIFFERENCE WHEN WE CLICK ON A FOLDER FWindow.FCurrentPAK = "ComparedPAK-WindowsClient.pak"; PAKEntries.PAKToDisplay.Add("ComparedPAK-WindowsClient.pak", newAssets.ToArray()); await FWindow.FMain.Dispatcher.InvokeAsync(() => { FWindow.FMain.ViewModel = srt; }); new UpdateMyProcessEvents("All PAK files have been compared successfully", "Success").Update(); } } else { new UpdateMyConsole("You change your mind pretty fast but it's fine ", CColors.White).Append(); new UpdateMyConsole("all paks have been loaded instead", CColors.Blue, true).Append(); FillTreeView(true); } }
private IReadOnlyDictionary <string, GameFile> ReadIndexUpdated(bool caseInsensitive) { // Prepare primary index and decrypt if necessary Ar.Position = Info.IndexOffset; FArchive primaryIndex = new FByteArchive($"{Name} - Primary Index", ReadAndDecrypt((int)Info.IndexSize)); string mountPoint; try { mountPoint = primaryIndex.ReadFString(); } catch (Exception e) { throw new InvalidAesKeyException($"Given aes key '{AesKey?.KeyString}'is not working with '{Name}'", e); } ValidateMountPoint(ref mountPoint); MountPoint = mountPoint; var fileCount = primaryIndex.Read <int>(); EncryptedFileCount = 0; primaryIndex.Position += 8; // PathHashSeed if (!primaryIndex.ReadBoolean()) { throw new ParserException(primaryIndex, "No path hash index"); } primaryIndex.Position += 36; // PathHashIndexOffset (long) + PathHashIndexSize (long) + PathHashIndexHash (20 bytes) if (!primaryIndex.ReadBoolean()) { throw new ParserException(primaryIndex, "No directory index"); } var directoryIndexOffset = primaryIndex.Read <long>(); var directoryIndexSize = primaryIndex.Read <long>(); primaryIndex.Position += 20; // Directory Index hash var encodedPakEntriesSize = primaryIndex.Read <int>(); var encodedPakEntries = primaryIndex.ReadBytes(encodedPakEntriesSize); if (primaryIndex.Read <int>() < 0) { throw new ParserException("Corrupt pak PrimaryIndex detected"); } // Read FDirectoryIndex Ar.Position = directoryIndexOffset; var directoryIndex = new FByteArchive($"{Name} - Directory Index", ReadAndDecrypt((int)directoryIndexSize)); unsafe { fixed(byte *ptr = encodedPakEntries) { var directoryIndexLength = directoryIndex.Read <int>(); var files = new Dictionary <string, GameFile>(fileCount); for (var i = 0; i < directoryIndexLength; i++) { var dir = directoryIndex.ReadFString(); var dirDictLength = directoryIndex.Read <int>(); for (var j = 0; j < dirDictLength; j++) { var name = directoryIndex.ReadFString(); var path = string.Concat(mountPoint, dir, name); var entry = new FPakEntry(this, path, ptr + directoryIndex.Read <int>()); if (entry.IsEncrypted) { EncryptedFileCount++; } if (caseInsensitive) { files[path.ToLowerInvariant()] = entry; } else { files[path] = entry; } } } Files = files; return(files); } } }