diff --git a/AssemblyRemapper.sln b/AssemblyRemapper.sln
new file mode 100644
index 0000000..940c7c6
--- /dev/null
+++ b/AssemblyRemapper.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.9.34728.123
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssemblyRemapper", "AssemblyRemapper\AssemblyRemapper.csproj", "{84BF5F6E-9EEC-4B08-BE72-9F419A369124}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {84BF5F6E-9EEC-4B08-BE72-9F419A369124}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {84BF5F6E-9EEC-4B08-BE72-9F419A369124}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {84BF5F6E-9EEC-4B08-BE72-9F419A369124}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {84BF5F6E-9EEC-4B08-BE72-9F419A369124}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {C2C7C51D-6773-404D-B51D-BC279AC9B923}
+ EndGlobalSection
+EndGlobal
diff --git a/AssemblyRemapper/AssemblyRemapper.csproj b/AssemblyRemapper/AssemblyRemapper.csproj
new file mode 100644
index 0000000..b21798a
--- /dev/null
+++ b/AssemblyRemapper/AssemblyRemapper.csproj
@@ -0,0 +1,15 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
diff --git a/AssemblyRemapper/Commands/CommandProcessor.cs b/AssemblyRemapper/Commands/CommandProcessor.cs
new file mode 100644
index 0000000..bc424c6
--- /dev/null
+++ b/AssemblyRemapper/Commands/CommandProcessor.cs
@@ -0,0 +1,41 @@
+using AssemblyRemapper.Reflection;
+using AssemblyRemapper.Utils;
+
+namespace AssemblyRemapper.Commands
+{
+ internal class CommandProcessor
+ {
+ public CommandProcessor()
+ { }
+
+ public void CommandLoop()
+ {
+ ShowStartText();
+
+ while (true)
+ {
+ var input = Console.ReadLine();
+ ProcessCommand(input);
+ }
+ }
+
+ private void ProcessCommand(string command)
+ {
+ if (command == "remap" || command == "Remap")
+ {
+ var remapper = new Remapper();
+
+ remapper.InitializeRemap();
+ }
+ }
+
+ private void ShowStartText()
+ {
+ Logger.Log($"-----------------------------------------------------------------", ConsoleColor.Green);
+ Logger.Log($"Cj's Assembly Tool", ConsoleColor.Green);
+ Logger.Log($"Version 0.1.0", ConsoleColor.Green);
+ Logger.Log($"Available Commands: `remap` `help`", ConsoleColor.Green);
+ Logger.Log($"-----------------------------------------------------------------", ConsoleColor.Green);
+ }
+ }
+}
\ No newline at end of file
diff --git a/AssemblyRemapper/Models/AppSettingsModel.cs b/AssemblyRemapper/Models/AppSettingsModel.cs
new file mode 100644
index 0000000..f2044c8
--- /dev/null
+++ b/AssemblyRemapper/Models/AppSettingsModel.cs
@@ -0,0 +1,65 @@
+namespace AssemblyRemapper.Models;
+
+///
+/// Remap config
+///
+internal class AppSettings
+{
+ public bool Debug { get; set; }
+ public bool SilentMode { get; set; }
+
+ public bool ScoringMode { get; set; }
+ public bool Publicize { get; set; }
+ public bool Unseal { get; set; }
+
+ public string AssemblyPath { get; set; }
+ public string OutputPath { get; set; }
+
+ public HashSet Remaps { get; set; } = [];
+}
+
+///
+/// Object to store linq statements in inside of json to search and remap classes
+///
+internal class Remap
+{
+ public string NewTypeName { get; set; } = string.Empty;
+
+ public string OldTypeName { get; set; } = string.Empty;
+
+ public bool UseDirectRename { get; set; }
+
+ public RemapSearchParams SearchParams { get; set; } = new();
+}
+
+///
+/// Search filters to find types and remap them
+///
+internal class RemapSearchParams
+{
+ public bool? IsPublic { get; set; } = null;
+ public bool? IsAbstract { get; set; } = null;
+ public bool? IsInterface { get; set; } = null;
+ public bool? IsEnum { get; set; } = null;
+ public bool? IsNested { get; set; } = null;
+ public string? ParentName { get; set; } = null;
+ public bool? IsSealed { get; set; } = null;
+ public bool? HasAttribute { get; set; } = null;
+ public bool? IsDerived { get; set; } = null;
+ public string? BaseClassName { get; set; } = null;
+ public bool? IsGeneric { get; set; } = null;
+ public HashSet MethodNamesToMatch { get; set; } = [];
+ public HashSet MethodNamesToIgnore { get; set; } = [];
+
+ public HashSet FieldNamesToMatch { get; set; } = [];
+ public HashSet FieldNamesToIgnore { get; set; } = [];
+ public HashSet PropertyNamesToMatch { get; set; } = [];
+ public HashSet PropertyNamesToIgnore { get; set; } = [];
+
+ public HashSet NestedTypesToMatch { get; set; } = [];
+ public HashSet NestedTypesToIgnore { get; set; } = [];
+
+ public RemapSearchParams()
+ {
+ }
+}
\ No newline at end of file
diff --git a/AssemblyRemapper/Models/ELogLevel.cs b/AssemblyRemapper/Models/ELogLevel.cs
new file mode 100644
index 0000000..eb2b590
--- /dev/null
+++ b/AssemblyRemapper/Models/ELogLevel.cs
@@ -0,0 +1,10 @@
+namespace AssemblyRemapper.Models;
+
+internal enum ELogLevel
+{
+ None = 0,
+ Success = 1,
+ Warn = 2,
+ Error = 3,
+ Full = 4,
+}
\ No newline at end of file
diff --git a/AssemblyRemapper/Models/EMatchResult.cs b/AssemblyRemapper/Models/EMatchResult.cs
new file mode 100644
index 0000000..7fe4512
--- /dev/null
+++ b/AssemblyRemapper/Models/EMatchResult.cs
@@ -0,0 +1,9 @@
+namespace AssemblyRemapper.Models;
+
+internal enum EMatchResult
+{
+ NoMatch = -1,
+ Disabled = 0,
+ SearchedNoResult = 1,
+ Match = 2,
+}
\ No newline at end of file
diff --git a/AssemblyRemapper/Models/ScoringModel.cs b/AssemblyRemapper/Models/ScoringModel.cs
new file mode 100644
index 0000000..8f2d2c6
--- /dev/null
+++ b/AssemblyRemapper/Models/ScoringModel.cs
@@ -0,0 +1,43 @@
+using AssemblyRemapper.Reflection;
+using Mono.Cecil;
+
+namespace AssemblyRemapper.Models;
+
+internal class ScoringModel
+{
+ public int Score { get; set; } = 0;
+
+ public string ProposedNewName { get; set; }
+
+ public TypeDefinition Definition { get; set; }
+
+ public ScoringModel()
+ {
+ }
+}
+
+internal static class ScoringModelExtensions
+{
+ public static void AddModelToResult(this ScoringModel model)
+ {
+ try
+ {
+ if (Remapper.ScoringModels.TryGetValue(model.ProposedNewName, out HashSet modelHashset))
+ {
+ modelHashset.Add(model);
+ return;
+ }
+
+ var newHash = new HashSet
+ {
+ model
+ };
+
+ Remapper.ScoringModels.Add(model.ProposedNewName, newHash);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e.ToString());
+ }
+ }
+}
\ No newline at end of file
diff --git a/AssemblyRemapper/Program.cs b/AssemblyRemapper/Program.cs
new file mode 100644
index 0000000..e0d3bec
--- /dev/null
+++ b/AssemblyRemapper/Program.cs
@@ -0,0 +1,13 @@
+using AssemblyRemapper.Commands;
+
+namespace AssemblyRemapper;
+
+public static class Program
+{
+ public static void Main(string[] args)
+ {
+ var cmd = new CommandProcessor();
+
+ cmd.CommandLoop();
+ }
+}
\ No newline at end of file
diff --git a/AssemblyRemapper/Reflection/Remapper.cs b/AssemblyRemapper/Reflection/Remapper.cs
new file mode 100644
index 0000000..2cb55cb
--- /dev/null
+++ b/AssemblyRemapper/Reflection/Remapper.cs
@@ -0,0 +1,306 @@
+using AssemblyRemapper.Models;
+using AssemblyRemapper.Utils;
+using Mono.Cecil;
+
+namespace AssemblyRemapper.Reflection;
+
+internal class Remapper
+{
+ public static Dictionary> ScoringModels { get; set; } = [];
+
+ public void InitializeRemap()
+ {
+ // Make sure any previous results are cleared just incase
+ ScoringModels.Clear();
+
+ DisplayBasicModuleInformation();
+ StartRemap();
+ }
+
+ private void DisplayBasicModuleInformation()
+ {
+ Logger.Log($"Module contains {DataProvider.ModuleDefinition.Types.Count} Types");
+ Logger.Log($"Starting remap...");
+ Logger.Log($"Publicize: {DataProvider.AppSettings.Publicize}");
+ Logger.Log($"Unseal: {DataProvider.AppSettings.Unseal}");
+ }
+
+ private void StartRemap()
+ {
+ foreach (var remap in DataProvider.AppSettings.Remaps)
+ {
+ Logger.Log("-----------------------------------------------");
+ Logger.Log($"Trying to remap {remap.NewTypeName}...");
+
+ HandleMapping(remap);
+ }
+
+ ChooseBestMatches();
+
+ if (DataProvider.AppSettings.ScoringMode) { return; }
+
+ // Dont publicize and unseal until after the remapping so we can use those as search parameters
+ HandlePublicize();
+ HandleUnseal();
+
+ // We are done, write the assembly
+ WriteAssembly();
+ }
+
+ private void HandleMapping(Remap mapping)
+ {
+ string newName = mapping.NewTypeName;
+ string oldName = mapping?.OldTypeName ?? string.Empty;
+
+ bool useDirectRename = mapping.UseDirectRename;
+
+ foreach (var type in DataProvider.ModuleDefinition.Types)
+ {
+ // Handle Direct Remaps by strict naming first bypasses everything else
+ if (useDirectRename)
+ {
+ HandleByDirectName(oldName, newName, type);
+ continue;
+ }
+
+ ScoreType(type, mapping);
+ }
+ }
+
+ private void HandlePublicize()
+ {
+ if (!DataProvider.AppSettings.Publicize) { return; }
+
+ Logger.Log("Starting publicization...");
+
+ foreach (var type in DataProvider.ModuleDefinition.Types)
+ {
+ if (type.IsNotPublic) { type.IsPublic = true; }
+
+ // We only want to do methods and properties
+
+ if (type.HasMethods)
+ {
+ foreach (var method in type.Methods)
+ {
+ method.IsPublic = true;
+ }
+ }
+
+ if (type.HasProperties)
+ {
+ foreach (var property in type.Properties)
+ {
+ if (property.SetMethod != null)
+ {
+ property.SetMethod.IsPublic = true;
+ }
+
+ if (property.GetMethod != null)
+ {
+ property.GetMethod.IsPublic = true;
+ }
+ }
+ }
+ }
+ }
+
+ private void HandleUnseal()
+ {
+ if (!DataProvider.AppSettings.Unseal) { return; }
+
+ Logger.Log("Starting unseal...");
+
+ foreach (var type in DataProvider.ModuleDefinition.Types)
+ {
+ if (type.IsSealed) { type.IsSealed = false; }
+ }
+ }
+
+ private void HandleByDirectName(string oldName, string newName, TypeDefinition type)
+ {
+ if (type.Name != oldName) { return; }
+
+ Logger.Log($"Renaming directly...");
+
+ type.Name = newName;
+
+ RenameService.RenameAllFields(oldName, newName, DataProvider.ModuleDefinition.Types);
+ RenameService.RenameAllProperties(oldName, newName, DataProvider.ModuleDefinition.Types);
+
+ Logger.Log($"Renamed {oldName} to {newName}");
+ Logger.Log("-----------------------------------------------");
+ }
+
+ private void ScoreType(TypeDefinition type, Remap remap, string parentTypeName = "")
+ {
+ foreach (var nestedType in type.NestedTypes)
+ {
+ ScoreType(nestedType, remap, type.Name);
+ }
+
+ var score = new ScoringModel
+ {
+ Definition = type,
+ ProposedNewName = remap.NewTypeName,
+ };
+
+ if (type.MatchIsAbstract(remap.SearchParams, score) == EMatchResult.NoMatch)
+ {
+ return;
+ }
+ /*
+ if (type.MatchIsEnum(remap.SearchParams, score) == EMatchResult.NoMatch)
+ {
+ return;
+ }
+
+ if (type.MatchIsNested(remap.SearchParams, score) == EMatchResult.NoMatch)
+ {
+ return;
+ }
+
+ type.MatchIsSealed(remap.SearchParams, score);
+
+ if (type.MatchIsSealed(remap.SearchParams, score) == EMatchResult.NoMatch)
+ {
+ return;
+ }
+
+ if (type.MatchIsDerived(remap.SearchParams, score) == EMatchResult.NoMatch)
+ {
+ return;
+ }
+
+ if (type.MatchIsInterface(remap.SearchParams, score) == EMatchResult.NoMatch)
+ {
+ return;
+ }
+
+ if (type.MatchIsGeneric(remap.SearchParams, score) == EMatchResult.NoMatch)
+ {
+ return;
+ }
+
+ if (type.MatchIsPublic(remap.SearchParams, score) == EMatchResult.NoMatch)
+ {
+ return;
+ }
+
+ if (type.MatchHasAttribute(remap.SearchParams, score) == EMatchResult.NoMatch)
+ {
+ return;
+ }
+
+ if (type.MatchMethods(remap.SearchParams, score) == EMatchResult.NoMatch)
+ {
+ return;
+ }
+
+ if (type.MatchFields(remap.SearchParams, score) == EMatchResult.NoMatch)
+ {
+ return;
+ }
+
+ if (type.MatchProperties(remap.SearchParams, score) == EMatchResult.NoMatch)
+ {
+ return;
+ }
+
+ if (type.MatchNestedTypes(remap.SearchParams, score) == EMatchResult.NoMatch)
+ {
+ return;
+ }
+ */
+ ScoringModelExtensions.AddModelToResult(score);
+ }
+
+ private void ChooseBestMatches()
+ {
+ foreach (var remap in ScoringModels)
+ {
+ ChooseBestMatch(remap.Value, true);
+ }
+ }
+
+ private void ChooseBestMatch(HashSet scores, bool isBest = false)
+ {
+ if (ScoringModels.Count == 0)
+ {
+ return;
+ }
+
+ var highestScore = scores.OrderByDescending(model => model.Score).FirstOrDefault();
+ var secondScore = scores.OrderByDescending(model => model.Score).Skip(1).FirstOrDefault();
+
+ if (highestScore is null || secondScore is null) { return; }
+
+ var potentialText = isBest
+ ? "Best potential"
+ : "Next potential";
+
+ if (highestScore.Score <= 0) { return; }
+
+ Logger.Log("-----------------------------------------------");
+ Logger.Log($"Found {scores.Count} possible matches");
+ Logger.Log($"Scored: {highestScore.Score} points");
+ Logger.Log($"Next Best: {secondScore.Score} points");
+ Logger.Log($"{potentialText} match is `{highestScore.Definition.Name}` for `{highestScore.ProposedNewName}`");
+
+ if (DataProvider.AppSettings.ScoringMode)
+ {
+ Logger.Log("Show next result? (y/n)");
+ var answer = Console.ReadLine();
+
+ if (answer == "yes" || answer == "y")
+ {
+ scores.Remove(highestScore);
+ ChooseBestMatch(scores);
+ }
+
+ Logger.Log("-----------------------------------------------");
+ return;
+ }
+
+ var anwser = "";
+
+ if (!DataProvider.AppSettings.SilentMode)
+ {
+ Logger.Log($"Should we continue? (y/n)");
+ anwser = Console.ReadLine();
+ }
+
+ if (anwser == "yes" || anwser == "y" || DataProvider.AppSettings.SilentMode)
+ {
+ var oldName = highestScore.Definition.Name;
+
+ highestScore.Definition.Name = highestScore.ProposedNewName;
+
+ RenameService.RenameAllFields(oldName, highestScore.Definition.Name, DataProvider.ModuleDefinition.Types);
+ RenameService.RenameAllProperties(oldName, highestScore.Definition.Name, DataProvider.ModuleDefinition.Types);
+
+ Logger.Log($"Remapped {oldName} to `{highestScore.Definition.Name}`");
+ Logger.Log("-----------------------------------------------");
+ return;
+ }
+
+ scores.Remove(highestScore);
+ ChooseBestMatch(scores);
+ }
+
+ private void WriteAssembly()
+ {
+ var filename = Path.GetFileNameWithoutExtension(DataProvider.AppSettings.AssemblyPath);
+ var strippedPath = Path.GetDirectoryName(filename);
+
+ filename = $"{filename}-Remapped.dll";
+
+ var remappedPath = Path.Combine(strippedPath, filename);
+
+ DataProvider.AssemblyDefinition.Write(remappedPath);
+
+ Logger.Log("-----------------------------------------------");
+ Logger.Log($"Complete: Assembly written to `{remappedPath}`");
+ Logger.Log("-----------------------------------------------");
+ }
+}
\ No newline at end of file
diff --git a/AssemblyRemapper/Reflection/RenameService.cs b/AssemblyRemapper/Reflection/RenameService.cs
new file mode 100644
index 0000000..fe14896
--- /dev/null
+++ b/AssemblyRemapper/Reflection/RenameService.cs
@@ -0,0 +1,72 @@
+using AssemblyRemapper.Utils;
+using Mono.Collections.Generic;
+
+namespace AssemblyRemapper.Reflection;
+
+internal static class RenameService
+{
+ public static void RenameAllFields(
+ string oldName,
+ string newName,
+ Collection types)
+ {
+ foreach (var type in types)
+ {
+ int fieldCount = 0;
+
+ foreach (var field in type.Fields)
+ {
+ if (field.FieldType.ToString() == newName)
+ {
+ Logger.Log($"Renaming Field: `{field.Name}` on Type `{type}`");
+ field.Name = GetNewFieldName(newName, field.IsPrivate, fieldCount);
+ fieldCount++;
+ }
+ }
+
+ if (type.HasNestedTypes)
+ {
+ foreach (var _ in type.NestedTypes)
+ {
+ RenameAllFields(oldName, newName, type.NestedTypes);
+ }
+ }
+ }
+ }
+
+ public static void RenameAllProperties(
+ string oldName,
+ string newName,
+ Collection types)
+ {
+ foreach (var type in types)
+ {
+ int propertyCount = 0;
+
+ foreach (var property in type.Properties)
+ {
+ if (property.PropertyType.ToString() == newName)
+ {
+ Logger.Log($"Renaming Property: `{property.Name}` on Type `{type}`");
+ property.Name = propertyCount > 0 ? $"{newName}_{propertyCount}" : newName;
+ }
+ }
+
+ if (type.HasNestedTypes)
+ {
+ foreach (var _ in type.NestedTypes)
+ {
+ RenameAllProperties(oldName, newName, type.NestedTypes);
+ }
+ }
+ }
+ }
+
+ private static string GetNewFieldName(string TypeName, bool isPrivate, int fieldCount = 0)
+ {
+ var discard = isPrivate ? "_" : "";
+ string newFieldCount = fieldCount > 0 ? $"_{fieldCount}" : string.Empty;
+
+ return $"{discard}{char.ToLower(TypeName[0])}{TypeName[1..]}{newFieldCount}";
+ }
+}
\ No newline at end of file
diff --git a/AssemblyRemapper/Reflection/SearchProvider.cs b/AssemblyRemapper/Reflection/SearchProvider.cs
new file mode 100644
index 0000000..c8ce93b
--- /dev/null
+++ b/AssemblyRemapper/Reflection/SearchProvider.cs
@@ -0,0 +1,326 @@
+using AssemblyRemapper.Models;
+using AssemblyRemapper.Utils;
+using Mono.Cecil;
+using Mono.Cecil.Rocks;
+
+namespace AssemblyRemapper.Reflection;
+
+internal static class SearchProvider
+{
+ public static int MatchCount { get; private set; }
+
+ public static EMatchResult MatchIsAbstract(this TypeDefinition type, RemapSearchParams parms, ScoringModel score)
+ {
+ if (parms.IsAbstract is null)
+ {
+ return EMatchResult.Disabled;
+ }
+
+ // Interfaces cannot be abstract, and abstract cannot be static
+ if (type.IsInterface || type.GetStaticConstructor() != null)
+ {
+ Logger.Log($"Searching for an abstract type, skipping interface or static");
+ return EMatchResult.NoMatch;
+ }
+
+ if (type.IsAbstract != parms.IsAbstract)
+ {
+ score.Score += 1;
+ Logger.Log($"Matched `{type.Name}` on search `{score.ProposedNewName}` : IsAbstract");
+ return EMatchResult.Match;
+ }
+
+ Logger.Log($"Skipping `{type.Name}` on search `{score.ProposedNewName}` IsAbstract does not match.");
+ return EMatchResult.NoMatch;
+ }
+
+ public static EMatchResult MatchIsEnum(this TypeDefinition type, RemapSearchParams parms, ScoringModel score)
+ {
+ if (parms.IsEnum is null)
+ {
+ return EMatchResult.Disabled;
+ }
+
+ if (type.IsEnum == parms.IsEnum)
+ {
+ score.Score += 1;
+ Logger.Log($"Matched `{type.Name}` on search `{score.ProposedNewName}` : IsEnum");
+ return EMatchResult.Match;
+ }
+
+ Logger.Log($"Skipping `{type.Name}` on search `{score.ProposedNewName}` IsEnum does not match.");
+ return EMatchResult.NoMatch;
+ }
+
+ public static EMatchResult MatchIsNested(this TypeDefinition type, RemapSearchParams parms, ScoringModel score)
+ {
+ if (parms.IsNested is null)
+ {
+ return EMatchResult.Disabled;
+ }
+
+ if (type.IsNested == parms.IsNested)
+ {
+ score.Score += 1;
+ Logger.Log($"Matched `{type.Name}` on search `{score.ProposedNewName}` : IsNested");
+ return EMatchResult.Match;
+ }
+
+ Logger.Log($"Skipping `{type.Name}` on search `{score.ProposedNewName}` IsNested does not match.");
+ return EMatchResult.NoMatch;
+ }
+
+ public static EMatchResult MatchIsSealed(this TypeDefinition type, RemapSearchParams parms, ScoringModel score)
+ {
+ if (parms.IsSealed is null)
+ {
+ return EMatchResult.Disabled;
+ }
+
+ if (type.IsSealed == parms.IsSealed)
+ {
+ score.Score += 1;
+ Logger.Log($"Matched `{type.Name}` on search `{score.ProposedNewName}` : IsSealed");
+ return EMatchResult.Match;
+ }
+
+ Logger.Log($"Skipping `{type.Name}` on search `{score.ProposedNewName}` IsSealed does not match.");
+ return EMatchResult.NoMatch;
+ }
+
+ public static EMatchResult MatchIsDerived(this TypeDefinition type, RemapSearchParams parms, ScoringModel score)
+ {
+ if (parms.IsDerived is null)
+ {
+ return EMatchResult.Disabled;
+ }
+
+ if (type.BaseType != null && (bool)parms.IsDerived)
+ {
+ score.Score += 1;
+ Logger.Log($"Matched `{type.Name}` on search `{score.ProposedNewName}` : IsDerived");
+ return EMatchResult.Match;
+ }
+
+ Logger.Log($"Skipping `{type.Name}` on search `{score.ProposedNewName}` IsDerived does not match.");
+ return EMatchResult.NoMatch;
+ }
+
+ public static EMatchResult MatchIsInterface(this TypeDefinition type, RemapSearchParams parms, ScoringModel score)
+ {
+ if (parms.IsInterface is null)
+ {
+ return EMatchResult.Disabled;
+ }
+
+ // Interfaces cannot be a class
+ if (type.IsClass)
+ {
+ return EMatchResult.NoMatch;
+ }
+
+ if (type.IsInterface != parms.IsInterface)
+ {
+ score.Score += 1;
+ Logger.Log($"Matched `{type.Name}` on search `{score.ProposedNewName}` : IsInterface");
+ return EMatchResult.Match;
+ }
+
+ Logger.Log($"Skipping `{type.Name}` on search `{score.ProposedNewName}` IsInterface does not match.");
+ return EMatchResult.NoMatch;
+ }
+
+ public static EMatchResult MatchIsGeneric(this TypeDefinition type, RemapSearchParams parms, ScoringModel score)
+ {
+ if (parms.IsGeneric is null)
+ {
+ return EMatchResult.Disabled;
+ }
+
+ if (type.HasGenericParameters == parms.IsGeneric)
+ {
+ score.Score += 1;
+ Logger.Log($"Matched `{type.Name}` on search `{score.ProposedNewName}` : IsGeneric");
+ return EMatchResult.Match;
+ }
+
+ Logger.Log($"Skipping `{type.Name}` on search `{score.ProposedNewName}` IsGeneric does not match.");
+ return EMatchResult.NoMatch;
+ }
+
+ public static EMatchResult MatchIsPublic(this TypeDefinition type, RemapSearchParams parms, ScoringModel score)
+ {
+ if (parms.IsPublic is null)
+ {
+ return EMatchResult.Disabled;
+ }
+
+ if (type.IsPublic == parms.IsPublic)
+ {
+ score.Score += 1;
+ Logger.Log($"Matched `{type.Name}` on search `{score.ProposedNewName}` : IsPublic");
+ return EMatchResult.Match;
+ }
+
+ Logger.Log($"Skipping `{type.Name}` on search `{score.ProposedNewName}` IsPublic does not match.");
+ return EMatchResult.NoMatch;
+ }
+
+ public static EMatchResult MatchHasAttribute(this TypeDefinition type, RemapSearchParams parms, ScoringModel score)
+ {
+ if (parms.HasAttribute is null)
+ {
+ return EMatchResult.Disabled;
+ }
+
+ if (type.HasCustomAttributes == parms.HasAttribute)
+ {
+ score.Score += 1;
+ Logger.Log($"Matched `{type.Name}` on search `{score.ProposedNewName}` : HasAttribute");
+ return EMatchResult.Match;
+ }
+
+ Logger.Log($"Skipping `{type.Name}` on search `{score.ProposedNewName}` HasAttribute does not match.");
+ return EMatchResult.NoMatch;
+ }
+
+ public static EMatchResult MatchMethods(this TypeDefinition type, RemapSearchParams parms, ScoringModel score)
+ {
+ // Ignore types that dont have methods when we are looking for them, and ignore types that
+ // have methods while we're not looking for them
+ if ((type.HasMethods is true && parms.MethodNamesToMatch.Count == 0) || (type.HasMethods is false && parms.MethodNamesToMatch.Count > 0))
+ {
+ return EMatchResult.NoMatch;
+ }
+
+ // `*` is the wildcard to ignore all methods that exist on types
+ if (parms.MethodNamesToIgnore.Contains("*"))
+ {
+ return EMatchResult.NoMatch;
+ }
+
+ int matchCount = 0;
+
+ foreach (var method in type.Methods)
+ {
+ if (parms.MethodNamesToIgnore.Contains(method.Name))
+ {
+ // Type contains blacklisted method
+ return EMatchResult.NoMatch;
+ }
+
+ if (parms.MethodNamesToMatch.Contains(method.Name))
+ {
+ matchCount++;
+ score.Score += 2;
+ }
+ }
+
+ return matchCount > 0 ? EMatchResult.Match : EMatchResult.NoMatch;
+ }
+
+ public static EMatchResult MatchFields(this TypeDefinition type, RemapSearchParams parms, ScoringModel score)
+ {
+ // Ignore types that dont have fields when we are looking for them, and ignore types that
+ // have fields while we're not looking for them
+ if ((type.HasFields is true && parms.FieldNamesToMatch.Count == 0) || (type.HasFields is false && parms.FieldNamesToMatch.Count > 0))
+ {
+ return EMatchResult.NoMatch;
+ }
+
+ // `*` is the wildcard to ignore all fields that exist on types
+ if (parms.FieldNamesToIgnore.Contains("*"))
+ {
+ return EMatchResult.NoMatch;
+ }
+
+ int matchCount = 0;
+
+ foreach (var field in type.Fields)
+ {
+ if (parms.FieldNamesToIgnore.Contains(field.Name))
+ {
+ // Type contains blacklisted field
+ return EMatchResult.NoMatch;
+ }
+
+ if (parms.FieldNamesToMatch.Contains(field.Name))
+ {
+ matchCount++;
+ score.Score += 2;
+ }
+ }
+
+ return matchCount > 0 ? EMatchResult.Match : EMatchResult.NoMatch;
+ }
+
+ public static EMatchResult MatchProperties(this TypeDefinition type, RemapSearchParams parms, ScoringModel score)
+ {
+ // Ignore types that dont have properties when we are looking for them, and ignore types
+ // that have properties while we're not looking for them
+ if ((type.HasProperties is true && parms.PropertyNamesToMatch.Count == 0) || (type.HasProperties is false && parms.PropertyNamesToMatch.Count > 0))
+ {
+ return EMatchResult.NoMatch;
+ }
+
+ // `*` is the wildcard to ignore all properties that exist on types
+ if (parms.PropertyNamesToIgnore.Contains("*"))
+ {
+ return EMatchResult.NoMatch;
+ }
+
+ int matchCount = 0;
+
+ foreach (var property in type.Properties)
+ {
+ if (parms.PropertyNamesToIgnore.Contains(property.Name))
+ {
+ // Type contains blacklisted property
+ return EMatchResult.NoMatch;
+ }
+
+ if (parms.PropertyNamesToMatch.Contains(property.Name))
+ {
+ matchCount++;
+ score.Score += 2;
+ }
+ }
+
+ return matchCount > 0 ? EMatchResult.Match : EMatchResult.NoMatch;
+ }
+
+ public static EMatchResult MatchNestedTypes(this TypeDefinition type, RemapSearchParams parms, ScoringModel score)
+ {
+ // Ignore types that dont have nested types when we are looking for them, and ignore types
+ // that have nested types while we're not looking for them
+ if ((type.HasNestedTypes is true && parms.NestedTypesToMatch.Count == 0) || (type.HasNestedTypes is false && parms.NestedTypesToMatch.Count > 0))
+ {
+ return EMatchResult.NoMatch;
+ }
+
+ // `*` is the wildcard to ignore all nested types that exist on types
+ if (parms.PropertyNamesToIgnore.Contains("*"))
+ {
+ return EMatchResult.NoMatch;
+ }
+
+ int matchCount = 0;
+
+ foreach (var nestedType in type.NestedTypes)
+ {
+ if (parms.NestedTypesToIgnore.Contains(nestedType.Name))
+ {
+ // Type contains blacklisted nested type
+ return EMatchResult.NoMatch;
+ }
+
+ if (parms.NestedTypesToMatch.Contains(nestedType.Name))
+ {
+ matchCount++;
+ score.Score += 2;
+ }
+ }
+
+ return matchCount > 0 ? EMatchResult.Match : EMatchResult.NoMatch;
+ }
+}
\ No newline at end of file
diff --git a/AssemblyRemapper/Utils/DataProvider.cs b/AssemblyRemapper/Utils/DataProvider.cs
new file mode 100644
index 0000000..c3ac009
--- /dev/null
+++ b/AssemblyRemapper/Utils/DataProvider.cs
@@ -0,0 +1,65 @@
+using AssemblyRemapper.Models;
+using Mono.Cecil;
+using Newtonsoft.Json;
+
+namespace AssemblyRemapper.Utils;
+
+internal static class DataProvider
+{
+ static DataProvider()
+ {
+ LoadAppSettings();
+ LoadAssemblyDefinition();
+ }
+
+ public static AppSettings AppSettings { get; private set; }
+
+ public static AssemblyDefinition AssemblyDefinition { get; private set; }
+
+ public static ModuleDefinition ModuleDefinition { get; private set; }
+
+ private static void LoadAppSettings()
+ {
+ var settingsPath = Path.Combine(AppContext.BaseDirectory, "Data", "Settings.jsonc");
+
+ if (!File.Exists(settingsPath))
+ {
+ throw new InvalidOperationException($"path `{settingsPath}` does not exist...");
+ }
+
+ var jsonText = File.ReadAllText(settingsPath);
+
+ JsonSerializerSettings settings = new JsonSerializerSettings
+ {
+ NullValueHandling = NullValueHandling.Ignore
+ };
+
+ AppSettings = JsonConvert.DeserializeObject(jsonText, settings);
+ }
+
+ private static void LoadAssemblyDefinition()
+ {
+ DefaultAssemblyResolver resolver = new();
+ resolver.AddSearchDirectory(Path.GetDirectoryName(AppSettings.AssemblyPath)); // Replace with the correct path
+ ReaderParameters parameters = new() { AssemblyResolver = resolver };
+
+ AssemblyDefinition = AssemblyDefinition.ReadAssembly(AppSettings.AssemblyPath, parameters);
+
+ if (AssemblyDefinition is null)
+ {
+ throw new InvalidOperationException("AssemblyDefinition was null...");
+ }
+
+ var fileName = Path.GetFileName(AppSettings.AssemblyPath);
+
+ foreach (var module in AssemblyDefinition.Modules.ToArray())
+ {
+ if (module.Name == fileName)
+ {
+ ModuleDefinition = module;
+ }
+ }
+
+ Logger.Log($"Module `{fileName}` not found in assembly {fileName}");
+ }
+}
\ No newline at end of file
diff --git a/AssemblyRemapper/Utils/Logger.cs b/AssemblyRemapper/Utils/Logger.cs
new file mode 100644
index 0000000..9714f4d
--- /dev/null
+++ b/AssemblyRemapper/Utils/Logger.cs
@@ -0,0 +1,35 @@
+namespace AssemblyRemapper.Utils;
+
+internal static class Logger
+{
+ static Logger()
+ {
+ if (File.Exists(_logPath))
+ {
+ File.Delete(_logPath);
+ File.Create(_logPath).Close();
+ }
+ }
+
+ private static string _logPath = Path.Combine(AppContext.BaseDirectory, "Data", "Log.log");
+
+ public static void Log(string message, ConsoleColor color = ConsoleColor.Gray)
+ {
+ Console.ForegroundColor = color;
+ Console.WriteLine(message);
+ Console.ResetColor();
+
+ try
+ {
+ using (StreamWriter sw = File.AppendText(_logPath))
+ {
+ sw.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}");
+ }
+ }
+ catch (IOException ex)
+ {
+ // Handle potential file writing errors gracefully
+ Console.WriteLine($"Error logging: {ex.Message}");
+ }
+ }
+}
\ No newline at end of file