From 65be58fbf29392a0c19a3ab110a9f48776a45689 Mon Sep 17 00:00:00 2001 From: Pat Hartl Date: Thu, 14 Nov 2024 20:53:08 -0600 Subject: [PATCH] Working validate files dialog Very basic, but it will compare local files against what's available on the server and allow you to selectively replace them. Save paths are ignored to avoid accidental replacement. Implements #142 --- .../Components/LibraryItemContextMenu.razor | 5 +-- .../UI/Components/ValidateFilesDialog.razor | 37 +++++++++++++++++-- LANCommander.SDK/Models/ArchiveEntry.cs | 1 + .../Models/ArchiveValidationConflict.cs | 13 +++++++ LANCommander.SDK/Services/GameService.cs | 32 ++++++++++++---- .../Controllers/Api/ArchivesController.cs | 3 +- 6 files changed, 77 insertions(+), 14 deletions(-) create mode 100644 LANCommander.SDK/Models/ArchiveValidationConflict.cs diff --git a/LANCommander.Launcher/UI/Components/LibraryItemContextMenu.razor b/LANCommander.Launcher/UI/Components/LibraryItemContextMenu.razor index 439a9ba6..8e4cae61 100644 --- a/LANCommander.Launcher/UI/Components/LibraryItemContextMenu.razor +++ b/LANCommander.Launcher/UI/Components/LibraryItemContextMenu.razor @@ -192,12 +192,11 @@ else { Title = $"File Conflicts", Maximizable = false, - DefaultMaximized = false, + DefaultMaximized = true, Closable = true, Draggable = true, Centered = true, - OkText = "Replace", - Footer = null, + OkText = "Replace Selected", }; var modalRef = ModalService.CreateModal(modalOptions, Model.DataItem as Game); diff --git a/LANCommander.Launcher/UI/Components/ValidateFilesDialog.razor b/LANCommander.Launcher/UI/Components/ValidateFilesDialog.razor index cb863fde..918ee9db 100644 --- a/LANCommander.Launcher/UI/Components/ValidateFilesDialog.razor +++ b/LANCommander.Launcher/UI/Components/ValidateFilesDialog.razor @@ -5,13 +5,34 @@ @inject SDK.Client Client @inject IMessageService MessageService - +
+ + + @context.LocalFileInfo?.CreationTime + + + @context.LocalFileInfo?.LastWriteTime + + + @if (context.LocalFileInfo?.Length > 0) + { + @ByteSizeLib.ByteSize.FromBytes(context.LocalFileInfo.Length) + } + + + @if (context.Length > 0) + { + @ByteSizeLib.ByteSize.FromBytes(context.Length) + } +
@code { - IEnumerable Conflicts = new List(); - IEnumerable Selected = new List(); + IEnumerable Conflicts = new List(); + IEnumerable Selected = new List(); + + bool Loading = true; protected override async Task OnInitializedAsync() { @@ -26,12 +47,22 @@ async Task LoadData() { Conflicts = await Client.Games.ValidateFilesAsync(Options.InstallDirectory, Options.Id); + + Loading = false; } public override async Task OnFeedbackOkAsync(ModalClosingEventArgs args) { + Loading = true; + StateHasChanged(); + await Task.Yield(); + await Client.Games.DownloadFilesAsync(Options.InstallDirectory, Options.Id, Selected.Select(e => e.FullName)); + Loading = false; + StateHasChanged(); + await Task.Yield(); + await base.CloseFeedbackAsync(); } } diff --git a/LANCommander.SDK/Models/ArchiveEntry.cs b/LANCommander.SDK/Models/ArchiveEntry.cs index 794f37fc..007e62ac 100644 --- a/LANCommander.SDK/Models/ArchiveEntry.cs +++ b/LANCommander.SDK/Models/ArchiveEntry.cs @@ -3,4 +3,5 @@ public class ArchiveEntry public string FullName { get; set; } public string Name { get; set; } public uint Crc32 { get; set; } + public long Length { get; set; } } \ No newline at end of file diff --git a/LANCommander.SDK/Models/ArchiveValidationConflict.cs b/LANCommander.SDK/Models/ArchiveValidationConflict.cs new file mode 100644 index 00000000..09d3fbcf --- /dev/null +++ b/LANCommander.SDK/Models/ArchiveValidationConflict.cs @@ -0,0 +1,13 @@ +using System.IO; + +namespace LANCommander.SDK.Models +{ + public class ArchiveValidationConflict + { + public string FullName { get; set; } + public string Name { get; set; } + public uint Crc32 { get; set; } + public long Length { get; set; } + public FileInfo LocalFileInfo { get; set; } + } +} diff --git a/LANCommander.SDK/Services/GameService.cs b/LANCommander.SDK/Services/GameService.cs index ec591981..18d221e8 100644 --- a/LANCommander.SDK/Services/GameService.cs +++ b/LANCommander.SDK/Services/GameService.cs @@ -1025,20 +1025,33 @@ namespace LANCommander.SDK.Services /// The game's install directory /// The game's ID /// List of file conflicts - public async Task> ValidateFilesAsync(string installDirectory, Guid gameId) + public async Task> ValidateFilesAsync(string installDirectory, Guid gameId) { var manifest = await ManifestHelper.ReadAsync(installDirectory, gameId); - var archive = await Client.GetRequestAsync($"/api/Archives/Contents/{gameId}/{manifest.Version}"); - var entries = await Client.GetRequestAsync>($"/api/Archives/{archive.Id}"); + var entries = await Client.GetRequestAsync>($"/api/Archives/Contents/{gameId}/{manifest.Version}"); - var conflictedEntries = new List(); + var conflictedEntries = new List(); + + var savePathEntries = manifest.SavePaths?.SelectMany(p => Client.Saves.GetFileSavePathEntries(p, installDirectory)) ?? new List(); foreach (var entry in entries) { + if (savePathEntries.Any(e => e.ArchivePath.Equals(entry.FullName, StringComparison.OrdinalIgnoreCase))) + continue; + + if (entry.FullName.EndsWith('/')) + continue; + var localFile = Path.Combine(installDirectory, entry.FullName.Replace('/', Path.DirectorySeparatorChar)); if (!Path.Exists(localFile)) - conflictedEntries.Add(entry); + conflictedEntries.Add(new ArchiveValidationConflict + { + Name = entry.Name, + FullName = entry.FullName, + Crc32 = entry.Crc32, + Length = entry.Length, + }); else { uint crc = 0; @@ -1062,7 +1075,13 @@ namespace LANCommander.SDK.Services } if (crc == 0 || crc != entry.Crc32) - conflictedEntries.Add(entry); + conflictedEntries.Add(new ArchiveValidationConflict + { + Name = entry.Name, + FullName = entry.FullName, + Crc32 = entry.Crc32, + LocalFileInfo = new FileInfo(localFile) + }); } } @@ -1096,7 +1115,6 @@ namespace LANCommander.SDK.Services { Overwrite = true, PreserveFileTime = true, - PreserveAttributes = true, }); } else // Skip to next entry diff --git a/LANCommander.Server/Controllers/Api/ArchivesController.cs b/LANCommander.Server/Controllers/Api/ArchivesController.cs index 11b9ba2a..22776c5e 100644 --- a/LANCommander.Server/Controllers/Api/ArchivesController.cs +++ b/LANCommander.Server/Controllers/Api/ArchivesController.cs @@ -110,7 +110,8 @@ namespace LANCommander.Server.Controllers.Api { FullName = entry.FullName, Name = entry.Name, - Crc32 = entry.Crc32 + Crc32 = entry.Crc32, + Length = entry.Length, }); } }