Skip to content

Commit

Permalink
Merge pull request #79 from Windows10CE/monomod-reorg
Browse files Browse the repository at this point in the history
Update MonoMod to 25.0.0, add Harmony 2.3 features
  • Loading branch information
ManlyMarco authored Dec 20, 2023
2 parents e457977 + 449b92d commit e9ff32f
Show file tree
Hide file tree
Showing 26 changed files with 397 additions and 331 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,4 @@ ModelManifest.xml
/Harmony/Documentation/api/*.html
/Harmony/Documentation/api/*.yml
/Harmony/Documentation/Documentation.dll
.idea/
13 changes: 0 additions & 13 deletions .idea/.idea.Harmony/.idea/.gitignore

This file was deleted.

1 change: 0 additions & 1 deletion .idea/.idea.Harmony/.idea/.name

This file was deleted.

6 changes: 0 additions & 6 deletions .idea/.idea.Harmony/.idea/CakeRider.xml

This file was deleted.

5 changes: 0 additions & 5 deletions .idea/.idea.Harmony/.idea/codeStyles/codeStyleConfig.xml

This file was deleted.

6 changes: 0 additions & 6 deletions .idea/.idea.Harmony/.idea/discord.xml

This file was deleted.

8 changes: 0 additions & 8 deletions .idea/.idea.Harmony/.idea/indexLayout.xml

This file was deleted.

6 changes: 0 additions & 6 deletions .idea/.idea.Harmony/.idea/misc.xml

This file was deleted.

13 changes: 0 additions & 13 deletions .idea/.idea.Harmony/.idea/vcs.xml

This file was deleted.

25 changes: 13 additions & 12 deletions Harmony/Harmony.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<Authors>Andreas Pardeike, Geoffrey Horsington, ManlyMarco et al.</Authors>
<AssemblyName>0Harmony</AssemblyName>
<SignAssembly>true</SignAssembly>
<Version>2.10.2</Version>
<Version>2.11.0</Version>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageProjectUrl>https://github.com/BepInEx/HarmonyX</PackageProjectUrl>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
Expand Down Expand Up @@ -46,9 +46,9 @@
</Choose>

<ItemGroup>
<None Include="..\LICENSE" Pack="true" PackagePath=""/>
<None Include="..\logo_mini.png" Pack="true" Visible="false" PackagePath=""/>
<None Include="..\README.md" Pack="false" PackagePath=""/>
<None Include="..\LICENSE" Pack="true" PackagePath="" />
<None Include="..\logo_mini.png" Pack="true" Visible="false" PackagePath="" />
<None Include="..\README.md" Pack="false" PackagePath="" />
</ItemGroup>

<PropertyGroup Condition="'$(Configuration)'=='Debug'">
Expand All @@ -66,34 +66,35 @@

<ItemGroup>
<!-- Reference assemblies are needed for non-Windows .NET Framework targeting builds. -->
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.2" PrivateAssets="all"/>
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.2" PrivateAssets="all" />

<!-- Reference MonoMod.RuntimeDetour for our detour needs -->
<PackageReference Include="MonoMod.RuntimeDetour" Version="22.3.23.4"/>
<PackageReference Include="MonoMod.RuntimeDetour" Version="25.0.0" />

<!-- Reference SRE helpers for .NET Standard -->
<PackageReference Include="System.Reflection.Emit" Version="4.7.0" Condition="'$(TargetFramework)' == 'netstandard2.0'"/>
<PackageReference Include="System.Reflection.Emit" Version="4.7.0" Condition="'$(TargetFramework)' == 'netstandard2.0'" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" Condition="'$(TargetFramework)' == 'netstandard2.0'" />
</ItemGroup>

<Target Name="RemoveOldNuGetPackages" BeforeTargets="PreBuildEvent">
<PropertyGroup>
<WorkingDirectory>$(MSBuildThisFileDirectory)bin\$(Configuration)</WorkingDirectory>
</PropertyGroup>
<ItemGroup>
<OldNugetPackages Include="$(WorkingDirectory)\$(PackageId).*.nupkg" Exclude="$(WorkingDirectory)\$(PackageId).$(Version).nupkg"/>
<OldNugetPackages Include="$(WorkingDirectory)\$(PackageId).*.nupkg" Exclude="$(WorkingDirectory)\$(PackageId).$(Version).nupkg" />
</ItemGroup>
<Delete Files="@(OldNugetPackages)"/>
<Delete Files="@(OldNugetPackages)" />
</Target>

<Target Name="Zip" AfterTargets="Pack" DependsOnTargets="CleanZip" Condition="'$(Configuration)'=='Release'">
<ZipDirectory SourceDirectory="$(MSBuildThisFileDirectory)bin\$(Configuration)" DestinationFile="$(MSBuildThisFileDirectory)bin\Harmony.$(Version).zip" Overwrite="true"/>
<ZipDirectory SourceDirectory="$(MSBuildThisFileDirectory)bin\$(Configuration)" DestinationFile="$(MSBuildThisFileDirectory)bin\Harmony.$(Version).zip" Overwrite="true" />
</Target>

<Target Name="CleanZip" AfterTargets="Clean">
<ItemGroup>
<OldZipFiles Include="$(MSBuildThisFileDirectory)bin\*.zip"/>
<OldZipFiles Include="$(MSBuildThisFileDirectory)bin\*.zip" />
</ItemGroup>
<Delete Files="@(OldZipFiles)"/>
<Delete Files="@(OldZipFiles)" />
</Target>

</Project>
4 changes: 2 additions & 2 deletions Harmony/Internal/PatchFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ static void PrintInfo(StringBuilder sb, ICollection<MethodInfo> methods, string
// TODO: Handle new debugType
Logger.Log(Logger.LogChannel.IL,
() => $"Generated reverse patcher ({ctx.Method.FullName}):\n{ctx.Body.ToILDasmString()}", debug);
}, new ILHookConfig { ManualApply = true });
}, applyByDefault: false);

try
{
Expand All @@ -157,7 +157,7 @@ static void PrintInfo(StringBuilder sb, ICollection<MethodInfo> methods, string
throw HarmonyException.Create(ex, patchBody);
}

var replacement = hook.GetCurrentTarget() as MethodInfo;
var replacement = hook.Method as MethodInfo;
PatchTools.RememberObject(standin.method, replacement);
return replacement;
}
Expand Down
15 changes: 10 additions & 5 deletions Harmony/Internal/PatchTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ internal static class PatchTools
// https://stackoverflow.com/a/33153868
// ThreadStatic has pitfalls (see RememberObject below), but since we must support net35, it's the best available option.
[ThreadStatic]
static Dictionary<object, object> objectReferences;
private static Dictionary<object, object> objectReferences;

internal static void RememberObject(object key, object value)
{
Expand Down Expand Up @@ -65,13 +65,13 @@ internal static MethodBase GetOriginalMethod(this HarmonyMethod attr)

case MethodType.Getter:
if (attr.methodName is null)
return null;
return AccessTools.DeclaredProperty(attr.GetDeclaringType(), attr.methodName).GetGetMethod(true);
return AccessTools.DeclaredIndexer(attr.declaringType, attr.argumentTypes).GetGetMethod(true);
return AccessTools.DeclaredProperty(attr.declaringType, attr.methodName).GetGetMethod(true);

case MethodType.Setter:
if (attr.methodName is null)
return null;
return AccessTools.DeclaredProperty(attr.GetDeclaringType(), attr.methodName).GetSetMethod(true);
return AccessTools.DeclaredIndexer(attr.declaringType, attr.argumentTypes).GetSetMethod(true);
return AccessTools.DeclaredProperty(attr.declaringType, attr.methodName).GetSetMethod(true);

case MethodType.Constructor:
return AccessTools.DeclaredConstructor(attr.GetDeclaringType(), attr.argumentTypes);
Expand All @@ -86,6 +86,11 @@ internal static MethodBase GetOriginalMethod(this HarmonyMethod attr)
return null;
return AccessTools.EnumeratorMoveNext(AccessTools.DeclaredMethod(attr.GetDeclaringType(),
attr.methodName, attr.argumentTypes));

case MethodType.Async:
if (attr.methodName is null)
return null;
return AccessTools.AsyncMoveNext(AccessTools.DeclaredMethod(attr.GetDeclaringType(), attr.methodName, attr.argumentTypes));
}
}
catch (AmbiguousMatchException ex)
Expand Down
45 changes: 0 additions & 45 deletions Harmony/Internal/Util/ILHookExtensions.cs

This file was deleted.

69 changes: 38 additions & 31 deletions Harmony/Internal/Util/StackTraceFixes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
using System.Diagnostics;
using System.Reflection;
using HarmonyLib.Tools;
using MonoMod.Core.Platforms;
using MonoMod.RuntimeDetour;
using System.Linq;

namespace HarmonyLib.Internal.RuntimeFixes
{
Expand All @@ -18,9 +20,9 @@ internal static class StackTraceFixes
private static readonly Dictionary<MethodBase, MethodBase> RealMethodMap =
new Dictionary<MethodBase, MethodBase>();

private static Func<Assembly> _realGetAss;
private static Func<StackFrame, MethodBase> _origGetMethod;
private static Action<object> _origRefresh;
private static Hook getAssemblyHookManaged;
private static NativeHook getAssemblyHookNative;
private static Hook getMethodHook;

public static void Install()
{
Expand All @@ -29,17 +31,20 @@ public static void Install()

try
{
var refreshDet = new Detour(AccessTools.Method(AccessTools.Inner(typeof(ILHook), "Context"), "Refresh"),
AccessTools.Method(typeof(StackTraceFixes), nameof(OnILChainRefresh)));
_origRefresh = refreshDet.GenerateTrampoline<Action<object>>();
DetourManager.ILHookApplied += OnILChainRefresh;
DetourManager.ILHookUndone += OnILChainRefresh;

var getMethodDet = new Detour(AccessTools.Method(typeof(StackFrame), nameof(StackFrame.GetMethod)),
AccessTools.Method(typeof(StackTraceFixes), nameof(GetMethodFix)));
_origGetMethod = getMethodDet.GenerateTrampoline<Func<StackFrame, MethodBase>>();
var getAssemblyMethod = AccessTools.DeclaredMethod(typeof(Assembly), nameof(Assembly.GetExecutingAssembly), EmptyType.NoArgs);
if (getAssemblyMethod.HasMethodBody())
{
getAssemblyHookManaged = new Hook(getAssemblyMethod, GetAssemblyFix);
}
else
{
getAssemblyHookNative = new NativeHook(PlatformTriple.Current.GetNativeMethodBody(getAssemblyMethod), GetAssemblyFix);
}

var nat = new NativeDetour(AccessTools.Method(typeof(Assembly), nameof(Assembly.GetExecutingAssembly)),
AccessTools.Method(typeof(StackTraceFixes), nameof(GetAssemblyFix)));
_realGetAss = nat.GenerateTrampoline<Func<Assembly>>();
getMethodHook = new Hook(AccessTools.DeclaredMethod(typeof(StackFrame), nameof(StackFrame.GetMethod), EmptyType.NoArgs), GetMethodFix);
}
catch (Exception e)
{
Expand All @@ -48,37 +53,39 @@ public static void Install()
_applied = true;
}

// Fix StackFrame's GetMethod to map patched method to unpatched one instead
private static MethodBase GetMethodFix(StackFrame self)
{
var m = _origGetMethod(self);
if (m == null)
return null;
lock (RealMethodMap)
{
return RealMethodMap.TryGetValue(m, out var real) ? real : m;
}
}
delegate Assembly GetAssemblyDelegate();

// We need to force GetExecutingAssembly make use of stack trace
// This is to fix cases where calling assembly is actually the patch
// This solves issues with code where it uses the method to get current filepath etc
private static Assembly GetAssemblyFix()
private static Assembly GetAssemblyFix(GetAssemblyDelegate orig)
{
return new StackFrame(1).GetMethod()?.Module.Assembly ?? _realGetAss();
var entry = getAssemblyHookManaged?.DetourInfo.Entry ?? getAssemblyHookNative.DetourInfo.Entry;
var method = new StackTrace().GetFrames()!.Select(f => f.GetMethod()).SkipWhile(method => method != entry).Skip(1).First();
return method.Module.Assembly;
}

// Helper to save the detour info after patch is complete
private static void OnILChainRefresh(object self)
private static MethodBase GetMethodFix(Func<StackFrame, MethodBase> orig, StackFrame self)
{
_origRefresh(self);
var method = orig(self);
if (method is not null && RealMethodMap.TryGetValue(PlatformTriple.Current.GetIdentifiable(method), out var real))
{
return real;
}
return method;
}

if (!(AccessTools.Field(self.GetType(), "Detour").GetValue(self) is Detour detour))
return;
private static readonly AccessTools.FieldRef<MethodDetourInfo, object> GetDetourState = AccessTools.FieldRefAccess<MethodDetourInfo, object>(AccessTools.DeclaredField(typeof(MethodDetourInfo), "state"));

private static readonly AccessTools.FieldRef<object, MethodBase> GetEndOfChain =
AccessTools.FieldRefAccess<object, MethodBase>(AccessTools.DeclaredField(typeof(DetourManager).GetNestedType("ManagedDetourState", AccessTools.all), "EndOfChain"));

// Helper to save the detour info after patch is complete
private static void OnILChainRefresh(ILHookInfo self)
{
lock (RealMethodMap)
{
RealMethodMap[detour.Target] = detour.Method;
RealMethodMap[PlatformTriple.Current.GetIdentifiable(GetEndOfChain(GetDetourState(self.Method)))] = PlatformTriple.Current.GetIdentifiable(self.Method.Method);
}
}
}
Expand Down
19 changes: 17 additions & 2 deletions Harmony/Public/Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public enum MethodType
StaticConstructor,
/// <summary>This is an enumerator (<see cref="IEnumerable{T}"/>, <see cref="IEnumerator{T}"/> or UniTask coroutine)</summary>
/// <remarks>This path will target the <see cref="IEnumerator.MoveNext"/> method that contains the actual enumerator code</remarks>
Enumerator
Enumerator,
Async
}

/// <summary>Specifies the type of argument</summary>
Expand Down Expand Up @@ -112,7 +113,21 @@ public class HarmonyAttribute : Attribute
public HarmonyMethod info = new HarmonyMethod();
}

/// <summary>Annotation to define targets of your Harmony patch methods</summary>
/// <summary>Annotation to define a category for use with PatchCategory</summary>
///
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class HarmonyPatchCategory : HarmonyAttribute
{
/// <summary>Annotation specifying the category</summary>
/// <param name="category">Name of patch category</param>
///
public HarmonyPatchCategory(string category)
{
info.category = category;
}
}

/// <summary>Annotation to define your Harmony patch methods</summary>
///
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Delegate | AttributeTargets.Method, AllowMultiple = true)]
public class HarmonyPatch : HarmonyAttribute
Expand Down
Loading

0 comments on commit e9ff32f

Please sign in to comment.