AssemblyTool/RecodeItLib/Remapper/ReCodeItRemapper.cs

351 lines
12 KiB
C#
Raw Normal View History

2024-06-14 19:06:21 -04:00
using Mono.Cecil;
using Mono.Cecil.Rocks;
using ReCodeIt.CrossCompiler;
2024-06-14 19:06:21 -04:00
using ReCodeIt.Enums;
using ReCodeIt.Models;
using ReCodeIt.ReMapper.Search;
using ReCodeIt.Utils;
2024-06-13 17:05:34 -04:00
using System.Diagnostics;
2024-06-11 19:18:48 -04:00
2024-06-14 19:06:21 -04:00
namespace ReCodeIt.ReMapper;
2024-06-11 19:18:48 -04:00
2024-06-14 19:06:21 -04:00
public class ReCodeItRemapper
2024-06-11 19:18:48 -04:00
{
2024-06-18 17:35:31 -04:00
public ReCodeItRemapper(ReCodeItCrossCompiler compiler)
{
_compiler = compiler;
}
public ReCodeItRemapper()
{ }
private readonly ReCodeItCrossCompiler _compiler;
2024-06-14 14:18:17 -04:00
public static bool IsRunning { get; private set; } = false;
2024-06-18 23:45:00 -04:00
public delegate void OnCompleteHandler();
2024-06-14 18:20:12 -04:00
public event OnCompleteHandler? OnComplete;
2024-06-14 18:20:12 -04:00
private static readonly Stopwatch Stopwatch = new();
2024-06-13 17:05:34 -04:00
2024-06-17 17:29:26 -04:00
private RemapperSettings Settings => DataProvider.Settings.Remapper;
2024-06-18 17:35:31 -04:00
private string OutPath { get; set; } = string.Empty;
private bool CrossMapMode { get; set; } = false;
2024-06-19 04:10:46 -04:00
private string AssemblyPath { get; set; }
2024-06-13 01:53:43 -04:00
/// <summary>
/// Start the remapping process
/// </summary>
public void InitializeRemap(
List<RemapModel> remapModels,
string assemblyPath,
string outPath,
bool crossMapMode = false)
2024-06-11 19:18:48 -04:00
{
2024-06-18 17:35:31 -04:00
DataProvider.LoadAssemblyDefinition(assemblyPath);
2024-06-19 04:10:46 -04:00
AssemblyPath = assemblyPath;
2024-06-18 17:35:31 -04:00
CrossMapMode = crossMapMode;
OutPath = outPath;
2024-06-14 14:18:17 -04:00
IsRunning = true;
2024-06-11 19:18:48 -04:00
DisplayBasicModuleInformation();
2024-06-13 17:05:34 -04:00
Stopwatch.Start();
foreach (var remap in remapModels)
2024-06-11 19:18:48 -04:00
{
2024-06-12 18:59:08 -04:00
Logger.Log($"Finding best match for {remap.NewTypeName}...", ConsoleColor.Gray);
2024-06-11 19:18:48 -04:00
2024-06-14 16:18:43 -04:00
ScoreMapping(remap);
2024-06-11 19:18:48 -04:00
}
ChooseBestMatches();
// Don't publicize and unseal until after the remapping, so we can use those as search parameters
if (Settings.MappingSettings.Publicize)
2024-06-13 17:55:06 -04:00
{
//Publicizer.Publicize();
Logger.Log("Publicizing classes...", ConsoleColor.Green);
SPTPublicizer.PublicizeClasses(DataProvider.ModuleDefinition);
2024-06-13 17:55:06 -04:00
}
if (Settings.MappingSettings.Unseal)
2024-06-13 17:55:06 -04:00
{
//Publicizer.Unseal();
2024-06-13 17:55:06 -04:00
}
2024-06-11 19:18:48 -04:00
// We are done, write the assembly
WriteAssembly();
2024-06-19 05:58:45 -04:00
if (CrossMapMode)
{
ProjectManager.SaveCrossCompilerProjectModel(_compiler.ActiveProject);
}
2024-06-11 19:18:48 -04:00
}
2024-06-13 01:53:43 -04:00
/// <summary>
/// Display information about the module we are remapping
/// </summary>
2024-06-12 14:38:43 -04:00
private void DisplayBasicModuleInformation()
{
Logger.Log("-----------------------------------------------", ConsoleColor.Yellow);
Logger.Log($"Starting remap...", ConsoleColor.Yellow);
Logger.Log($"Module contains {DataProvider.ModuleDefinition.Types.Count} Types", ConsoleColor.Yellow);
2024-06-17 18:19:17 -04:00
Logger.Log($"Publicize: {Settings.MappingSettings.Publicize}", ConsoleColor.Yellow);
Logger.Log($"Unseal: {Settings.MappingSettings.Unseal}", ConsoleColor.Yellow);
2024-06-12 14:38:43 -04:00
Logger.Log("-----------------------------------------------", ConsoleColor.Yellow);
}
2024-06-13 01:53:43 -04:00
/// <summary>
/// Loop over all types in the assembly and score them
/// </summary>
/// <param name="mapping">Mapping to score</param>
2024-06-14 16:18:43 -04:00
public void ScoreMapping(RemapModel mapping)
2024-06-11 19:18:48 -04:00
{
foreach (var type in DataProvider.ModuleDefinition.Types)
{
2024-06-14 16:18:43 -04:00
FindMatch(type, mapping);
2024-06-11 19:18:48 -04:00
}
}
2024-06-13 01:53:43 -04:00
/// <summary>
2024-06-13 17:31:38 -04:00
/// Find a match result
2024-06-13 01:53:43 -04:00
/// </summary>
2024-06-16 00:19:02 -04:00
/// <param name="type">OriginalTypeRef to score</param>
2024-06-13 01:53:43 -04:00
/// <param name="remap">Remap to check against</param>
/// <param name="parentTypeName"></param>
2024-06-13 17:31:38 -04:00
/// <returns>EMatchResult</returns>
2024-06-14 16:18:43 -04:00
private void FindMatch(TypeDefinition type, RemapModel remap)
2024-06-11 19:18:48 -04:00
{
2024-06-11 23:07:59 -04:00
// Handle Direct Remaps by strict naming first bypasses everything else
if (remap.UseForceRename)
{
HandleByDirectName(type, remap);
2024-06-14 16:18:43 -04:00
return;
2024-06-11 23:07:59 -04:00
}
2024-06-21 14:11:06 -04:00
var tokens = DataProvider.Settings.AutoMapper.TokensToMatch;
2024-06-22 02:28:36 -04:00
bool ignore = tokens
.Where(token => !tokens
.Any(token => type.Name.StartsWith(token))).Any();
if (ignore && remap.SearchParams.IsNested is null)
2024-06-21 14:11:06 -04:00
{
return;
}
2024-06-11 19:18:48 -04:00
foreach (var nestedType in type.NestedTypes)
{
2024-06-13 17:37:14 -04:00
FindMatch(nestedType, remap);
2024-06-11 19:18:48 -04:00
}
var score = new ScoringModel
{
ProposedNewName = remap.NewTypeName,
2024-06-13 01:46:51 -04:00
ReMap = remap,
2024-06-12 00:05:59 -04:00
Definition = type,
2024-06-11 19:18:48 -04:00
};
2024-06-21 21:46:10 -04:00
var matches = new List<EMatchResult>
2024-06-13 17:05:34 -04:00
{
2024-06-21 21:46:10 -04:00
type.MatchConstructors(remap.SearchParams, score),
type.MatchMethods(remap.SearchParams, score),
type.MatchFields(remap.SearchParams, score),
type.MatchProperties(remap.SearchParams, score),
type.MatchNestedTypes(remap.SearchParams, score),
type.MatchIsPublic(remap.SearchParams, score) ,
type.MatchIsInterface(remap.SearchParams, score),
2024-06-13 17:05:34 -04:00
type.MatchIsAbstract(remap.SearchParams, score),
2024-06-21 21:46:10 -04:00
type.MatchIsSealed(remap.SearchParams, score) ,
2024-06-13 17:05:34 -04:00
type.MatchIsEnum(remap.SearchParams, score) ,
type.MatchIsNested(remap.SearchParams, score),
type.MatchIsDerived(remap.SearchParams, score) ,
type.MatchHasGenericParameters(remap.SearchParams, score),
type.MatchHasAttribute(remap.SearchParams, score),
};
2024-06-13 04:56:08 -04:00
2024-06-13 17:05:34 -04:00
var NoMatch = matches.Where(x => x.Equals(EMatchResult.NoMatch)).FirstOrDefault();
2024-06-11 19:18:48 -04:00
2024-06-13 17:05:34 -04:00
if (NoMatch == EMatchResult.NoMatch)
2024-06-11 19:18:48 -04:00
{
2024-06-14 16:18:43 -04:00
remap.FailureReason = score.FailureReason;
return;
2024-06-11 19:18:48 -04:00
}
2024-06-21 05:18:23 -04:00
var match = matches
.Where(x => x.Equals(EMatchResult.Match))
.Where(x => !x.Equals(EMatchResult.Disabled))
.Any();
2024-06-11 19:18:48 -04:00
2024-06-13 17:05:34 -04:00
if (match)
2024-06-11 19:18:48 -04:00
{
2024-06-13 17:05:34 -04:00
// Set the original type name to be used later
2024-06-22 02:28:36 -04:00
score.ReMap.OriginalTypeName = type.FullName;
remap.OriginalTypeName = type.FullName;
2024-06-13 17:05:34 -04:00
remap.Succeeded = true;
remap.FailureReason = EFailureReason.None;
score.AddScoreToResult();
2024-06-11 19:18:48 -04:00
}
}
2024-06-12 00:05:59 -04:00
private void HandleByDirectName(TypeDefinition type, RemapModel remap)
2024-06-11 23:07:59 -04:00
{
2024-06-12 00:05:59 -04:00
if (type.Name != remap.OriginalTypeName) { return; }
2024-06-11 23:07:59 -04:00
var oldName = type.Name;
remap.OriginalTypeName = type.Name;
2024-06-13 01:46:51 -04:00
remap.FailureReason = EFailureReason.None;
remap.Succeeded = true;
2024-06-18 17:35:31 -04:00
if (CrossMapMode)
{
// Store the original types for caching
2024-06-19 05:58:45 -04:00
_compiler.ActiveProject.ChangedTypes.Add(remap.NewTypeName, type.Name);
2024-06-18 17:35:31 -04:00
}
2024-06-12 22:31:41 -04:00
type.Name = remap.NewTypeName;
2024-06-12 14:38:43 -04:00
Logger.Log("-----------------------------------------------", ConsoleColor.Green);
2024-06-11 23:07:59 -04:00
Logger.Log($"Renamed {oldName} to {type.Name} directly", ConsoleColor.Green);
2024-06-12 14:38:43 -04:00
2024-06-16 01:48:48 -04:00
RenameHelper.RenameAllDirect(remap, type);
2024-06-12 14:38:43 -04:00
Logger.Log("-----------------------------------------------", ConsoleColor.Green);
2024-06-11 23:07:59 -04:00
}
2024-06-13 01:53:43 -04:00
/// <summary>
/// Choose the best possible match from all remaps
/// </summary>
2024-06-11 19:18:48 -04:00
private void ChooseBestMatches()
{
2024-06-11 23:07:59 -04:00
foreach (var score in DataProvider.ScoringModels)
2024-06-11 19:18:48 -04:00
{
2024-06-13 01:53:43 -04:00
ChooseBestMatch(score.Value);
2024-06-11 19:18:48 -04:00
}
2024-06-13 01:46:51 -04:00
var failures = 0;
var changes = 0;
foreach (var remap in DataProvider.Remaps)
{
if (remap.Succeeded is false)
{
Logger.Log("-----------------------------------------------", ConsoleColor.Red);
Logger.Log($"Renaming {remap.NewTypeName} failed with reason {remap.FailureReason}", ConsoleColor.Red);
Logger.Log("-----------------------------------------------", ConsoleColor.Red);
failures++;
2024-06-18 17:35:31 -04:00
continue;
2024-06-13 01:46:51 -04:00
}
changes++;
}
Logger.Log("-----------------------------------------------", ConsoleColor.Yellow);
Logger.Log($"Result: Remapped {changes} Types. Failed to remap {failures} Types", ConsoleColor.Yellow);
Logger.Log("-----------------------------------------------", ConsoleColor.Yellow);
2024-06-11 19:18:48 -04:00
}
2024-06-13 01:53:43 -04:00
/// <summary>
/// Choose best match from a collection of scores, then start the renaming process
/// </summary>
/// <param name="scores">Scores to rate</param>
private void ChooseBestMatch(HashSet<ScoringModel> scores)
2024-06-11 19:18:48 -04:00
{
2024-06-13 01:46:51 -04:00
if (scores.Count == 0) { return; }
2024-06-11 19:18:48 -04:00
2024-06-13 08:18:16 -04:00
var filteredScores = scores
2024-06-21 05:18:23 -04:00
.Where(score => score.Score > 0)
.OrderByDescending(score => score.Score)
.Take(5);
2024-06-13 08:18:16 -04:00
var highestScore = filteredScores.FirstOrDefault();
2024-06-11 19:18:48 -04:00
2024-06-11 23:07:59 -04:00
if (highestScore is null) { return; }
2024-06-11 19:18:48 -04:00
2024-06-11 23:07:59 -04:00
Logger.Log("-----------------------------------------------", ConsoleColor.Green);
2024-06-22 02:28:36 -04:00
Logger.Log($"Renaming {highestScore.Definition.FullName} to {highestScore.ProposedNewName}", ConsoleColor.Green);
2024-06-12 15:47:11 -04:00
Logger.Log($"Scored: {highestScore.Score} points", ConsoleColor.Green);
2024-06-21 21:46:10 -04:00
if (filteredScores.Count() > 1 && filteredScores.Skip(1).Any(score => score.Score == highestScore.Score))
2024-06-12 15:47:11 -04:00
{
2024-06-21 05:18:23 -04:00
Logger.Log($"Warning! There were {filteredScores.Count()} possible matches. Considering adding more search parameters, Only showing first 5.", ConsoleColor.Yellow);
2024-06-12 15:47:11 -04:00
2024-06-20 23:33:55 -04:00
foreach (var score in filteredScores.Skip(1).Take(5))
2024-06-12 15:47:11 -04:00
{
Logger.Log($"{score.Definition.Name} - Score [{score.Score}]", ConsoleColor.Yellow);
}
}
2024-06-12 00:05:59 -04:00
2024-06-22 02:28:36 -04:00
highestScore.ReMap.OriginalTypeName = highestScore.Definition.Name;
2024-06-18 17:35:31 -04:00
if (CrossMapMode)
{// Store the original types for caching
2024-06-19 05:58:45 -04:00
_compiler.ActiveProject.ChangedTypes.Add(highestScore.ProposedNewName, highestScore.Definition.Name);
2024-06-18 17:35:31 -04:00
}
2024-06-12 20:40:10 -04:00
2024-06-12 14:38:43 -04:00
// Rename type and all associated type members
2024-06-21 17:01:07 -04:00
2024-06-16 01:48:48 -04:00
RenameHelper.RenameAll(highestScore);
2024-06-11 19:18:48 -04:00
2024-06-11 23:07:59 -04:00
Logger.Log("-----------------------------------------------", ConsoleColor.Green);
2024-06-11 19:18:48 -04:00
}
2024-06-13 01:53:43 -04:00
/// <summary>
/// Write the assembly back to disk and update the mapping file on disk
/// </summary>
2024-06-11 19:18:48 -04:00
private void WriteAssembly()
{
var moduleName = DataProvider.AssemblyDefinition.MainModule.Name;
moduleName = moduleName.Replace(".dll", "-Remapped.dll");
OutPath = Path.Combine(OutPath, moduleName);
2024-06-21 17:01:07 -04:00
var path = DataProvider.WriteAssemblyDefinition(OutPath);
2024-06-11 19:18:48 -04:00
Logger.Log("Creating Hollow...", ConsoleColor.Yellow);
Hollow();
var hollowedDir = Path.GetDirectoryName(OutPath);
var hollowedPath = Path.Combine(hollowedDir, "Hollowed.dll");
DataProvider.WriteAssemblyDefinition(hollowedPath);
2024-06-11 23:07:59 -04:00
Logger.Log("-----------------------------------------------", ConsoleColor.Green);
2024-06-16 03:43:00 -04:00
Logger.Log($"Complete: Assembly written to `{path}`", ConsoleColor.Green);
Logger.Log($"Complete: Hollowed written to `{hollowedPath}`", ConsoleColor.Green);
2024-06-12 20:40:10 -04:00
Logger.Log("Original type names updated on mapping file.", ConsoleColor.Green);
2024-06-16 03:43:00 -04:00
Logger.Log($"Remap took {Stopwatch.Elapsed.TotalSeconds:F1} seconds", ConsoleColor.Green);
2024-06-11 23:07:59 -04:00
Logger.Log("-----------------------------------------------", ConsoleColor.Green);
2024-06-13 17:05:34 -04:00
2024-06-14 14:18:17 -04:00
DataProvider.ScoringModels = [];
Stopwatch.Reset();
2024-06-19 22:33:08 -04:00
IsRunning = false;
OnComplete?.Invoke();
2024-06-11 19:18:48 -04:00
}
/// <summary>
/// Hollows out all logic from the dll
/// </summary>
private void Hollow()
{
foreach (var type in DataProvider.ModuleDefinition.GetAllTypes())
{
foreach (var method in type.Methods.Where(m => m.HasBody))
{
var ilProcessor = method.Body.GetILProcessor();
// Remove existing instructions
ilProcessor.Clear();
}
}
}
2024-06-11 19:18:48 -04:00
}