From 84683416b55c9d234ca51033dc9facf1f8dc673f Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Sat, 8 Nov 2025 01:15:04 -0500 Subject: [PATCH 01/30] Attempt nested for real this time --- ProtectionScan/Features/MainFeature.cs | 145 +++++++++++++++++++++++-- 1 file changed, 138 insertions(+), 7 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index c4c206f4..f739a3e9 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.IO; +#if NETCOREAPP +#endif using BinaryObjectScanner; using SabreTools.CommandLine; using SabreTools.CommandLine.Inputs; @@ -32,19 +34,32 @@ internal sealed class MainFeature : Feature #if NETCOREAPP private const string _jsonName = "json"; internal readonly FlagInput JsonInput = new(_jsonName, ["-j", "--json"], "Output to json file"); + +#if NET6_0_OR_GREATER + private const string _nestedName = "nested"; + internal readonly FlagInput NestedInput = new(_nestedName, ["-n", "--nested"], "Output to nested json file"); +#endif #endif private const string _noArchivesName = "no-archives"; - internal readonly FlagInput NoArchivesInput = new(_noArchivesName, ["-na", "--no-archives"], "Disable scanning archives"); + + internal readonly FlagInput NoArchivesInput = + new(_noArchivesName, ["-na", "--no-archives"], "Disable scanning archives"); private const string _noContentsName = "no-contents"; - internal readonly FlagInput NoContentsInput = new(_noContentsName, ["-nc", "--no-contents"], "Disable scanning for content checks"); + + internal readonly FlagInput NoContentsInput = + new(_noContentsName, ["-nc", "--no-contents"], "Disable scanning for content checks"); private const string _noPathsName = "no-paths"; - internal readonly FlagInput NoPathsInput = new(_noPathsName, ["-np", "--no-paths"], "Disable scanning for path checks"); + + internal readonly FlagInput NoPathsInput = + new(_noPathsName, ["-np", "--no-paths"], "Disable scanning for path checks"); private const string _noSubdirsName = "no-subdirs"; - internal readonly FlagInput NoSubdirsInput = new(_noSubdirsName, ["-ns", "--no-subdirs"], "Disable scanning subdirectories"); + + internal readonly FlagInput NoSubdirsInput = + new(_noSubdirsName, ["-ns", "--no-subdirs"], "Disable scanning subdirectories"); #endregion @@ -63,6 +78,11 @@ internal sealed class MainFeature : Feature /// Enable JSON output /// public bool Json { get; private set; } + + /// + /// Enable nested JSON output + /// + public bool Nested { get; private set; } #endif public MainFeature() @@ -74,6 +94,9 @@ public MainFeature() Add(FileOnlyInput); #if NETCOREAPP Add(JsonInput); +#if NET6_0_OR_GREATER + Add(NestedInput); +#endif #endif Add(NoContentsInput); Add(NoArchivesInput); @@ -93,6 +116,9 @@ public override bool Execute() FileOnly = GetBoolean(_fileOnlyName); #if NETCOREAPP Json = GetBoolean(_jsonName); +#if NET6_0_OR_GREATER + Nested = GetBoolean(_nestedName); +#endif #endif // Create scanner for all paths @@ -156,13 +182,18 @@ private void GetAndWriteProtections(Scanner scanner, string path) #if NETCOREAPP if (Json) WriteProtectionResultJson(path, protections); +#if NET6_0_OR_GREATER + if (Nested) + WriteProtectionResultNestedJson(path, protections); +#endif #endif } catch (Exception ex) { try { - using var sw = new StreamWriter(File.OpenWrite($"exception-{DateTime.Now:yyyy-MM-dd_HHmmss.ffff}.txt")); + using var sw = + new StreamWriter(File.OpenWrite($"exception-{DateTime.Now:yyyy-MM-dd_HHmmss.ffff}.txt")); sw.WriteLine(ex); } catch @@ -194,7 +225,8 @@ private void WriteProtectionResults(string path, Dictionary } catch { - Console.WriteLine("Could not open protection log file for writing. Only a console log will be provided."); + Console.WriteLine( + "Could not open protection log file for writing. Only a console log will be provided."); FileOnly = false; } @@ -246,7 +278,8 @@ private void WriteProtectionResultJson(string path, Dictionary + /// Write the protection results from a single path to a nested json file, if possible + /// + /// File or directory path + /// Dictionary of protections found, if any + private void WriteProtectionResultNestedJson(string path, Dictionary> protections) + { + if (protections == null) + { + Console.WriteLine($"No protections found for {path}"); + return; + } + + try + { + // Attempt to open a protection file for writing + using var jsw = + new StreamWriter(File.OpenWrite($"protection-{DateTime.Now:yyyy-MM-dd_HHmmss.ffff}.json")); + + var x = new Dictionary(); + + // Sort the keys for consistent output + string[] keys = [.. protections.Keys]; + Array.Sort(keys); + + // Loop over all keys + foreach (string key in keys) + { + // Skip over files with no protection + var value = protections[key]; + if (value.Count == 0) + continue; + + // Sort the detected protections for consistent output + string[] fileProtections = [.. value]; + Array.Sort(fileProtections); + //foreach (var fileProtection in fileProtections) + + DeepInsert(ref x, key, fileProtections); + } + + // Create the output data + var jsonSerializerOptions = new System.Text.Json.JsonSerializerOptions { WriteIndented = true }; + string serializedData = System.Text.Json.JsonSerializer.Serialize(x, jsonSerializerOptions); + + // Write the output data + // TODO: this prints plus symbols wrong, probably some other things too + jsw.WriteLine(serializedData); + jsw.Flush(); + } + catch (Exception ex) + { + Console.WriteLine(Debug ? ex : "[Exception opening file, please try again]"); + Console.WriteLine(); + } + } + + public static void DeepInsert(ref Dictionary obj, string path, string[] value) + { + var current = obj; + var pathParts = path.Split(Path.DirectorySeparatorChar); + + for (int i = 0; i < pathParts.Length; i++) + { + var part = pathParts[i]; + if (i != (pathParts.Length - 1)) + { + if (!current.ContainsKey(part)) + { + var innerObject = new Dictionary(); + current[part] = innerObject; + current = innerObject; + } + else + { + var innerObject = current[part]; + if (innerObject.GetType() != typeof(Dictionary)) + { + current[part] = new Dictionary(); + current = current[part]; + current.Add("", innerObject); + } + else + { + current[part] = innerObject; + current = innerObject; + } + } + } + else + { + current.Add(part, value); + } + } + } +#endif #endif } } From bb986531e8d3911c767fc2852a1186844e72d545 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Sat, 8 Nov 2025 01:57:48 -0500 Subject: [PATCH 02/30] forgot to include handling the base path --- ProtectionScan/Features/MainFeature.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index f739a3e9..4073aefe 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -317,8 +317,9 @@ private void WriteProtectionResultNestedJson(string path, Dictionary(); - + var jsonDictionary = new Dictionary(); + var trimmedPath = path.TrimEnd(['\\', '/']); + // Sort the keys for consistent output string[] keys = [.. protections.Keys]; Array.Sort(keys); @@ -336,12 +337,16 @@ private void WriteProtectionResultNestedJson(string path, Dictionary obj, string path, string[] value) + public static void DeepInsert(ref Dictionary obj, string path, string[] value, string trimmedPath) { var current = obj; var pathParts = path.Split(Path.DirectorySeparatorChar); From d025150fa9960a5c4b4db189997a01d9462480f7 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Sat, 8 Nov 2025 11:39:27 -0500 Subject: [PATCH 03/30] Reverted unnecesssary changes --- ProtectionScan/Features/MainFeature.cs | 30 +++++++------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index 4073aefe..ebbe523b 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -#if NETCOREAPP -#endif using BinaryObjectScanner; using SabreTools.CommandLine; using SabreTools.CommandLine.Inputs; @@ -42,24 +40,16 @@ internal sealed class MainFeature : Feature #endif private const string _noArchivesName = "no-archives"; - - internal readonly FlagInput NoArchivesInput = - new(_noArchivesName, ["-na", "--no-archives"], "Disable scanning archives"); + internal readonly FlagInput NoArchivesInput = new(_noArchivesName, ["-na", "--no-archives"], "Disable scanning archives"); private const string _noContentsName = "no-contents"; - - internal readonly FlagInput NoContentsInput = - new(_noContentsName, ["-nc", "--no-contents"], "Disable scanning for content checks"); + internal readonly FlagInput NoContentsInput = new(_noContentsName, ["-nc", "--no-contents"], "Disable scanning for content checks"); private const string _noPathsName = "no-paths"; - - internal readonly FlagInput NoPathsInput = - new(_noPathsName, ["-np", "--no-paths"], "Disable scanning for path checks"); + internal readonly FlagInput NoPathsInput = new(_noPathsName, ["-np", "--no-paths"], "Disable scanning for path checks"); private const string _noSubdirsName = "no-subdirs"; - - internal readonly FlagInput NoSubdirsInput = - new(_noSubdirsName, ["-ns", "--no-subdirs"], "Disable scanning subdirectories"); + internal readonly FlagInput NoSubdirsInput = new(_noSubdirsName, ["-ns", "--no-subdirs"], "Disable scanning subdirectories"); #endregion @@ -192,8 +182,7 @@ private void GetAndWriteProtections(Scanner scanner, string path) { try { - using var sw = - new StreamWriter(File.OpenWrite($"exception-{DateTime.Now:yyyy-MM-dd_HHmmss.ffff}.txt")); + using var sw = new StreamWriter(File.OpenWrite($"exception-{DateTime.Now:yyyy-MM-dd_HHmmss.ffff}.txt")); sw.WriteLine(ex); } catch @@ -225,8 +214,7 @@ private void WriteProtectionResults(string path, Dictionary } catch { - Console.WriteLine( - "Could not open protection log file for writing. Only a console log will be provided."); + Console.WriteLine("Could not open protection log file for writing. Only a console log will be provided."); FileOnly = false; } @@ -278,8 +266,7 @@ private void WriteProtectionResultJson(string path, Dictionary(); var trimmedPath = path.TrimEnd(['\\', '/']); From ff39badbd077e30acbfa9be19bff29ffcdad37dc Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Sat, 8 Nov 2025 11:46:34 -0500 Subject: [PATCH 04/30] Remove unneeded net6.0 gating --- ProtectionScan/Features/MainFeature.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index ebbe523b..5178045b 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -33,10 +33,8 @@ internal sealed class MainFeature : Feature private const string _jsonName = "json"; internal readonly FlagInput JsonInput = new(_jsonName, ["-j", "--json"], "Output to json file"); -#if NET6_0_OR_GREATER private const string _nestedName = "nested"; internal readonly FlagInput NestedInput = new(_nestedName, ["-n", "--nested"], "Output to nested json file"); -#endif #endif private const string _noArchivesName = "no-archives"; @@ -84,9 +82,7 @@ public MainFeature() Add(FileOnlyInput); #if NETCOREAPP Add(JsonInput); -#if NET6_0_OR_GREATER Add(NestedInput); -#endif #endif Add(NoContentsInput); Add(NoArchivesInput); @@ -106,9 +102,7 @@ public override bool Execute() FileOnly = GetBoolean(_fileOnlyName); #if NETCOREAPP Json = GetBoolean(_jsonName); -#if NET6_0_OR_GREATER Nested = GetBoolean(_nestedName); -#endif #endif // Create scanner for all paths @@ -172,10 +166,8 @@ private void GetAndWriteProtections(Scanner scanner, string path) #if NETCOREAPP if (Json) WriteProtectionResultJson(path, protections); -#if NET6_0_OR_GREATER if (Nested) WriteProtectionResultNestedJson(path, protections); -#endif #endif } catch (Exception ex) @@ -284,7 +276,6 @@ private void WriteProtectionResultJson(string path, Dictionary /// Write the protection results from a single path to a nested json file, if possible /// @@ -384,7 +375,6 @@ public static void DeepInsert(ref Dictionary obj, string path, } } } -#endif #endif } } From d2cb73f99210c1f490e314376163753c64f69574 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Sat, 8 Nov 2025 12:16:31 -0500 Subject: [PATCH 05/30] Add comments --- ProtectionScan/Features/MainFeature.cs | 45 ++++++++++++++++++-------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index 5178045b..244a0ec3 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -294,7 +294,10 @@ private void WriteProtectionResultNestedJson(string path, Dictionary(); + // A nested dictionary is used in order to avoid complex and unnecessary custom serialization. + // A dictionary with a dynamic value is used so that it's not necessary to first parse entries into a + // traditional node system and then bubble up the entire chain creating non-dynamic dictionaries. + var nestedDictionary = new Dictionary(); var trimmedPath = path.TrimEnd(['\\', '/']); // Sort the keys for consistent output @@ -314,16 +317,17 @@ private void WriteProtectionResultNestedJson(string path, Dictionary obj, string path, string[] value, string trimmedPath) + /// + /// Inserts file protection dictionary entries into a nested dictionary based on path + /// + /// File or directory path + /// The "key" for the given protection entry, already trimmed of its base path + /// The scanned protection(s) for a given file + public static void DeepInsert(ref Dictionary nestedDictionary, string path, string[] protections) { - var current = obj; - var pathParts = path.Split(Path.DirectorySeparatorChar); + var current = nestedDictionary; + var pathParts = path.Split(Path.DirectorySeparatorChar); + // Traverses the nested dictionary until the "root" dictionary is reached. for (int i = 0; i < pathParts.Length; i++) { var part = pathParts[i]; if (i != (pathParts.Length - 1)) { - if (!current.ContainsKey(part)) + if (!current.ContainsKey(part)) // Inserts new subdictionaries if one doesn't already exist { var innerObject = new Dictionary(); current[part] = innerObject; current = innerObject; } - else + else // Traverses already existing subdictionaries { var innerObject = current[part]; + + // If i.e. a packer has protections detected on it, and then files within it also have + // detections of their own, the later traversal of the files within it will fail, as + // the subdictionary for that packer has already been set to . Since it's + // no longer dynamic after being assigned once, the existing value must be pulled, then the + // new subdictionary can be added, and then the existing value can be re-added within the + // packer with a key of an empty string, in order to indicate it's for the packer itself, and + // to avoid potential future collisions. if (innerObject.GetType() != typeof(Dictionary)) { current[part] = new Dictionary(); @@ -369,9 +388,9 @@ public static void DeepInsert(ref Dictionary obj, string path, } } } - else + else // If the root dictionary has been reached, add the file and its protections. { - current.Add(part, value); + current.Add(part, protections); } } } From a9312326b5ea22a59b305441092b74ea2c7d4005 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Sat, 8 Nov 2025 12:25:51 -0500 Subject: [PATCH 06/30] Finish comments --- ProtectionScan/Features/MainFeature.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index 244a0ec3..927ec8a3 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -321,6 +321,11 @@ private void WriteProtectionResultNestedJson(string path, Dictionary nestedDictionary, var current = nestedDictionary; var pathParts = path.Split(Path.DirectorySeparatorChar); - // Traverses the nested dictionary until the "root" dictionary is reached. + // Traverses the nested dictionary until the "leaf" dictionary is reached. for (int i = 0; i < pathParts.Length; i++) { var part = pathParts[i]; @@ -388,7 +393,7 @@ public static void DeepInsert(ref Dictionary nestedDictionary, } } } - else // If the root dictionary has been reached, add the file and its protections. + else // If the "leaf" dictionary has been reached, add the file and its protections. { current.Add(part, protections); } From 5a46e1761bc43b831c1a0d86272d08d8ff3f8e4f Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Sat, 8 Nov 2025 12:57:54 -0500 Subject: [PATCH 07/30] Might as well safeguard if no protections are returned. --- ProtectionScan/Features/MainFeature.cs | 29 +++++++++++++++----------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index 927ec8a3..b6d44b7f 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -326,18 +326,23 @@ private void WriteProtectionResultNestedJson(string path, Dictionary Date: Sat, 8 Nov 2025 20:38:57 -0500 Subject: [PATCH 08/30] Use object instead of dynamic --- ProtectionScan/Features/MainFeature.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index b6d44b7f..3cd9190b 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -295,9 +295,9 @@ private void WriteProtectionResultNestedJson(string path, Dictionary(); + // A dictionary with an object value is used so that it's not necessary to first parse entries into a + // traditional node system and then bubble up the entire chain creating non-object dictionaries. + var nestedDictionary = new Dictionary(); var trimmedPath = path.TrimEnd(['\\', '/']); // Sort the keys for consistent output @@ -357,7 +357,7 @@ private void WriteProtectionResultNestedJson(string path, DictionaryFile or directory path /// The "key" for the given protection entry, already trimmed of its base path /// The scanned protection(s) for a given file - public static void DeepInsert(ref Dictionary nestedDictionary, string path, string[] protections) + public static void DeepInsert(ref Dictionary nestedDictionary, string path, string[] protections) { var current = nestedDictionary; var pathParts = path.Split(Path.DirectorySeparatorChar); @@ -370,7 +370,7 @@ public static void DeepInsert(ref Dictionary nestedDictionary, { if (!current.ContainsKey(part)) // Inserts new subdictionaries if one doesn't already exist { - var innerObject = new Dictionary(); + var innerObject = new Dictionary(); current[part] = innerObject; current = innerObject; } @@ -381,20 +381,20 @@ public static void DeepInsert(ref Dictionary nestedDictionary, // If i.e. a packer has protections detected on it, and then files within it also have // detections of their own, the later traversal of the files within it will fail, as // the subdictionary for that packer has already been set to . Since it's - // no longer dynamic after being assigned once, the existing value must be pulled, then the + // no longer object after being assigned once, the existing value must be pulled, then the // new subdictionary can be added, and then the existing value can be re-added within the // packer with a key of an empty string, in order to indicate it's for the packer itself, and // to avoid potential future collisions. if (innerObject.GetType() != typeof(Dictionary)) { current[part] = new Dictionary(); - current = current[part]; + current = (Dictionary)current[part]; current.Add("", innerObject); } else { current[part] = innerObject; - current = innerObject; + current = (Dictionary)innerObject; } } } From cc1503e8184a5bc8670ce512ad2980cca408ecc7 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Sat, 8 Nov 2025 20:41:22 -0500 Subject: [PATCH 09/30] Remove weird empty string root node handling --- ProtectionScan/Features/MainFeature.cs | 29 +++++++++++++------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index 3cd9190b..d0eb1fc7 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -328,21 +328,21 @@ private void WriteProtectionResultNestedJson(string path, Dictionary>() { - var tempValue = nestedDictionary[""]; - nestedDictionary.Remove(""); - nestedDictionary.Add(trimmedPath, tempValue); - - // Create the output data - var jsonSerializerOptions = new System.Text.Json.JsonSerializerOptions { WriteIndented = true }; - string serializedData = System.Text.Json.JsonSerializer.Serialize(nestedDictionary, jsonSerializerOptions); - - // Write the output data - // TODO: this prints plus symbols wrong, probably some other things too - jsw.WriteLine(serializedData); - jsw.Flush(); - } + {trimmedPath, nestedDictionary} + }; + + // Create the output data + var jsonSerializerOptions = new System.Text.Json.JsonSerializerOptions { WriteIndented = true }; + string serializedData = System.Text.Json.JsonSerializer.Serialize(finalDictionary, jsonSerializerOptions); + + // Write the output data + // TODO: this prints plus symbols wrong, probably some other things too + jsw.WriteLine(serializedData); + jsw.Flush(); + } catch (Exception ex) { @@ -360,6 +360,7 @@ private void WriteProtectionResultNestedJson(string path, Dictionary nestedDictionary, string path, string[] protections) { var current = nestedDictionary; + path = path.TrimStart(Path.DirectorySeparatorChar); var pathParts = path.Split(Path.DirectorySeparatorChar); // Traverses the nested dictionary until the "leaf" dictionary is reached. From 502fbac071e876e9e0c88e7b3be0f7f8b84d98a3 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Sat, 8 Nov 2025 20:48:33 -0500 Subject: [PATCH 10/30] remove uneeded ref --- ProtectionScan/Features/MainFeature.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index d0eb1fc7..e393d283 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -318,7 +318,7 @@ private void WriteProtectionResultNestedJson(string path, DictionaryFile or directory path /// The "key" for the given protection entry, already trimmed of its base path /// The scanned protection(s) for a given file - public static void DeepInsert(ref Dictionary nestedDictionary, string path, string[] protections) + public static void DeepInsert(Dictionary nestedDictionary, string path, string[] protections) { var current = nestedDictionary; path = path.TrimStart(Path.DirectorySeparatorChar); From f536fc4b6b876684a9b84c2b3822d5dc4298ce06 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Sat, 8 Nov 2025 20:50:53 -0500 Subject: [PATCH 11/30] Modify comment accordingly --- ProtectionScan/Features/MainFeature.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index e393d283..346e6528 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -381,11 +381,10 @@ public static void DeepInsert(Dictionary nestedDictionary, strin // If i.e. a packer has protections detected on it, and then files within it also have // detections of their own, the later traversal of the files within it will fail, as - // the subdictionary for that packer has already been set to . Since it's - // no longer object after being assigned once, the existing value must be pulled, then the - // new subdictionary can be added, and then the existing value can be re-added within the - // packer with a key of an empty string, in order to indicate it's for the packer itself, and - // to avoid potential future collisions. + // the subdictionary for that packer has already been set to . The existing + // value must be pulled, then the new subdictionary can be added, and then the existing value + // can be re-added within the packer with a key of an empty string, in order to indicate it's + // for the packer itself, and to avoid potential future collisions. if (innerObject.GetType() != typeof(Dictionary)) { current[part] = new Dictionary(); From 8d8f3df714ff97a3023e5e04f51c141f1af24d81 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Sat, 8 Nov 2025 21:02:58 -0500 Subject: [PATCH 12/30] Merge regular and nested json writing --- ProtectionScan/Features/MainFeature.cs | 121 +++++++++---------------- 1 file changed, 43 insertions(+), 78 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index 346e6528..bc6fa95b 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -34,7 +34,7 @@ internal sealed class MainFeature : Feature internal readonly FlagInput JsonInput = new(_jsonName, ["-j", "--json"], "Output to json file"); private const string _nestedName = "nested"; - internal readonly FlagInput NestedInput = new(_nestedName, ["-n", "--nested"], "Output to nested json file"); + internal readonly FlagInput NestedInput = new(_nestedName, ["-n", "--nested"], "Output to nested json file if json is already enabled"); #endif private const string _noArchivesName = "no-archives"; @@ -166,8 +166,6 @@ private void GetAndWriteProtections(Scanner scanner, string path) #if NETCOREAPP if (Json) WriteProtectionResultJson(path, protections); - if (Nested) - WriteProtectionResultNestedJson(path, protections); #endif } catch (Exception ex) @@ -260,89 +258,56 @@ private void WriteProtectionResultJson(string path, Dictionary - /// Write the protection results from a single path to a nested json file, if possible - /// - /// File or directory path - /// Dictionary of protections found, if any - private void WriteProtectionResultNestedJson(string path, Dictionary> protections) - { - if (protections == null) - { - Console.WriteLine($"No protections found for {path}"); - return; - } - - try - { - // Attempt to open a protection file for writing - using var jsw = new StreamWriter(File.OpenWrite($"protection-{DateTime.Now:yyyy-MM-dd_HHmmss.ffff}.json")); - - // A nested dictionary is used in order to avoid complex and unnecessary custom serialization. - // A dictionary with an object value is used so that it's not necessary to first parse entries into a - // traditional node system and then bubble up the entire chain creating non-object dictionaries. - var nestedDictionary = new Dictionary(); - var trimmedPath = path.TrimEnd(['\\', '/']); - - // Sort the keys for consistent output - string[] keys = [.. protections.Keys]; - Array.Sort(keys); - - // Loop over all keys - foreach (string key in keys) + string serializedData; + if (Nested) { - // Skip over files with no protection - var value = protections[key]; - if (value.Count == 0) - continue; - - // Sort the detected protections for consistent output - string[] fileProtections = [.. value]; - Array.Sort(fileProtections); - //foreach (var fileProtection in fileProtections) - - // Inserts key and protections into nested dictionary, with the key trimmed of the base path. - DeepInsert(nestedDictionary, key.Substring(trimmedPath.Length), fileProtections); - } + // A nested dictionary is used in order to avoid complex and unnecessary custom serialization. + // A dictionary with an object value is used so that it's not necessary to first parse entries into a + // traditional node system and then bubble up the entire chain creating non-object dictionaries. + var nestedDictionary = new Dictionary(); + var trimmedPath = path.TrimEnd(['\\', '/']); + + // Sort the keys for consistent output + string[] keys = [.. protections.Keys]; + Array.Sort(keys); + + // Loop over all keys + foreach (string key in keys) + { + // Skip over files with no protection + var value = protections[key]; + if (value.Count == 0) + continue; + + // Sort the detected protections for consistent output + string[] fileProtections = [.. value]; + Array.Sort(fileProtections); + //foreach (var fileProtection in fileProtections) + + // Inserts key and protections into nested dictionary, with the key trimmed of the base path. + DeepInsert(nestedDictionary, key.Substring(trimmedPath.Length), fileProtections); + } - // While it's possible to hardcode the root dictionary key to be changed to the base path beforehand, - // it's cleaner to avoid trying to circumvent the path splitting logic, and just move the root - // dictionary value into an entry with the base path as the key. - // There is no input as far as has been tested that can result in there not being a root dictionary key - // of an empty string, so this is safe. - // The only exception is if absolutely no protections were returned whatsoever, which is why there's a - // safeguard here at all - - var finalDictionary = new Dictionary>() + // Move nested dictionary into final dictionary with the base path as a key. + var finalDictionary = new Dictionary>() + { + {trimmedPath, nestedDictionary} + }; + + // Create the output data + serializedData = System.Text.Json.JsonSerializer.Serialize(finalDictionary, jsonSerializerOptions); + } + else { - {trimmedPath, nestedDictionary} - }; - - // Create the output data - var jsonSerializerOptions = new System.Text.Json.JsonSerializerOptions { WriteIndented = true }; - string serializedData = System.Text.Json.JsonSerializer.Serialize(finalDictionary, jsonSerializerOptions); + // Create the output data + serializedData = System.Text.Json.JsonSerializer.Serialize(protections, jsonSerializerOptions); + } // Write the output data // TODO: this prints plus symbols wrong, probably some other things too jsw.WriteLine(serializedData); - jsw.Flush(); - + jsw.Flush(); } catch (Exception ex) { From 2f2472d7f2445838e92306ef384b694235beed13 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Sat, 8 Nov 2025 23:06:37 -0500 Subject: [PATCH 13/30] Simplify object value checking --- ProtectionScan/Features/MainFeature.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index bc6fa95b..4048182d 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -350,7 +350,7 @@ public static void DeepInsert(Dictionary nestedDictionary, strin // value must be pulled, then the new subdictionary can be added, and then the existing value // can be re-added within the packer with a key of an empty string, in order to indicate it's // for the packer itself, and to avoid potential future collisions. - if (innerObject.GetType() != typeof(Dictionary)) + if (innerObject is string[]) { current[part] = new Dictionary(); current = (Dictionary)current[part]; From 4a4f3fed01d519bbbd1949ee5c0c0da0cf5fd0fc Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest <50224630+HeroponRikiBestest@users.noreply.github.com> Date: Sun, 9 Nov 2025 20:51:29 -0500 Subject: [PATCH 14/30] change flag handling Co-authored-by: Matt Nadareski --- ProtectionScan/Features/MainFeature.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index 4048182d..1017d03a 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -81,8 +81,8 @@ public MainFeature() Add(DebugInput); Add(FileOnlyInput); #if NETCOREAPP + JsonInput.Add(NestedInput); Add(JsonInput); - Add(NestedInput); #endif Add(NoContentsInput); Add(NoArchivesInput); From c095b7eef072021e057783c8a5f9c902e6d0a6c9 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Sun, 9 Nov 2025 20:54:59 -0500 Subject: [PATCH 15/30] Initial fixes --- ProtectionScan/Features/MainFeature.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index 1017d03a..5276a0e4 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -34,7 +34,7 @@ internal sealed class MainFeature : Feature internal readonly FlagInput JsonInput = new(_jsonName, ["-j", "--json"], "Output to json file"); private const string _nestedName = "nested"; - internal readonly FlagInput NestedInput = new(_nestedName, ["-n", "--nested"], "Output to nested json file if json is already enabled"); + internal readonly FlagInput NestedInput = new(_nestedName, ["-n", "--nested"], "Output to nested json file"); #endif private const string _noArchivesName = "no-archives"; @@ -262,9 +262,7 @@ private void WriteProtectionResultJson(string path, Dictionary(); var trimmedPath = path.TrimEnd(['\\', '/']); @@ -283,7 +281,6 @@ private void WriteProtectionResultJson(string path, Dictionary nestedDictionary, strin { var innerObject = current[part]; - // If i.e. a packer has protections detected on it, and then files within it also have - // detections of their own, the later traversal of the files within it will fail, as - // the subdictionary for that packer has already been set to . The existing - // value must be pulled, then the new subdictionary can be added, and then the existing value - // can be re-added within the packer with a key of an empty string, in order to indicate it's - // for the packer itself, and to avoid potential future collisions. + // Handle instances where a protection was already assigned to the current node if (innerObject is string[]) { current[part] = new Dictionary(); From 8ffa9d6b5af14861e71c0ae9265da100a090782b Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Mon, 10 Nov 2025 07:26:08 -0500 Subject: [PATCH 16/30] Invert if-else to de-nest main logic --- ProtectionScan/Features/MainFeature.cs | 47 +++++++++++++------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index 5276a0e4..bd21a537 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -329,36 +329,35 @@ public static void DeepInsert(Dictionary nestedDictionary, strin for (int i = 0; i < pathParts.Length; i++) { var part = pathParts[i]; - if (i != (pathParts.Length - 1)) + if (i == (pathParts.Length - 1)) // If the "leaf" dictionary has been reached, add the file and its protections. { - if (!current.ContainsKey(part)) // Inserts new subdictionaries if one doesn't already exist + current.Add(part, protections); + continue; + } + + if (!current.ContainsKey(part)) // Inserts new subdictionaries if one doesn't already exist + { + var innerObject = new Dictionary(); + current[part] = innerObject; + current = innerObject; + } + else // Traverses already existing subdictionaries + { + var innerObject = current[part]; + + // Handle instances where a protection was already assigned to the current node + if (innerObject is string[]) { - var innerObject = new Dictionary(); - current[part] = innerObject; - current = innerObject; + current[part] = new Dictionary(); + current = (Dictionary)current[part]; + current.Add("", innerObject); } - else // Traverses already existing subdictionaries + else { - var innerObject = current[part]; - - // Handle instances where a protection was already assigned to the current node - if (innerObject is string[]) - { - current[part] = new Dictionary(); - current = (Dictionary)current[part]; - current.Add("", innerObject); - } - else - { - current[part] = innerObject; - current = (Dictionary)innerObject; - } + current[part] = innerObject; + current = (Dictionary)innerObject; } } - else // If the "leaf" dictionary has been reached, add the file and its protections. - { - current.Add(part, protections); - } } } #endif From 4860eda333f46726bf4ed345138acf303adf8b3b Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Mon, 10 Nov 2025 07:52:34 -0500 Subject: [PATCH 17/30] minor formatting fixes --- ProtectionScan/Features/MainFeature.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index bd21a537..c7c12d69 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -283,7 +283,7 @@ private void WriteProtectionResultJson(string path, DictionaryFile or directory path /// The "key" for the given protection entry, already trimmed of its base path /// The scanned protection(s) for a given file - public static void DeepInsert(Dictionary nestedDictionary, string path, string[] protections) + public static void InsertNode(Dictionary nestedDictionary, string path, string[] protections) { var current = nestedDictionary; path = path.TrimStart(Path.DirectorySeparatorChar); @@ -329,13 +329,16 @@ public static void DeepInsert(Dictionary nestedDictionary, strin for (int i = 0; i < pathParts.Length; i++) { var part = pathParts[i]; - if (i == (pathParts.Length - 1)) // If the "leaf" dictionary has been reached, add the file and its protections. + + // If the "leaf" dictionary has been reached, add the file and its protections. + if (i == (pathParts.Length - 1)) { current.Add(part, protections); continue; } - if (!current.ContainsKey(part)) // Inserts new subdictionaries if one doesn't already exist + // Inserts new subdictionaries if one doesn't already exist + if (!current.ContainsKey(part)) { var innerObject = new Dictionary(); current[part] = innerObject; From a07599781844016cec0c8e15c0b166893cd61f4d Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Mon, 10 Nov 2025 08:36:31 -0500 Subject: [PATCH 18/30] Improved Json output --- ProtectionScan/Features/MainFeature.cs | 50 ++++++++++++++++---------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index c7c12d69..81006c88 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -269,6 +269,8 @@ private void WriteProtectionResultJson(string path, Dictionary(); // Loop over all keys foreach (string key in keys) @@ -283,7 +285,27 @@ private void WriteProtectionResultJson(string path, Dictionary node = (Dictionary)modifyNodeList[i].Item1; + + // Copy the existing KVPs out so they won't be lost + var copyDictionary = new Dictionary((Dictionary)node[part]); + + //Redefine node that needs to be modified to a list of objects + node[part] = new List(); + List modifyNode = (List)node[part]; + + // Add the "root" protection + modifyNode.Add(nodeProtections); + + // Add all the subdirectories back + modifyNode.Add(copyDictionary); } // Move nested dictionary into final dictionary with the base path as a key. @@ -319,24 +341,17 @@ private void WriteProtectionResultJson(string path, DictionaryFile or directory path /// The "key" for the given protection entry, already trimmed of its base path /// The scanned protection(s) for a given file - public static void InsertNode(Dictionary nestedDictionary, string path, string[] protections) + public static void InsertNode(Dictionary nestedDictionary, string path, string[] protections, List<(object, string, string[])> modifyNodeList) { var current = nestedDictionary; path = path.TrimStart(Path.DirectorySeparatorChar); var pathParts = path.Split(Path.DirectorySeparatorChar); // Traverses the nested dictionary until the "leaf" dictionary is reached. - for (int i = 0; i < pathParts.Length; i++) + for (int i = 0; i < pathParts.Length - 1; i++) { var part = pathParts[i]; - // If the "leaf" dictionary has been reached, add the file and its protections. - if (i == (pathParts.Length - 1)) - { - current.Add(part, protections); - continue; - } - // Inserts new subdictionaries if one doesn't already exist if (!current.ContainsKey(part)) { @@ -351,17 +366,16 @@ public static void InsertNode(Dictionary nestedDictionary, strin // Handle instances where a protection was already assigned to the current node if (innerObject is string[]) { - current[part] = new Dictionary(); - current = (Dictionary)current[part]; - current.Add("", innerObject); - } - else - { - current[part] = innerObject; - current = (Dictionary)innerObject; + modifyNodeList.Add((current, part, (string[])innerObject)); + innerObject = new Dictionary(); } + + current[part] = innerObject; + current = (Dictionary)current[part]; } } + // If the "leaf" dictionary has been reached, add the file and its protections. + current.Add(pathParts[^1], protections); } #endif } From 6e73cdae2b332a4ecf95bc8b0d34f1141a5c318e Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Mon, 10 Nov 2025 08:38:49 -0500 Subject: [PATCH 19/30] Remove unnecessary comments --- ProtectionScan/Features/MainFeature.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index 81006c88..82200c8b 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -288,23 +288,16 @@ private void WriteProtectionResultJson(string path, Dictionary node = (Dictionary)modifyNodeList[i].Item1; - - // Copy the existing KVPs out so they won't be lost var copyDictionary = new Dictionary((Dictionary)node[part]); - - //Redefine node that needs to be modified to a list of objects node[part] = new List(); List modifyNode = (List)node[part]; - - // Add the "root" protection modifyNode.Add(nodeProtections); - - // Add all the subdirectories back modifyNode.Add(copyDictionary); } From 50214d6e12f631beede0d8d062eb79b78b10a70c Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Mon, 10 Nov 2025 08:43:01 -0500 Subject: [PATCH 20/30] That's just a string --- ProtectionScan/Features/MainFeature.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index 82200c8b..2583b599 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -291,7 +291,7 @@ private void WriteProtectionResultJson(string path, Dictionary node = (Dictionary)modifyNodeList[i].Item1; var copyDictionary = new Dictionary((Dictionary)node[part]); From e840bbc20799e62d75dec9ddba844031b6b1b9da Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Mon, 10 Nov 2025 09:06:13 -0500 Subject: [PATCH 21/30] Slight improvement --- ProtectionScan/Features/MainFeature.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index 2583b599..e433c9f7 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -357,9 +357,9 @@ public static void InsertNode(Dictionary nestedDictionary, strin var innerObject = current[part]; // Handle instances where a protection was already assigned to the current node - if (innerObject is string[]) + if (innerObject is string[] existingProtections) { - modifyNodeList.Add((current, part, (string[])innerObject)); + modifyNodeList.Add((current, part, existingProtections)); innerObject = new Dictionary(); } From 8810c7d5a7ee933a35e5cec09c0fad66ad6213c9 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Mon, 10 Nov 2025 09:09:30 -0500 Subject: [PATCH 22/30] Simplify casting --- ProtectionScan/Features/MainFeature.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index e433c9f7..3fbb9953 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -270,7 +270,7 @@ private void WriteProtectionResultJson(string path, Dictionary(); + var modifyNodeList = new List<(Dictionary, string, string[])>(); // Loop over all keys foreach (string key in keys) @@ -293,10 +293,9 @@ private void WriteProtectionResultJson(string path, Dictionary node = (Dictionary)modifyNodeList[i].Item1; - var copyDictionary = new Dictionary((Dictionary)node[part]); - node[part] = new List(); - List modifyNode = (List)node[part]; + var copyDictionary = new Dictionary((Dictionary)modifyNodeList[i].Item1[part]); + modifyNodeList[i].Item1[part] = new List(); + List modifyNode = (List)modifyNodeList[i].Item1[part]; modifyNode.Add(nodeProtections); modifyNode.Add(copyDictionary); } @@ -334,7 +333,7 @@ private void WriteProtectionResultJson(string path, DictionaryFile or directory path /// The "key" for the given protection entry, already trimmed of its base path /// The scanned protection(s) for a given file - public static void InsertNode(Dictionary nestedDictionary, string path, string[] protections, List<(object, string, string[])> modifyNodeList) + public static void InsertNode(Dictionary nestedDictionary, string path, string[] protections, List<(Dictionary, string, string[])> modifyNodeList) { var current = nestedDictionary; path = path.TrimStart(Path.DirectorySeparatorChar); From 66afd7d22c31108412381e9e8a9f03a9f118bac1 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Mon, 10 Nov 2025 09:15:05 -0500 Subject: [PATCH 23/30] attept further simplification --- ProtectionScan/Features/MainFeature.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index 3fbb9953..52fa2230 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -292,12 +292,14 @@ private void WriteProtectionResultJson(string path, Dictionary((Dictionary)modifyNodeList[i].Item1[part]); - modifyNodeList[i].Item1[part] = new List(); - List modifyNode = (List)modifyNodeList[i].Item1[part]; - modifyNode.Add(nodeProtections); - modifyNode.Add(copyDictionary); + var modifyNode = modifyNodeList[i].Item1[part]; + var copyDictionary = new Dictionary((Dictionary)modifyNode); + modifyNode = new List(); + if (modifyNode is List modifyThisNode) + { + modifyThisNode.Add(modifyNodeList[i].Item3); + modifyThisNode.Add(copyDictionary); + } } // Move nested dictionary into final dictionary with the base path as a key. From e220db37e7229188899069649d4b368b23093cb0 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Mon, 10 Nov 2025 09:15:38 -0500 Subject: [PATCH 24/30] Further --- ProtectionScan/Features/MainFeature.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index 52fa2230..059841e6 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -291,8 +291,7 @@ private void WriteProtectionResultJson(string path, Dictionary((Dictionary)modifyNode); modifyNode = new List(); if (modifyNode is List modifyThisNode) From 7adc53a9b08bf1c2c32dcf40533fb7b97e09e23d Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Mon, 10 Nov 2025 09:19:54 -0500 Subject: [PATCH 25/30] Reduce nesting using inversion and continue --- ProtectionScan/Features/MainFeature.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index 059841e6..698618d0 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -294,11 +294,9 @@ private void WriteProtectionResultJson(string path, Dictionary((Dictionary)modifyNode); modifyNode = new List(); - if (modifyNode is List modifyThisNode) - { - modifyThisNode.Add(modifyNodeList[i].Item3); - modifyThisNode.Add(copyDictionary); - } + if (modifyNode is not List modifyThisNode) continue; + modifyThisNode.Add(modifyNodeList[i].Item3); + modifyThisNode.Add(copyDictionary); } // Move nested dictionary into final dictionary with the base path as a key. From 9decc06ec79715f82aec202f9b2fb23908aa135b Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Mon, 10 Nov 2025 09:29:21 -0500 Subject: [PATCH 26/30] Further simplified logic --- ProtectionScan/Features/MainFeature.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index 698618d0..5c60eda8 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -291,10 +291,9 @@ private void WriteProtectionResultJson(string path, Dictionary((Dictionary)modifyNode); - modifyNode = new List(); - if (modifyNode is not List modifyThisNode) continue; + var copyDictionary = modifyNodeList[i].Item1[modifyNodeList[i].Item2]; + modifyNodeList[i].Item1[modifyNodeList[i].Item2] = new List(); + if (modifyNodeList[i].Item1[modifyNodeList[i].Item2] is not List modifyThisNode) continue; modifyThisNode.Add(modifyNodeList[i].Item3); modifyThisNode.Add(copyDictionary); } From e721e54e6d8baf22eae32ce6fb8ac1bed23cef49 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Mon, 10 Nov 2025 09:35:49 -0500 Subject: [PATCH 27/30] Replace my code with sabre's --- ProtectionScan/Features/MainFeature.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index 5c60eda8..c0603a49 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -292,10 +292,12 @@ private void WriteProtectionResultJson(string path, Dictionary(); - if (modifyNodeList[i].Item1[modifyNodeList[i].Item2] is not List modifyThisNode) continue; - modifyThisNode.Add(modifyNodeList[i].Item3); - modifyThisNode.Add(copyDictionary); + + var modifyNode = new List(); + modifyNode.Add(modifyNodeList[i].Item3); + modifyNode.Add(copyDictionary); + + modifyNodeList[i].Item1[modifyNodeList[i].Item2] = modifyNode; } // Move nested dictionary into final dictionary with the base path as a key. From 4863878204ae8a4b4b27f20e2d68507ffe3ac23f Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Mon, 10 Nov 2025 09:41:35 -0500 Subject: [PATCH 28/30] De-nest using continue --- ProtectionScan/Features/MainFeature.cs | 29 +++++++++++++------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index c0603a49..d0f24cc7 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -347,24 +347,23 @@ public static void InsertNode(Dictionary nestedDictionary, strin // Inserts new subdictionaries if one doesn't already exist if (!current.ContainsKey(part)) { - var innerObject = new Dictionary(); - current[part] = innerObject; - current = innerObject; + var innerDictionary = new Dictionary(); + current[part] = innerDictionary; + current = innerDictionary; + continue; } - else // Traverses already existing subdictionaries + + var innerObject = current[part]; + + // Handle instances where a protection was already assigned to the current node + if (innerObject is string[] existingProtections) { - var innerObject = current[part]; - - // Handle instances where a protection was already assigned to the current node - if (innerObject is string[] existingProtections) - { - modifyNodeList.Add((current, part, existingProtections)); - innerObject = new Dictionary(); - } - - current[part] = innerObject; - current = (Dictionary)current[part]; + modifyNodeList.Add((current, part, existingProtections)); + innerObject = new Dictionary(); } + + current[part] = innerObject; + current = (Dictionary)current[part]; } // If the "leaf" dictionary has been reached, add the file and its protections. current.Add(pathParts[^1], protections); From 801daa89abefa9dfdc1befbe421061f4ef9bbbef Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Mon, 10 Nov 2025 09:41:46 -0500 Subject: [PATCH 29/30] newline --- ProtectionScan/Features/MainFeature.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index d0f24cc7..ab7f7e70 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -365,6 +365,7 @@ public static void InsertNode(Dictionary nestedDictionary, strin current[part] = innerObject; current = (Dictionary)current[part]; } + // If the "leaf" dictionary has been reached, add the file and its protections. current.Add(pathParts[^1], protections); } From 04ca3044db92e3cc0ac1cfa222f6c9a6da462425 Mon Sep 17 00:00:00 2001 From: HeroponRikiBestest Date: Mon, 10 Nov 2025 09:46:40 -0500 Subject: [PATCH 30/30] Remove all instances where it can end in a directory seperator --- ProtectionScan/Features/MainFeature.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ProtectionScan/Features/MainFeature.cs b/ProtectionScan/Features/MainFeature.cs index ab7f7e70..7cee73d1 100644 --- a/ProtectionScan/Features/MainFeature.cs +++ b/ProtectionScan/Features/MainFeature.cs @@ -336,8 +336,7 @@ private void WriteProtectionResultJson(string path, Dictionary nestedDictionary, string path, string[] protections, List<(Dictionary, string, string[])> modifyNodeList) { var current = nestedDictionary; - path = path.TrimStart(Path.DirectorySeparatorChar); - var pathParts = path.Split(Path.DirectorySeparatorChar); + var pathParts = path.Split(Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries); // Traverses the nested dictionary until the "leaf" dictionary is reached. for (int i = 0; i < pathParts.Length - 1; i++)