diff --git a/CUE4Parse b/CUE4Parse index a01c1442..4f0c1da5 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit a01c144292b1f941b3d67b66ff4b723d4a90d336 +Subproject commit 4f0c1da58127d7b6d46b18c6242971dd140b0bc5 diff --git a/FModel/ViewModels/BackupManagerViewModel.cs b/FModel/ViewModels/BackupManagerViewModel.cs index 14497692..814258c1 100644 --- a/FModel/ViewModels/BackupManagerViewModel.cs +++ b/FModel/ViewModels/BackupManagerViewModel.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; +using CUE4Parse.FileProvider.Objects; using CUE4Parse.UE4.VirtualFileSystem; using FModel.Framework; using FModel.Services; @@ -20,6 +21,8 @@ namespace FModel.ViewModels; public class BackupManagerViewModel : ViewModel { + public const uint FBKP_MAGIC = 0x504B4246; + private ThreadWorkerViewModel _threadWorkerView => ApplicationService.ThreadWorkerView; private ApiEndpointViewModel _apiEndpointView => ApplicationService.ApiEndpointView; private ApplicationViewModel _applicationView => ApplicationService.ApplicationView; @@ -64,23 +67,21 @@ public class BackupManagerViewModel : ViewModel var backupFolder = Path.Combine(UserSettings.Default.OutputDirectory, "Backups"); var fileName = $"{_gameName}_{DateTime.Now:MM'_'dd'_'yyyy}.fbkp"; var fullPath = Path.Combine(backupFolder, fileName); + var func = new Func(x => !x.Path.EndsWith(".uexp") && !x.Path.EndsWith(".ubulk") && !x.Path.EndsWith(".uptnl")); using var fileStream = new FileStream(fullPath, FileMode.Create); using var compressedStream = LZ4Stream.Encode(fileStream, LZ4Level.L00_FAST); using var writer = new BinaryWriter(compressedStream); + writer.Write(FBKP_MAGIC); + writer.Write((byte) EBackupVersion.Latest); + writer.Write(_applicationView.CUE4Parse.Provider.Files.Values.Count(func)); + foreach (var asset in _applicationView.CUE4Parse.Provider.Files.Values) { - if (asset is not VfsEntry entry || entry.Path.EndsWith(".uexp") || - entry.Path.EndsWith(".ubulk") || entry.Path.EndsWith(".uptnl")) - continue; - - writer.Write((long) 0); - writer.Write((long) 0); - writer.Write(entry.Size); - writer.Write(entry.IsEncrypted); - writer.Write(0); - writer.Write($"/{entry.Path.ToLower()}"); - writer.Write(0); + if (!func(asset)) continue; + writer.Write(asset.Size); + writer.Write(asset.IsEncrypted); + writer.Write($"/{asset.Path.ToLower()}"); } SaveCheck(fullPath, fileName, "created", "create"); @@ -116,3 +117,12 @@ public class BackupManagerViewModel : ViewModel } } } + +public enum EBackupVersion : byte +{ + BeforeVersionWasAdded = 0, + Initial, + + LatestPlusOne, + Latest = LatestPlusOne - 1 +} diff --git a/FModel/ViewModels/Commands/LoadCommand.cs b/FModel/ViewModels/Commands/LoadCommand.cs index 3dfa13b1..2bb6a989 100644 --- a/FModel/ViewModels/Commands/LoadCommand.cs +++ b/FModel/ViewModels/Commands/LoadCommand.cs @@ -154,7 +154,16 @@ public class LoadCommand : ViewModelCommand FLogger.Append(ELog.Information, () => FLogger.Text($"Backup file older than current game is '{openFileDialog.FileName.SubstringAfterLast("\\")}'", Constants.WHITE, true)); - using var fileStream = new FileStream(openFileDialog.FileName, FileMode.Open); + var mode = UserSettings.Default.LoadingMode; + var entries = ParseBackup(openFileDialog.FileName, mode, cancellationToken); + + _applicationView.Status.UpdateStatusLabel($"{mode.ToString()[6..]} Folders & Packages"); + _applicationView.CUE4Parse.AssetsFolder.BulkPopulate(entries); + } + + private List ParseBackup(string path, ELoadingMode mode, CancellationToken cancellationToken = default) + { + using var fileStream = new FileStream(path, FileMode.Open); using var memoryStream = new MemoryStream(); if (fileStream.ReadUInt32() == _IS_LZ4) @@ -169,25 +178,41 @@ public class LoadCommand : ViewModelCommand using var archive = new FStreamArchive(fileStream.Name, memoryStream); var entries = new List(); - var mode = UserSettings.Default.LoadingMode; switch (mode) { case ELoadingMode.AllButNew: { - var paths = new Dictionary(); - while (archive.Position < archive.Length) + var paths = new HashSet(); + var magic = archive.Read(); + if (magic != BackupManagerViewModel.FBKP_MAGIC) { - cancellationToken.ThrowIfCancellationRequested(); + archive.Position -= sizeof(uint); + while (archive.Position < archive.Length) + { + cancellationToken.ThrowIfCancellationRequested(); - archive.Position += 29; - paths[archive.ReadString().ToLower()[1..]] = 0; - archive.Position += 4; + archive.Position += 29; + paths.Add(archive.ReadString().ToLower()[1..]); + archive.Position += 4; + } + } + else + { + var version = archive.Read(); + var count = archive.Read(); + for (var i = 0; i < count; i++) + { + cancellationToken.ThrowIfCancellationRequested(); + + archive.Position += sizeof(long) + sizeof(byte); + paths.Add(archive.ReadString().ToLower()[1..]); + } } foreach (var (key, value) in _applicationView.CUE4Parse.Provider.Files) { cancellationToken.ThrowIfCancellationRequested(); - if (value is not VfsEntry entry || paths.ContainsKey(key) || entry.Path.EndsWith(".uexp") || + if (value is not VfsEntry entry || paths.Contains(key) || entry.Path.EndsWith(".uexp") || entry.Path.EndsWith(".ubulk") || entry.Path.EndsWith(".uptnl")) continue; entries.Add(entry); @@ -198,31 +223,54 @@ public class LoadCommand : ViewModelCommand } case ELoadingMode.AllButModified: { - while (archive.Position < archive.Length) + var magic = archive.Read(); + if (magic != BackupManagerViewModel.FBKP_MAGIC) { - cancellationToken.ThrowIfCancellationRequested(); + archive.Position -= sizeof(uint); + while (archive.Position < archive.Length) + { + cancellationToken.ThrowIfCancellationRequested(); - archive.Position += 16; - var uncompressedSize = archive.Read(); - var isEncrypted = archive.ReadFlag(); - archive.Position += 4; - var fullPath = archive.ReadString().ToLower()[1..]; - archive.Position += 4; + archive.Position += 16; + var uncompressedSize = archive.Read(); + var isEncrypted = archive.ReadFlag(); + archive.Position += 4; + var fullPath = archive.ReadString().ToLower()[1..]; + archive.Position += 4; - if (fullPath.EndsWith(".uexp") || fullPath.EndsWith(".ubulk") || fullPath.EndsWith(".uptnl") || - !_applicationView.CUE4Parse.Provider.Files.TryGetValue(fullPath, out var asset) || asset is not VfsEntry entry || - entry.Size == uncompressedSize && entry.IsEncrypted == isEncrypted) - continue; - - entries.Add(entry); - _applicationView.Status.UpdateStatusLabel(entry.Vfs.Name); + AddEntry(fullPath, uncompressedSize, isEncrypted, entries); + } } + else + { + var version = archive.Read(); + var count = archive.Read(); + for (var i = 0; i < count; i++) + { + cancellationToken.ThrowIfCancellationRequested(); + var uncompressedSize = archive.Read(); + var isEncrypted = archive.ReadFlag(); + var fullPath = archive.ReadString().ToLower()[1..]; + + AddEntry(fullPath, uncompressedSize, isEncrypted, entries); + } + } break; } } - _applicationView.Status.UpdateStatusLabel($"{mode.ToString()[6..]} Folders & Packages"); - _applicationView.CUE4Parse.AssetsFolder.BulkPopulate(entries); + return entries; + } + + private void AddEntry(string path, long uncompressedSize, bool isEncrypted, List entries) + { + if (path.EndsWith(".uexp") || path.EndsWith(".ubulk") || path.EndsWith(".uptnl") || + !_applicationView.CUE4Parse.Provider.Files.TryGetValue(path, out var asset) || asset is not VfsEntry entry || + entry.Size == uncompressedSize && entry.IsEncrypted == isEncrypted) + return; + + entries.Add(entry); + _applicationView.Status.UpdateStatusLabel(entry.Vfs.Name); } }