Add hollower, fix bugs, add spt publicizer
This commit is contained in:
parent
398aabaf98
commit
6381a3b0f8
@ -424,7 +424,7 @@ public class MappingSettings
|
|||||||
get { return _publicize; }
|
get { return _publicize; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_unseal = value;
|
_publicize = value;
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using ReCodeIt.Utils;
|
using Mono.Cecil;
|
||||||
|
using Mono.Cecil.Rocks;
|
||||||
|
using ReCodeIt.Utils;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace ReCodeIt.ReMapper;
|
namespace ReCodeIt.ReMapper;
|
||||||
|
|
||||||
@ -50,3 +53,142 @@ public static class Publicizer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static class SPTPublicizer
|
||||||
|
{
|
||||||
|
private static ModuleDefinition MainModule;
|
||||||
|
|
||||||
|
public static void PublicizeClasses(ModuleDefinition definition)
|
||||||
|
{
|
||||||
|
var types = definition.GetAllTypes();
|
||||||
|
|
||||||
|
foreach (var type in types)
|
||||||
|
{
|
||||||
|
if (type.IsNested) continue; // Nested types are handled when publicizing the parent type
|
||||||
|
|
||||||
|
PublicizeType(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PublicizeType(TypeDefinition type)
|
||||||
|
{
|
||||||
|
// if (type.CustomAttributes.Any(a => a.AttributeType.Name ==
|
||||||
|
// nameof(CompilerGeneratedAttribute))) { return; }
|
||||||
|
|
||||||
|
if (!type.IsNested && !type.IsPublic || type.IsNested && !type.IsNestedPublic)
|
||||||
|
{
|
||||||
|
type.Attributes &= ~TypeAttributes.VisibilityMask; // Remove all visibility mask attributes
|
||||||
|
type.Attributes |= type.IsNested ? TypeAttributes.NestedPublic : TypeAttributes.Public; // Apply a public visibility attribute
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.IsSealed)
|
||||||
|
{
|
||||||
|
type.Attributes &= ~TypeAttributes.Sealed; // Remove the Sealed attribute if it exists
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var method in type.Methods)
|
||||||
|
{
|
||||||
|
PublicizeMethod(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var property in type.Properties)
|
||||||
|
{
|
||||||
|
if (property.GetMethod != null) PublicizeMethod(property.GetMethod);
|
||||||
|
if (property.SetMethod != null) PublicizeMethod(property.SetMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
// var eventNames = new HashSet<string>(type.Events.Select(e => e.Name)); foreach (var field
|
||||||
|
// in type.Fields) { if (eventNames.Contains(field.Name)) { continue; }
|
||||||
|
//
|
||||||
|
// // if (type.Name.StartsWith("GClass") || !type.Namespace.Contains("EFT") ||
|
||||||
|
// !type.Namespace.Contains("UI") || !string.IsNullOrWhiteSpace(type.Namespace)) // if
|
||||||
|
// (type.Namespace.Length > 0 && type.Namespace[0] > 'E') PublicizeField(field); }
|
||||||
|
|
||||||
|
var nestedTypesToPublicize = type.NestedTypes.ToArray();
|
||||||
|
|
||||||
|
// Workaround to not publicize some nested types that cannot be patched easily and cause
|
||||||
|
// issues Specifically, we want to find any type that implements the "IHealthController"
|
||||||
|
// interface and make sure none of it's nested types that implement "IEffect" are changed
|
||||||
|
if (GetFlattenedInterfacesRecursive(type).Any(i => i.InterfaceType.Name == "IHealthController"))
|
||||||
|
{
|
||||||
|
// Specifically, any type that implements the IHealthController interface needs to not
|
||||||
|
// publicize any nested types that implement the IEffect interface
|
||||||
|
nestedTypesToPublicize = type.NestedTypes.Where(t => t.IsAbstract || t.Interfaces.All(i => i.InterfaceType.Name != "IEffect")).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var nestedType in nestedTypesToPublicize)
|
||||||
|
{
|
||||||
|
PublicizeType(nestedType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't publicize methods that implement interfaces not belonging to the current assembly
|
||||||
|
// Unused - sometimes some ambiguous reference errors appear due to this, but the pros outweigh
|
||||||
|
// the cons at the moment
|
||||||
|
private static bool CanPublicizeMethod(MethodDefinition method)
|
||||||
|
{
|
||||||
|
return !method.HasOverrides && method.GetBaseMethod().Equals(method) && !method.IsVirtual;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PublicizeMethod(MethodDefinition method)
|
||||||
|
{
|
||||||
|
if (method.IsCompilerControlled /*|| method.CustomAttributes.Any(a => a.AttributeType.Name == nameof(CompilerGeneratedAttribute))*/)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method.IsPublic) return;
|
||||||
|
|
||||||
|
// if (!CanPublicizeMethod(method)) return;
|
||||||
|
|
||||||
|
// Workaround to not publicize a specific method so the game doesn't crash
|
||||||
|
if (method.Name == "TryGetScreen") return;
|
||||||
|
|
||||||
|
method.Attributes &= ~MethodAttributes.MemberAccessMask;
|
||||||
|
method.Attributes |= MethodAttributes.Public;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unused for now - publicizing fields is tricky, as it often creates MonoBehaviour loading
|
||||||
|
// errors and prevents scenes from loading, most notably breaking the initial game loader scene
|
||||||
|
// and causing the game to CTD right after starting
|
||||||
|
private static void PublicizeField(FieldDefinition field)
|
||||||
|
{
|
||||||
|
if (field.CustomAttributes.Any(a => a.AttributeType.Name == nameof(CompilerGeneratedAttribute))
|
||||||
|
// || field.HasCustomAttributes
|
||||||
|
|| field.Name.StartsWith("delegate")
|
||||||
|
|| field.Name.Contains("__BackingField"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field.IsPublic || field.IsCompilerControlled || field.IsLiteral || field.IsStatic || field.IsInitOnly) return;
|
||||||
|
|
||||||
|
field.Attributes &= ~FieldAttributes.FieldAccessMask;
|
||||||
|
field.Attributes |= FieldAttributes.Public;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<InterfaceImplementation> GetFlattenedInterfacesRecursive(TypeDefinition type)
|
||||||
|
{
|
||||||
|
var interfaces = new List<InterfaceImplementation>();
|
||||||
|
|
||||||
|
if (type is null) return interfaces;
|
||||||
|
|
||||||
|
if (type.Interfaces.Any())
|
||||||
|
{
|
||||||
|
interfaces.AddRange(type.Interfaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.BaseType != null && !type.BaseType.Name.Contains("Object"))
|
||||||
|
{
|
||||||
|
var baseTypeDefinition = MainModule?.ImportReference(type.BaseType)?.Resolve();
|
||||||
|
var baseTypeInterfaces = GetFlattenedInterfacesRecursive(baseTypeDefinition);
|
||||||
|
|
||||||
|
if (baseTypeInterfaces.Any())
|
||||||
|
{
|
||||||
|
interfaces.AddRange(baseTypeInterfaces);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return interfaces;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
|
using Mono.Cecil.Cil;
|
||||||
|
using Mono.Cecil.Rocks;
|
||||||
using ReCodeIt.CrossCompiler;
|
using ReCodeIt.CrossCompiler;
|
||||||
using ReCodeIt.Enums;
|
using ReCodeIt.Enums;
|
||||||
using ReCodeIt.Models;
|
using ReCodeIt.Models;
|
||||||
@ -66,15 +68,19 @@ public class ReCodeItRemapper
|
|||||||
|
|
||||||
ChooseBestMatches();
|
ChooseBestMatches();
|
||||||
|
|
||||||
// Dont publicize and unseal until after the remapping so we can use those as search parameters
|
// Don't publicize and unseal until after the remapping, so we can use those as search parameters
|
||||||
if (Settings.MappingSettings.Publicize)
|
if (Settings.MappingSettings.Publicize)
|
||||||
{
|
{
|
||||||
Publicizer.Publicize();
|
//Publicizer.Publicize();
|
||||||
|
|
||||||
|
Logger.Log("Publicizing classes...", ConsoleColor.Green);
|
||||||
|
|
||||||
|
SPTPublicizer.PublicizeClasses(DataProvider.ModuleDefinition);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings.MappingSettings.Unseal)
|
if (Settings.MappingSettings.Unseal)
|
||||||
{
|
{
|
||||||
Publicizer.Unseal();
|
//Publicizer.Unseal();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We are done, write the assembly
|
// We are done, write the assembly
|
||||||
@ -297,10 +303,26 @@ public class ReCodeItRemapper
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void WriteAssembly()
|
private void WriteAssembly()
|
||||||
{
|
{
|
||||||
|
if (!OutPath.EndsWith(".dll"))
|
||||||
|
{
|
||||||
|
var moduleName = DataProvider.AssemblyDefinition.MainModule.Name;
|
||||||
|
moduleName = moduleName.Replace(".dll", "-Remapped.dll");
|
||||||
|
|
||||||
|
OutPath = Path.Combine(OutPath, moduleName);
|
||||||
|
}
|
||||||
|
|
||||||
var path = DataProvider.WriteAssemblyDefinition(OutPath);
|
var path = DataProvider.WriteAssemblyDefinition(OutPath);
|
||||||
|
|
||||||
|
Logger.Log("-----------------------------------------------", ConsoleColor.Green);
|
||||||
|
Hollow();
|
||||||
|
|
||||||
|
var hollowedDir = Path.GetDirectoryName(OutPath);
|
||||||
|
var hollowedPath = Path.Combine(hollowedDir, "Hollowed.dll");
|
||||||
|
DataProvider.WriteAssemblyDefinition(hollowedPath);
|
||||||
|
|
||||||
Logger.Log("-----------------------------------------------", ConsoleColor.Green);
|
Logger.Log("-----------------------------------------------", ConsoleColor.Green);
|
||||||
Logger.Log($"Complete: Assembly written to `{path}`", ConsoleColor.Green);
|
Logger.Log($"Complete: Assembly written to `{path}`", ConsoleColor.Green);
|
||||||
|
Logger.Log($"Complete: Hollowed written to `{hollowedPath}`", ConsoleColor.Green);
|
||||||
Logger.Log("Original type names updated on mapping file.", ConsoleColor.Green);
|
Logger.Log("Original type names updated on mapping file.", ConsoleColor.Green);
|
||||||
Logger.Log($"Remap took {Stopwatch.Elapsed.TotalSeconds:F1} seconds", ConsoleColor.Green);
|
Logger.Log($"Remap took {Stopwatch.Elapsed.TotalSeconds:F1} seconds", ConsoleColor.Green);
|
||||||
Logger.Log("-----------------------------------------------", ConsoleColor.Green);
|
Logger.Log("-----------------------------------------------", ConsoleColor.Green);
|
||||||
@ -311,4 +333,43 @@ public class ReCodeItRemapper
|
|||||||
IsRunning = false;
|
IsRunning = false;
|
||||||
OnComplete?.Invoke();
|
OnComplete?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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();
|
||||||
|
|
||||||
|
if (method.ReturnType.FullName != "System.Void")
|
||||||
|
{
|
||||||
|
// Return appropriate default value based on return type
|
||||||
|
if (method.ReturnType.IsValueType)
|
||||||
|
{
|
||||||
|
// Load 0 onto the stack (works for most value types)
|
||||||
|
ilProcessor.Emit(OpCodes.Ldc_I4_0);
|
||||||
|
|
||||||
|
// Convert to Int64 if needed
|
||||||
|
if (method.ReturnType.FullName == "System.Int64")
|
||||||
|
ilProcessor.Emit(OpCodes.Conv_I8);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Load null for reference types
|
||||||
|
ilProcessor.Emit(OpCodes.Ldnull);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a return instruction
|
||||||
|
ilProcessor.Emit(OpCodes.Ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user