using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using ReCodeIt.Models; using ReCodeIt.ReMapper; using ReCodeIt.Utils; using System.Diagnostics; namespace ReCodeIt.CrossCompiler; public class ReCodeItCrossCompiler { public ReCodeItCrossCompiler() { Remapper = new(this); } private ReCodeItRemapper Remapper { get; } public CrossCompilerSettings Settings => DataProvider.Settings.CrossCompiler; public CrossCompilerProjectModel ActiveProject => ProjectManager.ActiveProject; private int _identifiersChanged = 0; public void StartRemap() { ActiveProject.ChangedTypes.Clear(); if (ActiveProject == null) { Logger.Log("ERROR: No Cross Compiler Project is loaded, create or load one first.", ConsoleColor.Red); return; } if (ActiveProject.ReCodeItProjectPath == string.Empty) { Logger.Log("ERROR: No ReCodeIt Project directory is set. (Project Creation Failed)", ConsoleColor.Red); return; } Remapper.InitializeRemap( ActiveProject.RemapModels, ActiveProject.OriginalAssemblyPath, ActiveProject.RemappedAssemblyPath, true); Logger.Log("-----------------------------------------------", ConsoleColor.Green); Logger.Log($"Generated Cross Mapped DLL for project {ActiveProject.SolutionName}", ConsoleColor.Green); Logger.Log($"Changed {ActiveProject.ChangedTypes.Count} types", ConsoleColor.Green); Logger.Log($"Original assembly path: {ActiveProject.OriginalAssemblyPath}", ConsoleColor.Green); Logger.Log($"Original assembly hash: {ActiveProject.OriginalAssemblyHash}", ConsoleColor.Green); Logger.Log($"Original patched assembly path: {ActiveProject.RemappedAssemblyPath}", ConsoleColor.Green); //Logger.Log($"Original patched assembly hash: {ActiveProject.RemappedAssemblyHash}", ConsoleColor.Green); Logger.Log("-----------------------------------------------", ConsoleColor.Green); } public void StartCrossCompile() { _identifiersChanged = 0; AnalyzeSourceFiles(); StartBuild(); MoveResult(); } private void AnalyzeSourceFiles() { foreach (var file in ProjectManager.AllProjectSourceFiles) { AnalyzeSourcefile(file); } var fileName = Path.GetFileName(ActiveProject.OriginalAssemblyPath); var outPath = Path.Combine(ActiveProject.RemappedAssemblyPath, fileName); Logger.Log($"Placing original reference into cloned build directory", ConsoleColor.Green); File.Copy(ActiveProject.OriginalAssemblyPath, outPath, true); } private void AnalyzeSourcefile(string file) { var source = File.ReadAllText(file); var syntaxTree = CSharpSyntaxTree.ParseText(source); var root = syntaxTree.GetCompilationUnitRoot(); // Get the things we want to change var identifiers = root.DescendantNodes() .OfType() .Where(id => ActiveProject.ChangedTypes.ContainsKey(id.Identifier.Text)); if (!identifiers.Any()) { return; } _identifiersChanged += identifiers.Count(); Logger.Log($"changing {_identifiersChanged} identifiers in file {Path.GetFileName(file)}", ConsoleColor.Green); // Do Black Voodoo Magic var newRoot = root.ReplaceNodes(identifiers, (oldNode, newNode) => SyntaxFactory.IdentifierName(ActiveProject.ChangedTypes[oldNode.Identifier.Text]) .WithLeadingTrivia(oldNode.GetLeadingTrivia()) .WithTrailingTrivia(oldNode.GetTrailingTrivia())); File.WriteAllText(file, newRoot.ToFullString()); } /// /// Starts the build process for the active project. /// private void StartBuild() { var arguements = $"build {ActiveProject.VisualStudioClonedSolutionPath} " + $"/p:Configuration=Debug " + $"/p:Platform=\"Any CPU\""; var path = ActiveProject.VisualStudioClonedSolutionDirectory; // clean the project first ExecuteDotnetCommand("clean", path); // Restore packages ExecuteDotnetCommand("restore", path); // Then build the project ExecuteDotnetCommand(arguements, path); } private static void ExecuteDotnetCommand(string arguments, string workingDirectory) { ProcessStartInfo startInfo = new ProcessStartInfo { FileName = "dotnet", Arguments = arguments, WorkingDirectory = workingDirectory, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; using (Process process = new Process()) { process.StartInfo = startInfo; process.OutputDataReceived += (sender, e) => Logger.Log(e.Data); process.Start(); process.BeginOutputReadLine(); process.BeginErrorReadLine(); process.WaitForExit(); int exitCode = process.ExitCode; Logger.Log($"dotnet {arguments} exited with code {exitCode}"); } } private void MoveResult() { Logger.Log("-----------------------------------------------", ConsoleColor.Green); Logger.Log($"Successfully Cross Compiled Project {ActiveProject.SolutionName}", ConsoleColor.Green); Logger.Log($"Reversed {_identifiersChanged} Remaps", ConsoleColor.Green); Logger.Log($"Original assembly path: {ActiveProject.OriginalAssemblyPath}", ConsoleColor.Green); Logger.Log($"Original assembly hash: {ActiveProject.OriginalAssemblyHash}", ConsoleColor.Green); Logger.Log($"Final build directory: {ActiveProject.BuildDirectory}", ConsoleColor.Green); //Logger.Log($"Original patched assembly hash: {ActiveProject.RemappedAssemblyHash}", ConsoleColor.Green); Logger.Log("-----------------------------------------------", ConsoleColor.Green); } }