versioned backups
Some checks failed
FModel QA Builder / build (push) Has been cancelled

This commit is contained in:
Asval 2024-09-25 18:32:23 +02:00
parent 4b82eb37b0
commit bbb495b485
3 changed files with 96 additions and 38 deletions

@ -1 +1 @@
Subproject commit a01c144292b1f941b3d67b66ff4b723d4a90d336
Subproject commit 4f0c1da58127d7b6d46b18c6242971dd140b0bc5

View File

@ -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<GameFile, bool>(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
}

View File

@ -154,7 +154,16 @@ public class LoadCommand : ViewModelCommand<LoadingModesViewModel>
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<VfsEntry> 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<LoadingModesViewModel>
using var archive = new FStreamArchive(fileStream.Name, memoryStream);
var entries = new List<VfsEntry>();
var mode = UserSettings.Default.LoadingMode;
switch (mode)
{
case ELoadingMode.AllButNew:
{
var paths = new Dictionary<string, int>();
while (archive.Position < archive.Length)
var paths = new HashSet<string>();
var magic = archive.Read<uint>();
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<EBackupVersion>();
var count = archive.Read<int>();
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<LoadingModesViewModel>
}
case ELoadingMode.AllButModified:
{
while (archive.Position < archive.Length)
var magic = archive.Read<uint>();
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<long>();
var isEncrypted = archive.ReadFlag();
archive.Position += 4;
var fullPath = archive.ReadString().ToLower()[1..];
archive.Position += 4;
archive.Position += 16;
var uncompressedSize = archive.Read<long>();
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<EBackupVersion>();
var count = archive.Read<int>();
for (var i = 0; i < count; i++)
{
cancellationToken.ThrowIfCancellationRequested();
var uncompressedSize = archive.Read<long>();
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<VfsEntry> 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);
}
}