Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Updater] Show zipmod files with changed version in filename as a single entry #181

Merged
merged 1 commit into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/KKManager.Core/Util/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,15 @@ public static async Task<bool> WithTimeout(this Task task, TimeSpan timeout, Can
}

public static IEnumerable<TOut> Attempt<TIn, TOut>(this IEnumerable<TIn> source, Func<TIn, TOut> action)
{
return Attempt(source, action, (@in, e) => Console.Error.WriteLine(e));
}

public static IEnumerable<TOut> Attempt<TIn, TOut>(this IEnumerable<TIn> source, Func<TIn, TOut> action, Action<TIn, Exception> onError)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (action == null) throw new ArgumentNullException(nameof(action));
if (onError == null) throw new ArgumentNullException(nameof(onError));

foreach (var item in source)
{
Expand All @@ -84,7 +90,7 @@ public static IEnumerable<TOut> Attempt<TIn, TOut>(this IEnumerable<TIn> source,
}
catch (Exception e)
{
Console.Error.WriteLine(e);
onError(item, e);
continue;
}
yield return output;
Expand Down
91 changes: 82 additions & 9 deletions src/KKManager.Updater/Windows/ModUpdateSelectDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using BrightIdeasSoftware;
using KKManager.Functions;
using KKManager.Updater.Data;
using KKManager.Updater.Properties;
using KKManager.Updater.Sources;
using KKManager.Util;

namespace KKManager.Updater.Windows
Expand All @@ -34,15 +34,15 @@ private ModUpdateSelectDialog()

objectListView2.EmptyListMsg = Resources.ModUpdateSelect_SelectTaskToView;
objectListView2.FormatRow += ObjectListView2_FormatRow;
olvColumnFileName.AspectGetter = rowObject => ((UpdateItem)rowObject).TargetPath.FullName.Substring(InstallDirectoryHelper.GameDirectory.FullName.Length);
olvColumnFileName.AspectName = "FileName";
olvColumnFileDate.AspectGetter = rowObject =>
{
var date = ((UpdateItem)rowObject).RemoteFile?.ModifiedTime;
var date = ((UpdateItemListData)rowObject).Modified;
if (date == null || date == DateTime.MinValue)
return Resources.ModUpdateSelect_WillBeRemoved;
return date.Value.ToShortDateString();
};
olvColumnFileSize.AspectGetter = rowObject => ((UpdateItem)rowObject).GetDownloadSize();
olvColumnFileSize.AspectName = "Size";
}

public static List<UpdateTask> ShowWindow(ModUpdateProgressDialog owner, List<UpdateTask> updateTasks)
Expand Down Expand Up @@ -92,11 +92,11 @@ private void buttonAccept_Click(object sender, EventArgs e)

private static void ObjectListView2_FormatRow(object sender, FormatRowEventArgs e)
{
if (e.Model is UpdateItem task)
if (e.Model is UpdateItemListData item)
{
if (task is DeleteFileUpdateItem || task.RemoteFile == null)
if (item.IsDeleted)
e.Item.ForeColor = Color.DarkRed;
else if (!task.TargetPath.Exists)
else if (item.IsNew)
e.Item.ForeColor = Color.Green;
}
}
Expand Down Expand Up @@ -133,8 +133,16 @@ private void objectListView1_ItemChecked(object sender, ItemCheckedEventArgs e)

private void objectListView1_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
{
var selection = objectListView1.SelectedObject as UpdateTask;
objectListView2.SetObjects(selection?.Items);
if (objectListView1.SelectedObject is UpdateTask selection)
{
var listItems = UpdateItemListData.ToListItems(selection.Items);
objectListView2.SetObjects(listItems);
}
else
{
objectListView2.ClearObjects();
}

if (objectListView2.GetItemCount() > 0)
objectListView2.AutoResizeColumns();
}
Expand All @@ -144,5 +152,70 @@ private void UpdateDownloadSizeLabel()
var sumFileSizes = FileSize.SumFileSizes(objectListView1.CheckedObjects.Cast<UpdateTask>().Select(x => x.TotalUpdateSize));
labelDownload.Text = sumFileSizes == FileSize.Empty ? Resources.ModUpdateSelect_SizeStatus_Nothing : string.Format(Resources.ModUpdateSelect_SizeStatus_BytesToDownload, sumFileSizes);
}

private sealed class UpdateItemListData
{
public static List<UpdateItemListData> ToListItems(List<UpdateItem> allUpdates)
{
// The goal is to correctly display if a zipmod is actually removed or just updated with a different filename
// Currently: strip the version number from the filename and group by the name, then do dirty work on the grouped UpdateItems
// TODO Do this properly by detecting these pairs in the UpdateItem factory, somehow, maybe

var allUpdatesWithVersions = allUpdates.Attempt(item =>
{
var match = Regex.Match(item.RemoteFile?.Name ?? item.TargetPath.Name, @"^(.+?)[_ -]v(\d+(\.\d+)*)\.zipmod$", RegexOptions.IgnoreCase);

var hasVersion = match.Success;

// Version extraction if it's ever needed in the future
//Version version = null;
//if (hasVersion)
//{
// var versionString = match.Groups[2].Value;
// // If version is just a single number, add .0 to the end so `new Version()` doesn't crash
// if (!match.Groups[3].Success) versionString += ".0";
// version = new Version(versionString);
//}

return new { item, name = hasVersion ? match.Groups[1].Value : null };
}, (item, exception) => Console.WriteLine($@"[Updater] WARN: Failed to parse filename of '{item}' - {exception.Message}")).ToList();

var results = new List<UpdateItemListData>(allUpdates.Count);

foreach (var group in allUpdatesWithVersions.GroupBy(x => x.name))
{
if (group.Key == null)
results.AddRange(group.Select(x => new UpdateItemListData(new[] { x.item })));
else
results.Add(new UpdateItemListData(group.Select(x => x.item).ToList()));
}

results.Sort((data1, data2) => string.Compare(data1.FileName, data2.FileName, StringComparison.CurrentCultureIgnoreCase));

return results;
}

private UpdateItemListData(ICollection<UpdateItem> items)
{
if (items == null) throw new ArgumentNullException(nameof(items));
if (items.Count == 0) throw new ArgumentException(@"Value cannot be an empty collection.", nameof(items));

Size = items.Select(x => x.GetDownloadSize()).OrderByDescending(x => x).First();
IsNew = items.All(x => x.TargetPath?.Exists != true);

var latestTargetPath = items.OrderByDescending(x => x.RemoteFile != null).Select(x => x.TargetPath).Where(x => x != null).OrderByDescending(x => x.Name, StringComparer.OrdinalIgnoreCase).First();
FileName = latestTargetPath.FullName.Substring(InstallDirectoryHelper.GameDirectory.FullName.Length);

var remote = items.Select(x => x.RemoteFile).Where(x => x != null).OrderByDescending(x => x.ModifiedTime).FirstOrDefault();
Modified = remote?.ModifiedTime;
IsDeleted = remote == null;
}

public string FileName { get; }
public DateTime? Modified { get; }
public FileSize Size { get; }
public bool IsNew { get; }
public bool IsDeleted { get; }
}
}
}
Loading