From 5908b07509d0f6855ccd8fd940a91f15b37bedd1 Mon Sep 17 00:00:00 2001 From: Jason Ngo Date: Wed, 30 Apr 2025 13:34:03 -0400 Subject: [PATCH 01/10] Check only different mod manifests --- Program.cs | 52 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/Program.cs b/Program.cs index 4f69da0..94c7be9 100755 --- a/Program.cs +++ b/Program.cs @@ -13,6 +13,8 @@ namespace ModlinksShaVerifier { internal static class Program { + private const string RawGithubUrl = "https://github.com/hk-modding/modlinks/raw"; + private static readonly HttpClient _Client = new(); private static string ShaToString(byte[] hash) @@ -58,21 +60,38 @@ internal static async Task Main(string[] args) var sw = new Stopwatch(); sw.Start(); - if (args.Length != 1) + if (args.Length != 2) { - await Console.Error.WriteLineAsync("Usage: ModlinksShaVerifier [FILE]"); + await Console.Error.WriteLineAsync("Usage: ModlinksShaVerifier [FILE] [COMPARE_SHA]"); return 1; } - var path = args[0]; + string path = args[0]; + var fileName = Path.GetFileName(path); + + string compareSha = args[1]; - if (!File.Exists(path)) + string compareCommitUrl = $"{RawGithubUrl}/{compareSha}/{fileName}"; + + string compareContents = await _Client.GetStringAsync(compareCommitUrl); + + await Console.Out.WriteLineAsync($"Compare contents: " + compareContents); + + var compareDocument = new XmlDocument(); + compareDocument.LoadXml(compareContents); + var modLinksNode = compareDocument.DocumentElement; + + if (modLinksNode == null) { - await Console.Error.WriteLineAsync($"Unable to access {path}! Does it exist?"); + await Console.Error.WriteLineAsync("ModLinks XML node could not be found!"); return 1; } + + var reader = XmlReader.Create(path, new XmlReaderSettings {Async = true}); - var reader = XmlReader.Create(path, new XmlReaderSettings {Async = true}); + var compareManifests = modLinksNode.ChildNodes; + + await Console.Out.WriteLineAsync($"Manifests: " + compareManifests.Count); var serializer = new XmlSerializer(typeof(Manifest)); @@ -88,6 +107,27 @@ internal static async Task Main(string[] args) var manifest = (Manifest?) serializer.Deserialize(reader) ?? throw new InvalidDataException(); + bool matching = false; + for (int i = 0; i < compareManifests.Count; i++) + { + var xmlNode = compareManifests[i] ?? throw new InvalidDataException(); + var compareManifestStr = xmlNode.OuterXml; + + var manifestStr = await reader.ReadOuterXmlAsync(); + + if (compareManifestStr == manifestStr) + { + matching = true; + break; + } + } + + if (matching) + { + await Console.Out.WriteLineAsync($"Remote and local manifests of mod \"{manifest.Name}\" are matching!"); + continue; + } + checks.Add(CheckSingleSha(manifest)); } From 323d06cb08db0442f4892d36100026c5705239d5 Mon Sep 17 00:00:00 2001 From: Jason Ngo Date: Wed, 30 Apr 2025 13:35:35 -0400 Subject: [PATCH 02/10] File exists check --- Program.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Program.cs b/Program.cs index 94c7be9..8578e11 100755 --- a/Program.cs +++ b/Program.cs @@ -67,6 +67,13 @@ internal static async Task Main(string[] args) } string path = args[0]; + + if (!File.Exists(path)) + { + await Console.Error.WriteLineAsync($"Unable to access {path}! Does it exist?"); + return 1; + } + var fileName = Path.GetFileName(path); string compareSha = args[1]; From 9672ff77c164b0b2e58b576d6de51028026955d0 Mon Sep 17 00:00:00 2001 From: Jason Ngo Date: Wed, 30 Apr 2025 13:37:22 -0400 Subject: [PATCH 03/10] Remove debug log statements --- Program.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Program.cs b/Program.cs index 8578e11..62ae9e6 100755 --- a/Program.cs +++ b/Program.cs @@ -81,8 +81,6 @@ internal static async Task Main(string[] args) string compareCommitUrl = $"{RawGithubUrl}/{compareSha}/{fileName}"; string compareContents = await _Client.GetStringAsync(compareCommitUrl); - - await Console.Out.WriteLineAsync($"Compare contents: " + compareContents); var compareDocument = new XmlDocument(); compareDocument.LoadXml(compareContents); @@ -97,8 +95,6 @@ internal static async Task Main(string[] args) var reader = XmlReader.Create(path, new XmlReaderSettings {Async = true}); var compareManifests = modLinksNode.ChildNodes; - - await Console.Out.WriteLineAsync($"Manifests: " + compareManifests.Count); var serializer = new XmlSerializer(typeof(Manifest)); From 5af7bde96a75647b20f5665a42319ba308649770 Mon Sep 17 00:00:00 2001 From: Jason Ngo Date: Wed, 30 Apr 2025 15:20:08 -0400 Subject: [PATCH 04/10] Diff handler --- ModLinks.cs | 21 ++++++++++++++++----- Program.cs | 25 ++++++++++++++++--------- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/ModLinks.cs b/ModLinks.cs index 2694dfd..c0253a4 100644 --- a/ModLinks.cs +++ b/ModLinks.cs @@ -1,10 +1,7 @@ using System; -using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Xml; -using System.Xml.Schema; using System.Xml.Serialization; namespace ModlinksShaVerifier; @@ -58,7 +55,7 @@ public override string ToString() } } -public class Links +public class Links : IEquatable { public Link? Windows; public Link? Mac; @@ -82,9 +79,17 @@ public IEnumerable AsEnumerable() if (Linux is not null) yield return Linux; } + + public bool Equals(Links? other) + { + if (other is null) return false; + return ((Windows == null && other.Windows == null) || (Windows != null && Windows.Equals(other.Windows))) && + ((Mac == null && other.Mac == null) || (Mac != null && Mac.Equals(other.Mac))) && + ((Linux == null && other.Linux == null) || (Linux != null && Linux.Equals(other.Linux))); + } } -public class Link +public class Link : IEquatable { [XmlAttribute] public string SHA256 = null!; @@ -96,6 +101,12 @@ public override string ToString() { return $"[Link: {nameof(SHA256)} = {SHA256}, {nameof(URL)}: {URL}]"; } + + public bool Equals(Link? other) + { + if (other is null) return false; + return SHA256 == other.SHA256 && URL == other.URL.Trim(); + } } [Serializable] diff --git a/Program.cs b/Program.cs index 62ae9e6..308d750 100755 --- a/Program.cs +++ b/Program.cs @@ -92,7 +92,7 @@ internal static async Task Main(string[] args) return 1; } - var reader = XmlReader.Create(path, new XmlReaderSettings {Async = true}); + var reader = XmlReader.Create(path, new XmlReaderSettings {Async = true}); var compareManifests = modLinksNode.ChildNodes; @@ -114,11 +114,21 @@ internal static async Task Main(string[] args) for (int i = 0; i < compareManifests.Count; i++) { var xmlNode = compareManifests[i] ?? throw new InvalidDataException(); - var compareManifestStr = xmlNode.OuterXml; - - var manifestStr = await reader.ReadOuterXmlAsync(); - - if (compareManifestStr == manifestStr) + var stream = new MemoryStream(); + var writer = new StreamWriter(stream); + await writer.WriteAsync(xmlNode.OuterXml); + await writer.FlushAsync(); + + stream.Position = 0; + + var compareManifest = (Manifest?) serializer.Deserialize(stream) ?? throw new InvalidDataException(); + + if (compareManifest.Name != manifest.Name) continue; + + if (compareManifest.Name == manifest.Name && + compareManifest.Description == manifest.Description && + compareManifest.Dependencies == null && manifest.Dependencies == null || compareManifest.Dependencies != null && compareManifest.Dependencies.SequenceEqual(manifest.Dependencies) && + compareManifest.Links.Equals(manifest.Links)) { matching = true; break; @@ -126,10 +136,7 @@ internal static async Task Main(string[] args) } if (matching) - { - await Console.Out.WriteLineAsync($"Remote and local manifests of mod \"{manifest.Name}\" are matching!"); continue; - } checks.Add(CheckSingleSha(manifest)); } From ac47d3b58a222ae6ef0e32080df02069eef12e0b Mon Sep 17 00:00:00 2001 From: Jason Ngo Date: Wed, 30 Apr 2025 16:32:28 -0400 Subject: [PATCH 05/10] Accept a second file instead of a commit SHA - Remove additional condition --- Program.cs | 65 ++++++++++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/Program.cs b/Program.cs index 308d750..1dac77c 100755 --- a/Program.cs +++ b/Program.cs @@ -13,8 +13,6 @@ namespace ModlinksShaVerifier { internal static class Program { - private const string RawGithubUrl = "https://github.com/hk-modding/modlinks/raw"; - private static readonly HttpClient _Client = new(); private static string ShaToString(byte[] hash) @@ -62,58 +60,59 @@ internal static async Task Main(string[] args) if (args.Length != 2) { - await Console.Error.WriteLineAsync("Usage: ModlinksShaVerifier [FILE] [COMPARE_SHA]"); + await Console.Error.WriteLineAsync("Usage: ModlinksShaVerifier [CURRENT_FILE] [INCOMING_FILE]"); return 1; } - string path = args[0]; + string currentPath = args[0]; - if (!File.Exists(path)) + if (!File.Exists(currentPath)) { - await Console.Error.WriteLineAsync($"Unable to access {path}! Does it exist?"); + await Console.Error.WriteLineAsync($"Unable to access current XML file {currentPath}! Does it exist?"); return 1; } - - var fileName = Path.GetFileName(path); - - string compareSha = args[1]; - string compareCommitUrl = $"{RawGithubUrl}/{compareSha}/{fileName}"; + var incomingPath = args[1]; - string compareContents = await _Client.GetStringAsync(compareCommitUrl); - - var compareDocument = new XmlDocument(); - compareDocument.LoadXml(compareContents); - var modLinksNode = compareDocument.DocumentElement; + if (!File.Exists(incomingPath)) + { + await Console.Error.WriteLineAsync($"Unable to access incoming XML file {incomingPath}! Does it exist?"); + return 1; + } + + string currentContents = await File.ReadAllTextAsync(currentPath); + var currentDocument = new XmlDocument(); + currentDocument.LoadXml(currentContents); + var modLinksNode = currentDocument.DocumentElement; if (modLinksNode == null) { - await Console.Error.WriteLineAsync("ModLinks XML node could not be found!"); + await Console.Error.WriteLineAsync("Could not get document element of current XML file!"); return 1; } - - var reader = XmlReader.Create(path, new XmlReaderSettings {Async = true}); - var compareManifests = modLinksNode.ChildNodes; + var currentManifests = modLinksNode.ChildNodes; + + var incomingReader = XmlReader.Create(incomingPath, new XmlReaderSettings {Async = true}); var serializer = new XmlSerializer(typeof(Manifest)); List> checks = new(); - while (await reader.ReadAsync()) + while (await incomingReader.ReadAsync()) { - if (reader.NodeType != XmlNodeType.Element) + if (incomingReader.NodeType != XmlNodeType.Element) continue; - if (reader.Name != "Manifest") + if (incomingReader.Name != "Manifest") continue; - var manifest = (Manifest?) serializer.Deserialize(reader) ?? throw new InvalidDataException(); + var incomingManifest = (Manifest?) serializer.Deserialize(incomingReader) ?? throw new InvalidDataException(); bool matching = false; - for (int i = 0; i < compareManifests.Count; i++) + for (int i = 0; i < currentManifests.Count; i++) { - var xmlNode = compareManifests[i] ?? throw new InvalidDataException(); + var xmlNode = currentManifests[i] ?? throw new InvalidDataException(); var stream = new MemoryStream(); var writer = new StreamWriter(stream); await writer.WriteAsync(xmlNode.OuterXml); @@ -121,14 +120,12 @@ internal static async Task Main(string[] args) stream.Position = 0; - var compareManifest = (Manifest?) serializer.Deserialize(stream) ?? throw new InvalidDataException(); - - if (compareManifest.Name != manifest.Name) continue; + var currentManifest = (Manifest?) serializer.Deserialize(stream) ?? throw new InvalidDataException(); - if (compareManifest.Name == manifest.Name && - compareManifest.Description == manifest.Description && - compareManifest.Dependencies == null && manifest.Dependencies == null || compareManifest.Dependencies != null && compareManifest.Dependencies.SequenceEqual(manifest.Dependencies) && - compareManifest.Links.Equals(manifest.Links)) + if (currentManifest.Name == incomingManifest.Name && + currentManifest.Description == incomingManifest.Description && + currentManifest.Dependencies == null && incomingManifest.Dependencies == null || currentManifest.Dependencies != null && currentManifest.Dependencies.SequenceEqual(incomingManifest.Dependencies) && + currentManifest.Links.Equals(incomingManifest.Links)) { matching = true; break; @@ -138,7 +135,7 @@ internal static async Task Main(string[] args) if (matching) continue; - checks.Add(CheckSingleSha(manifest)); + checks.Add(CheckSingleSha(incomingManifest)); } var res = await Task.WhenAll(checks); From 59bbc3162c1cf1ba61a95cabc8a8c749c1548cf8 Mon Sep 17 00:00:00 2001 From: Jason Ngo Date: Wed, 30 Apr 2025 21:07:20 -0400 Subject: [PATCH 06/10] Less nested looping, compare hash codes --- ModLinks.cs | 28 ++++++++------------ Program.cs | 74 +++++++++++++++++++---------------------------------- 2 files changed, 36 insertions(+), 66 deletions(-) diff --git a/ModLinks.cs b/ModLinks.cs index c0253a4..d16f763 100644 --- a/ModLinks.cs +++ b/ModLinks.cs @@ -53,9 +53,15 @@ public override string ToString() + $"\t{nameof(Description)}: {Description}\n" + "}"; } + + public override int GetHashCode() + { + var hashCode = HashCode.Combine(Name, Description, Links); + return Dependencies?.Aggregate(hashCode, HashCode.Combine) ?? hashCode; + } } -public class Links : IEquatable +public record Links { public Link? Windows; public Link? Mac; @@ -69,7 +75,7 @@ public override string ToString() + $"\t{nameof(Linux)} = {Linux}\n" + "}"; } - + public IEnumerable AsEnumerable() { if (Windows is not null) @@ -79,34 +85,20 @@ public IEnumerable AsEnumerable() if (Linux is not null) yield return Linux; } - - public bool Equals(Links? other) - { - if (other is null) return false; - return ((Windows == null && other.Windows == null) || (Windows != null && Windows.Equals(other.Windows))) && - ((Mac == null && other.Mac == null) || (Mac != null && Mac.Equals(other.Mac))) && - ((Linux == null && other.Linux == null) || (Linux != null && Linux.Equals(other.Linux))); - } } -public class Link : IEquatable +public record Link { [XmlAttribute] public string SHA256 = null!; [XmlText] - public string URL = null!; + public string URL = null!; public override string ToString() { return $"[Link: {nameof(SHA256)} = {SHA256}, {nameof(URL)}: {URL}]"; } - - public bool Equals(Link? other) - { - if (other is null) return false; - return SHA256 == other.SHA256 && URL == other.URL.Trim(); - } } [Serializable] diff --git a/Program.cs b/Program.cs index 1dac77c..1fc1d92 100755 --- a/Program.cs +++ b/Program.cs @@ -65,7 +65,7 @@ internal static async Task Main(string[] args) } string currentPath = args[0]; - + if (!File.Exists(currentPath)) { await Console.Error.WriteLineAsync($"Unable to access current XML file {currentPath}! Does it exist?"); @@ -73,32 +73,36 @@ internal static async Task Main(string[] args) } var incomingPath = args[1]; - + if (!File.Exists(incomingPath)) { - await Console.Error.WriteLineAsync($"Unable to access incoming XML file {incomingPath}! Does it exist?"); + await Console.Error.WriteLineAsync( + $"Unable to access incoming XML file {incomingPath}! Does it exist?"); return 1; } - string currentContents = await File.ReadAllTextAsync(currentPath); - var currentDocument = new XmlDocument(); - currentDocument.LoadXml(currentContents); - var modLinksNode = currentDocument.DocumentElement; + var currentReader = XmlReader.Create(currentPath, new XmlReaderSettings { Async = true }); + var incomingReader = XmlReader.Create(incomingPath, new XmlReaderSettings { Async = true }); + + var serializer = new XmlSerializer(typeof(Manifest)); - if (modLinksNode == null) + HashSet currentManifests = new(); + + while (await currentReader.ReadAsync()) { - await Console.Error.WriteLineAsync("Could not get document element of current XML file!"); - return 1; - } + if (currentReader.NodeType != XmlNodeType.Element) + continue; - var currentManifests = modLinksNode.ChildNodes; - - var incomingReader = XmlReader.Create(incomingPath, new XmlReaderSettings {Async = true}); + if (currentReader.Name != "Manifest") + continue; - var serializer = new XmlSerializer(typeof(Manifest)); + var currentManifest = (Manifest?)serializer.Deserialize(currentReader) ?? throw new InvalidDataException(); + currentManifests.Add(currentManifest); + } + List> checks = new(); - + while (await incomingReader.ReadAsync()) { if (incomingReader.NodeType != XmlNodeType.Element) @@ -107,37 +111,11 @@ internal static async Task Main(string[] args) if (incomingReader.Name != "Manifest") continue; - var incomingManifest = (Manifest?) serializer.Deserialize(incomingReader) ?? throw new InvalidDataException(); - - bool matching = false; - for (int i = 0; i < currentManifests.Count; i++) - { - var xmlNode = currentManifests[i] ?? throw new InvalidDataException(); - var stream = new MemoryStream(); - var writer = new StreamWriter(stream); - await writer.WriteAsync(xmlNode.OuterXml); - await writer.FlushAsync(); - - stream.Position = 0; - - var currentManifest = (Manifest?) serializer.Deserialize(stream) ?? throw new InvalidDataException(); - - if (currentManifest.Name == incomingManifest.Name && - currentManifest.Description == incomingManifest.Description && - currentManifest.Dependencies == null && incomingManifest.Dependencies == null || currentManifest.Dependencies != null && currentManifest.Dependencies.SequenceEqual(incomingManifest.Dependencies) && - currentManifest.Links.Equals(incomingManifest.Links)) - { - matching = true; - break; - } - } - - if (matching) - continue; - - checks.Add(CheckSingleSha(incomingManifest)); - } - + var incomingManifest = (Manifest?)serializer.Deserialize(incomingReader) ?? throw new InvalidDataException(); + if (currentManifests.All(m => m.GetHashCode() != incomingManifest.GetHashCode())) + checks.Add(CheckSingleSha(incomingManifest)); + } + var res = await Task.WhenAll(checks); sw.Stop(); @@ -151,4 +129,4 @@ internal static async Task Main(string[] args) private static void WriteError(string title, string message) => Console.WriteLine($"::error title={title}::{message}"); } -} +} \ No newline at end of file From 3c1ba7093022dbb0a1570c55a949d940bd5dd2ac Mon Sep 17 00:00:00 2001 From: Jason Ngo Date: Wed, 30 Apr 2025 21:23:49 -0400 Subject: [PATCH 07/10] Annoying extra space --- ModLinks.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ModLinks.cs b/ModLinks.cs index d16f763..0652735 100644 --- a/ModLinks.cs +++ b/ModLinks.cs @@ -93,7 +93,7 @@ public record Link public string SHA256 = null!; [XmlText] - public string URL = null!; + public string URL = null!; public override string ToString() { From 46d2d96ca033b76eb1a7b770b60260cb92d85d48 Mon Sep 17 00:00:00 2001 From: Jason Ngo Date: Fri, 2 May 2025 10:06:11 -0400 Subject: [PATCH 08/10] - Trim manifest URLs for correct equality checking - Remove Description and Dependencies fields from manifest deserialization --- ModLinks.cs | 13 ++----------- Program.cs | 33 ++++++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/ModLinks.cs b/ModLinks.cs index 0652735..9e03114 100644 --- a/ModLinks.cs +++ b/ModLinks.cs @@ -38,32 +38,23 @@ public Links Links set => _links = value; } - [XmlArray("Dependencies")] - [XmlArrayItem("Dependency")] - public string[]? Dependencies { get; set; } - - public string Description { get; set; } = null!; - public override string ToString() { return "{\n" + $"\t{nameof(Name)}: {Name},\n" + $"\t{nameof(Links)}: {(object?) _link ?? Links},\n" - + $"\t{nameof(Dependencies)}: {string.Join(", ", Dependencies ?? Array.Empty())},\n" - + $"\t{nameof(Description)}: {Description}\n" + "}"; } public override int GetHashCode() { - var hashCode = HashCode.Combine(Name, Description, Links); - return Dependencies?.Aggregate(hashCode, HashCode.Combine) ?? hashCode; + return HashCode.Combine(Name, Links); } } public record Links { - public Link? Windows; + public Link Windows = null!; public Link? Mac; public Link? Linux; diff --git a/Program.cs b/Program.cs index 1fc1d92..fb7b867 100755 --- a/Program.cs +++ b/Program.cs @@ -53,6 +53,14 @@ private static async Task CheckSingleSha(Manifest m) return res.All(x => x); } + private static void TrimManifest(ref Manifest m) + { + foreach (var link in m.Links.AsEnumerable()) + { + link.URL = link.URL.Trim(); + } + } + internal static async Task Main(string[] args) { var sw = new Stopwatch(); @@ -86,7 +94,7 @@ await Console.Error.WriteLineAsync( var serializer = new XmlSerializer(typeof(Manifest)); - HashSet currentManifests = new(); + Dictionary checkedLinks = new(); while (await currentReader.ReadAsync()) { @@ -97,8 +105,8 @@ await Console.Error.WriteLineAsync( continue; var currentManifest = (Manifest?)serializer.Deserialize(currentReader) ?? throw new InvalidDataException(); - - currentManifests.Add(currentManifest); + TrimManifest(ref currentManifest); + checkedLinks.Add(currentManifest.Name, currentManifest.Links); } List> checks = new(); @@ -112,15 +120,26 @@ await Console.Error.WriteLineAsync( continue; var incomingManifest = (Manifest?)serializer.Deserialize(incomingReader) ?? throw new InvalidDataException(); - if (currentManifests.All(m => m.GetHashCode() != incomingManifest.GetHashCode())) + TrimManifest(ref incomingManifest); + + if (checkedLinks.ContainsKey(incomingManifest.Name)) + { + if (checkedLinks[incomingManifest.Name] != incomingManifest.Links) + { + checks.Add(CheckSingleSha(incomingManifest)); + } + } + else + { checks.Add(CheckSingleSha(incomingManifest)); - } - + } + } + var res = await Task.WhenAll(checks); sw.Stop(); - Console.WriteLine($"Completed in {sw.ElapsedMilliseconds}ms."); + Console.WriteLine($"Checked {checks.Count} mods in {sw.ElapsedMilliseconds}ms."); // If they're not all correct, error. return !res.All(x => x) ? 1 : 0; From 8aa830d84cec88f1a2fc54845de19d5f93281d7b Mon Sep 17 00:00:00 2001 From: Jason Ngo Date: Sat, 3 May 2025 00:14:05 -0400 Subject: [PATCH 09/10] Use TryGetValue --- Program.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Program.cs b/Program.cs index fb7b867..94cd95b 100755 --- a/Program.cs +++ b/Program.cs @@ -94,7 +94,7 @@ await Console.Error.WriteLineAsync( var serializer = new XmlSerializer(typeof(Manifest)); - Dictionary checkedLinks = new(); + Dictionary checkedLinksDict = new(); while (await currentReader.ReadAsync()) { @@ -106,7 +106,7 @@ await Console.Error.WriteLineAsync( var currentManifest = (Manifest?)serializer.Deserialize(currentReader) ?? throw new InvalidDataException(); TrimManifest(ref currentManifest); - checkedLinks.Add(currentManifest.Name, currentManifest.Links); + checkedLinksDict.Add(currentManifest.Name, currentManifest.Links); } List> checks = new(); @@ -122,9 +122,9 @@ await Console.Error.WriteLineAsync( var incomingManifest = (Manifest?)serializer.Deserialize(incomingReader) ?? throw new InvalidDataException(); TrimManifest(ref incomingManifest); - if (checkedLinks.ContainsKey(incomingManifest.Name)) + if (checkedLinksDict.TryGetValue(incomingManifest.Name, out var checkedLinks)) { - if (checkedLinks[incomingManifest.Name] != incomingManifest.Links) + if (checkedLinks != incomingManifest.Links) { checks.Add(CheckSingleSha(incomingManifest)); } From 78e6cd0ab787b879aceb9b423dc8825ac1a82187 Mon Sep 17 00:00:00 2001 From: Jason Ngo Date: Sat, 3 May 2025 00:48:26 -0400 Subject: [PATCH 10/10] Custom deserializer for Link - Use fewer strings - Remove unused GetHashCode function - Fix functionality for ApiLinks.xml (with missing Name property) --- ModLinks.cs | 24 +++++++++++++++++------- Program.cs | 19 +++++-------------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/ModLinks.cs b/ModLinks.cs index 9e03114..406cccb 100644 --- a/ModLinks.cs +++ b/ModLinks.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Xml; +using System.Xml.Schema; using System.Xml.Serialization; namespace ModlinksShaVerifier; @@ -19,7 +21,7 @@ public record Manifest private Links? _links; private Link? _link; - public string Name { get; set; } = null!; + public string? Name { get; set; } [XmlElement] public Link? Link @@ -45,11 +47,6 @@ public override string ToString() + $"\t{nameof(Links)}: {(object?) _link ?? Links},\n" + "}"; } - - public override int GetHashCode() - { - return HashCode.Combine(Name, Links); - } } public record Links @@ -78,7 +75,7 @@ public IEnumerable AsEnumerable() } } -public record Link +public record Link : IXmlSerializable { [XmlAttribute] public string SHA256 = null!; @@ -90,6 +87,19 @@ public override string ToString() { return $"[Link: {nameof(SHA256)} = {SHA256}, {nameof(URL)}: {URL}]"; } + + public XmlSchema? GetSchema() => null; + + public void ReadXml(XmlReader reader) + { + var sha256 = reader.GetAttribute(nameof(SHA256)); + SHA256 = sha256 ?? throw new InvalidDataException("SHA256 attribute not found"); + URL = reader.ReadElementContentAsString().Trim(); + } + + public void WriteXml(XmlWriter writer) + { + } } [Serializable] diff --git a/Program.cs b/Program.cs index 94cd95b..059b4b5 100755 --- a/Program.cs +++ b/Program.cs @@ -53,14 +53,6 @@ private static async Task CheckSingleSha(Manifest m) return res.All(x => x); } - private static void TrimManifest(ref Manifest m) - { - foreach (var link in m.Links.AsEnumerable()) - { - link.URL = link.URL.Trim(); - } - } - internal static async Task Main(string[] args) { var sw = new Stopwatch(); @@ -101,11 +93,11 @@ await Console.Error.WriteLineAsync( if (currentReader.NodeType != XmlNodeType.Element) continue; - if (currentReader.Name != "Manifest") + if (currentReader.Name != nameof(Manifest)) continue; var currentManifest = (Manifest?)serializer.Deserialize(currentReader) ?? throw new InvalidDataException(); - TrimManifest(ref currentManifest); + currentManifest.Name ??= nameof(ApiLinks); checkedLinksDict.Add(currentManifest.Name, currentManifest.Links); } @@ -116,12 +108,11 @@ await Console.Error.WriteLineAsync( if (incomingReader.NodeType != XmlNodeType.Element) continue; - if (incomingReader.Name != "Manifest") + if (incomingReader.Name != nameof(Manifest)) continue; var incomingManifest = (Manifest?)serializer.Deserialize(incomingReader) ?? throw new InvalidDataException(); - TrimManifest(ref incomingManifest); - + incomingManifest.Name ??= nameof(ApiLinks); if (checkedLinksDict.TryGetValue(incomingManifest.Name, out var checkedLinks)) { if (checkedLinks != incomingManifest.Links) @@ -139,7 +130,7 @@ await Console.Error.WriteLineAsync( sw.Stop(); - Console.WriteLine($"Checked {checks.Count} mods in {sw.ElapsedMilliseconds}ms."); + Console.WriteLine($"Checked {checks.Count} manifests in {sw.ElapsedMilliseconds}ms."); // If they're not all correct, error. return !res.All(x => x) ? 1 : 0;