diff --git a/Wabbajack.Common/Utils.cs b/Wabbajack.Common/Utils.cs index 77ff661b..9b2034bc 100644 --- a/Wabbajack.Common/Utils.cs +++ b/Wabbajack.Common/Utils.cs @@ -147,13 +147,28 @@ namespace Wabbajack.Common return Convert.ToBase64String(data); } - public static string ToHEX(this byte[] bytes) + public static string ToHex(this byte[] bytes) { var builder = new StringBuilder(); for (var i = 0; i < bytes.Length; i++) builder.Append(bytes[i].ToString("x2")); return builder.ToString(); } + public static byte[] FromHex(this string hex) + { + return Enumerable.Range(0, hex.Length) + .Where(x => x % 2 == 0) + .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) + .ToArray(); + } + + public static DateTime AsUnixTime(this long timestamp) + { + System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc); + dtDateTime = dtDateTime.AddSeconds(timestamp).ToLocalTime(); + return dtDateTime; + } + /// /// Returns data from a base64 stream /// @@ -535,8 +550,8 @@ namespace Wabbajack.Common public static void CreatePatch(byte[] a, byte[] b, Stream output) { - var data_a = a.SHA256().FromBase64().ToHEX(); - var data_b = b.SHA256().FromBase64().ToHEX(); + var data_a = a.SHA256().FromBase64().ToHex(); + var data_b = b.SHA256().FromBase64().ToHex(); var cache_file = Path.Combine("patch_cache", $"{data_a}_{data_b}.patch"); if (!Directory.Exists("patch_cache")) Directory.CreateDirectory("patch_cache"); @@ -570,7 +585,7 @@ namespace Wabbajack.Common public static void TryGetPatch(string foundHash, string fileHash, out byte[] ePatch) { var patch_name = Path.Combine("patch_cache", - $"{foundHash.FromBase64().ToHEX()}_{fileHash.FromBase64().ToHEX()}.patch"); + $"{foundHash.FromBase64().ToHex()}_{fileHash.FromBase64().ToHex()}.patch"); ePatch = File.Exists(patch_name) ? File.ReadAllBytes(patch_name) : null; } diff --git a/Wabbajack.Lib/Downloaders/NexusDownloader.cs b/Wabbajack.Lib/Downloaders/NexusDownloader.cs index 8abc49e5..9498da77 100644 --- a/Wabbajack.Lib/Downloaders/NexusDownloader.cs +++ b/Wabbajack.Lib/Downloaders/NexusDownloader.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Wabbajack.Common; using Wabbajack.Lib.NexusApi; using Wabbajack.Lib.Validation; +using Game = Wabbajack.Common.Game; namespace Wabbajack.Lib.Downloaders { @@ -54,6 +55,8 @@ namespace Wabbajack.Lib.Downloaders Utils.Error($"Automated installs with Wabbajack requires a premium nexus account. {client.Username} is not a premium account."); return; } + + var updated = client.GetModsUpdatedSince(Game.Skyrim,DateTime.Now - TimeSpan.FromDays(30)); } public class State : AbstractDownloadState diff --git a/Wabbajack.Lib/NexusApi/NexusApi.cs b/Wabbajack.Lib/NexusApi/NexusApi.cs index e16c210c..44716889 100644 --- a/Wabbajack.Lib/NexusApi/NexusApi.cs +++ b/Wabbajack.Lib/NexusApi/NexusApi.cs @@ -197,7 +197,7 @@ namespace Wabbajack.Lib.NexusApi private T GetCached(string url) { - var code = Encoding.UTF8.GetBytes(url).ToHEX(); + var code = Encoding.UTF8.GetBytes(url).ToHex(); var cache_file = Path.Combine(Consts.NexusCacheDirectory, code + ".json"); if (File.Exists(cache_file) && DateTime.Now - File.GetLastWriteTime(cache_file) < Consts.NexusCacheExpiry) { @@ -310,6 +310,40 @@ namespace Wabbajack.Lib.NexusApi { public string URI { get; set; } } + + private class UpdatedMod + { + public long mod_id; + public long latest_file_update; + public long latest_mod_activity; + } + + public IEnumerable GetModsUpdatedSince(Game game, DateTime since) + { + var result = + Get>( + $"https://api.nexusmods.com/v1/games/{GameRegistry.Games[game].NexusName}/mods/updated.json?period=1m"); + return result.Where(r => r.latest_file_update.AsUnixTime() >= since) + .Select(m => m.mod_id) + .ToList(); + } + + public static void ClearCacheFor(HashSet<(Game, long)> mods) + { + Directory.EnumerateFiles(Consts.NexusCacheDirectory, "*.json") + .PMap(f => + { + Utils.Status("Cleaning Nexus cache for"); + var filename = Encoding.UTF8.GetString(Path.GetFileNameWithoutExtension(f).FromHex()); + foreach (var (game, modid) in mods) + { + if (filename.Contains(GameRegistry.Games[game].NexusName) && + (filename.Contains("\\" + modid + "\\") || + filename.Contains("\\" + modid + "."))) + File.Delete(f); + } + }); + } } } \ No newline at end of file diff --git a/Wabbajack.Lib/ReportBuilder.cs b/Wabbajack.Lib/ReportBuilder.cs index c6d09198..a87b1f3e 100644 --- a/Wabbajack.Lib/ReportBuilder.cs +++ b/Wabbajack.Lib/ReportBuilder.cs @@ -61,7 +61,7 @@ namespace Wabbajack.Lib $"#### Download Summary ({lst.Archives.Count} archives - {lst.Archives.Sum(a => a.Size).ToFileSizeString()})"); foreach (var archive in SortArchives(lst.Archives)) { - var hash = archive.Hash.FromBase64().ToHEX(); + var hash = archive.Hash.FromBase64().ToHex(); NoWrapText(archive.State.GetReportEntry(archive)); NoWrapText($" * Size : {archive.Size.ToFileSizeString()}"); NoWrapText($" * SHA256 : [{hash}](https://www.virustotal.com/gui/file/{hash})");