Initial commit

This commit is contained in:
Flero 2025-05-25 05:29:54 +09:30
commit c75afff785
Signed by: flerouwu
SSH key fingerprint: SHA256:FB8cuclKIbQLfo1o/JNTq7r9e3vocaOu/vkEg+gViVU
27345 changed files with 3421853 additions and 0 deletions

13
.idea/.idea.decompiled/.idea/.gitignore generated vendored Normal file
View file

@ -0,0 +1,13 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/contentModel.xml
/.idea.decompiled.iml
/modules.xml
/projectSettingsUpdater.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

6
.idea/.idea.decompiled/.idea/vcs.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View file

@ -0,0 +1,6 @@
using System;
using System.Runtime.CompilerServices;
using MonoMod.Core.Interop;
[CompilerGenerated]
internal unsafe delegate CoreCLR.CorJitResult _003C_003Ef__AnonymousDelegate0(IntPtr arg1, IntPtr arg2, CoreCLR.V21.CORINFO_METHOD_INFO* arg3, uint arg4, byte** arg5, uint* arg6);

View file

@ -0,0 +1,6 @@
using System;
using System.Runtime.CompilerServices;
using MonoMod.Core.Interop;
[CompilerGenerated]
internal unsafe delegate void _003C_003Ef__AnonymousDelegate1(IntPtr arg1, CoreCLR.V70.AllocMemArgs* arg2);

View file

@ -0,0 +1,126 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[CompilerGenerated]
internal sealed class _003C027f1d0e_002D6e0b_002D4adc_002Dbc2b_002Da5d0603c6ea8_003E_003CPrivateImplementationDetails_003E
{
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 12)]
private struct __StaticArrayInitTypeSize_003D12
{
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 13)]
private struct __StaticArrayInitTypeSize_003D13
{
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 16)]
private struct __StaticArrayInitTypeSize_003D16
{
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 19)]
private struct __StaticArrayInitTypeSize_003D19
{
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 22)]
private struct __StaticArrayInitTypeSize_003D22
{
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 23)]
private struct __StaticArrayInitTypeSize_003D23
{
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 24)]
private struct __StaticArrayInitTypeSize_003D24
{
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 27)]
private struct __StaticArrayInitTypeSize_003D27
{
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 30)]
private struct __StaticArrayInitTypeSize_003D30
{
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 32)]
private struct __StaticArrayInitTypeSize_003D32
{
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 34)]
private struct __StaticArrayInitTypeSize_003D34
{
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 66)]
private struct __StaticArrayInitTypeSize_003D66
{
}
internal static readonly __StaticArrayInitTypeSize_003D19 _063D533BF7BF33642F4FC29769379C0BABC72FEA4306A19D07042E80825D6B11/* Not supported: data(40 B8 00 00 00 00 00 00 00 00 66 FF 00 0F 85 02 02 02 02) */;
internal static readonly __StaticArrayInitTypeSize_003D13 _068180318FD5EE121B32F89781E9280F6FF999CAC1FA0DB527A95A3843378DD5/* Not supported: data(4C 8B 15 FB 0F 00 00 FF 25 02 02 02 02) */;
internal static readonly __StaticArrayInitTypeSize_003D27 _0B64EDF08F75F2FE52EC6547561D9C8A66BA0B61776459182FADE1BB78716190/* Not supported: data(F0 FF 00 00 00 00 00 00 00 00 FF FF F0 FF 00 FF FF 00 00 00 00 00 00 00 00 FF FF) */;
internal static readonly __StaticArrayInitTypeSize_003D32 _0F9187567FB5894625BFC40F5925C790EFFBCB097E38C4DE2B08DFD8CD904B8A/* Not supported: data(B8 00 00 FF 00 FF 00 FF 00 FF 90 00 E8 00 00 FF 00 FF 00 FF 00 FF E9 00 02 FF 02 FF 02 FF 02 FF) */;
internal static readonly __StaticArrayInitTypeSize_003D19 _2BB2CC148EF6C3FAE6F41C31639ADA373805CBFF80B3B73F68CE07508ED616AB/* Not supported: data(FF 25 02 02 02 02 4C 8B 15 FB 0F 00 00 FF 25 FD 0F 00 00) */;
internal static readonly __StaticArrayInitTypeSize_003D24 _304265D0B85BB47E38400410EE72E860FF5F8D4B615C540398B95EBF79DFBAF2/* Not supported: data(48 00 B8 00 02 FF 02 FF 02 FF 02 FF 02 FF 02 FF 02 FF 02 FF FF 00 E0 00) */;
internal static readonly __StaticArrayInitTypeSize_003D34 _343ADBD2A8A9F3CE874C597E817C24EA409128280C492571CF8CDA6C2556EA2F/* Not supported: data(FF 00 25 00 02 FF 02 FF 02 FF 02 FF A1 00 00 FF 00 FF 00 FF 00 FF FF 00 25 00 00 FF 00 FF 00 FF 00 FF) */;
internal static readonly __StaticArrayInitTypeSize_003D22 _38B58BF5636F848CEBA118506041F474DAD63E659CEDF4355BC1BDA17C4B73FD/* Not supported: data(A1 00 00 FF 00 FF 00 FF 00 FF FF 00 25 00 02 FF 02 FF 02 FF 02 FF) */;
internal static readonly __StaticArrayInitTypeSize_003D19 _3C127FF5DF33251D5AB22B90A37DD92F53161A76EE34B97383E68FB883D683B7/* Not supported: data(FF 25 02 02 02 02 4C 8B 15 FB 3F 00 00 FF 25 FD 3F 00 00) */;
internal static readonly __StaticArrayInitTypeSize_003D13 _4136F0A2BA848BD55F29F31133F78F4D594325EA7E5AC3652F9BBE219A0D6BF6/* Not supported: data(4C 8B 15 FB 3F 00 00 FF 25 02 02 02 02) */;
internal static readonly __StaticArrayInitTypeSize_003D12 _486D5777753C6CA37679B858C47120A8DFFC0A184AF7C5EBDC8DF58DDE238E14/* Not supported: data(E8 00 02 FF 02 FF 02 FF 02 FF 5E 00) */;
internal static readonly __StaticArrayInitTypeSize_003D66 _515E6FAFF15DEF5864366DF19ADF4AE474B76C6463F432464F7312069F227443/* Not supported: data(48 00 85 00 C9 00 74 00 00 FF 48 00 8B 00 01 00 49 00 00 FF 00 FF 00 FF 00 FF 00 FF 00 FF 00 FF 00 FF 00 FF 49 00 3B 00 C2 00 74 00 00 FF 48 00 B8 00 02 FF 02 FF 02 FF 02 FF 02 FF 02 FF 02 FF 02 FF) */;
internal static readonly __StaticArrayInitTypeSize_003D19 _64A1CE71E7611E1196ECCD081EED0243B16EADAC673FE76ABBBDC5813B33C1E8/* Not supported: data(F0 FF 00 00 00 00 00 00 00 00 FF FF F0 FF FF 00 00 00 00) */;
internal static readonly __StaticArrayInitTypeSize_003D12 _73C5DDF3D1B50EF2973C84F1EFCA106392E86FA50DDE4274E0BB239D13E1B2C4/* Not supported: data(B8 00 00 00 00 B9 00 00 00 00 FF E1) */;
internal static readonly __StaticArrayInitTypeSize_003D16 _74A985B69363DA038C215EBDC95C2DEC71CCAA7EEE4FE7A349F688FAF77E0BAE/* Not supported: data(48 8B 49 08 48 8B 01 FF A0 55 55 55 55 CC CC CC) */;
internal static readonly __StaticArrayInitTypeSize_003D12 _8414C9ACCC6CAC2333C96A5A5DC464C0E6DBE568068318ABA594E10C4585A5FC/* Not supported: data(E8 00 02 FF 02 FF 02 FF 02 FF CC 00) */;
internal static readonly __StaticArrayInitTypeSize_003D27 _93CA2598B856E56332AD9FCA06FE4AE26CF02ED7D37C8C4D790EDFC53FB9DA81/* Not supported: data(2F 74 6D 70 2F 6D 6D 2D 65 78 68 65 6C 70 65 72 2E 73 6F 2E 58 58 58 58 58 58 00) */;
internal static readonly __StaticArrayInitTypeSize_003D24 _9A97788FFCA1A5AAFD2A31A42C4C9CA76CF4FF59CB4AC7E5DBBBD10B33793FFA/* Not supported: data(FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 00 00 FF FF FF FF FF FF) */;
internal static readonly __StaticArrayInitTypeSize_003D12 _9C8D4649EBE69770F6443FF6405385B6EE93B8D7B430CF5E7191FF869587F23C/* Not supported: data(E9 00 02 FF 02 FF 02 FF 02 FF 5F 00) */;
internal static readonly __StaticArrayInitTypeSize_003D16 _9E97865F268FBCC4190687A97D459D7AC09DFADED381B251528E2041FFE51739/* Not supported: data(00 00 00 00 01 00 00 00 03 00 00 00 02 00 00 00) */;
internal static readonly __StaticArrayInitTypeSize_003D19 A95E8D7DCA08CE4FA177AB74F30B979C33FF00EE013F03E6F2E244F5B93AFC9C/* Not supported: data(FF FF 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF) */;
internal static readonly __StaticArrayInitTypeSize_003D16 BAED642339816AFFB3FE8719792D0E4CE82F12DB72B7373D244EAA65445800FE/* Not supported: data(00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00) */;
internal static readonly __StaticArrayInitTypeSize_003D12 CC04E60244F11264BA0C35EBD477099E8E811C6267800C53D2A16574265D530A/* Not supported: data(8B 49 04 8B 01 FF A0 55 55 55 55 CC) */;
internal static readonly __StaticArrayInitTypeSize_003D30 D2092BB8F0B39D69931977DCBF7F6A48B0209B37F74004EAB47FA529ECBA7672/* Not supported: data(2F 74 6D 70 2F 6D 6D 2D 65 78 68 65 6C 70 65 72 2E 64 79 6C 69 62 2E 58 58 58 58 58 58 00) */;
internal static readonly __StaticArrayInitTypeSize_003D16 D9C90CF616457576F11A613E9AD2971E209CEC74951E276569E1097ADFEB26D5/* Not supported: data(48 8B 7F 08 48 8B 07 FF A0 55 55 55 55 CC CC CC) */;
internal static readonly __StaticArrayInitTypeSize_003D24 DD455B255FEA8FB07C13201AFD90CCA95D44404F7E4E907591F15937439AAC5D/* Not supported: data(48 8B 05 F9 0F 00 00 66 FF 08 74 06 FF 25 02 02 02 02 FF 25 F8 0F 00 00) */;
internal static readonly __StaticArrayInitTypeSize_003D13 E0B74C191B43F7CA650292EC688A3D4FC903BD02AE6C2F9CE334155FCF80BBC0/* Not supported: data(FF FF FF FF FF FF FF FF FF 00 00 00 00) */;
internal static readonly __StaticArrayInitTypeSize_003D24 EC1FC66EB8E0DCEC61EEDAF5B5384E45ECDA8B56E37FDAAD9442AD5601732850/* Not supported: data(48 8B 05 F9 3F 00 00 66 FF 08 74 06 FF 25 02 02 02 02 FF 25 F8 3F 00 00) */;
internal static readonly __StaticArrayInitTypeSize_003D23 EF9D8CA5CBE169340066F08BC0A169D0779A59AAABC495DBE3FF991D03534944/* Not supported: data(48 B8 00 00 00 00 00 00 00 00 49 BA 00 00 00 00 00 00 00 00 41 FF E2) */;
internal static readonly __StaticArrayInitTypeSize_003D27 FBAEBE8D1D36A1131B667CBBFD90F9F47AFBFDE1C60700E663CC2209AB5545BB/* Not supported: data(40 B8 00 00 00 00 00 00 00 00 66 FF 00 74 00 48 B8 02 02 02 02 02 02 02 02 FF E0) */;
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,22 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[CompilerGenerated]
internal sealed class _003C22576685_002D8ec8_002D4022_002D94e7_002Db5a630de7c65_003E_003CPrivateImplementationDetails_003E
{
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 32)]
private struct __StaticArrayInitTypeSize_003D32
{
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 288)]
private struct __StaticArrayInitTypeSize_003D288
{
}
internal static readonly __StaticArrayInitTypeSize_003D32 _3BF63951626584EB1653F9B8DBB590A5EE1EAE1135A904B9317C3773896DF076/* Not supported: data(00 01 1C 02 1D 0E 18 03 1E 16 14 0F 19 11 04 08 1F 1B 0D 17 15 13 10 07 1A 0C 12 06 0B 05 0A 09) */;
internal static readonly __StaticArrayInitTypeSize_003D32 _4BCD43D478B9229AB7A13406353712C7944B60348C36B4D0E6B789D10F697652/* Not supported: data(00 09 01 0A 0D 15 02 1D 0B 0E 10 12 16 19 03 1E 08 0C 14 1C 0F 11 18 07 13 1B 17 06 1A 05 04 1F) */;
internal static readonly __StaticArrayInitTypeSize_003D288 _74BCD6ED20AF2231F2BB1CDE814C5F4FF48E54BAC46029EEF90DDF4A208E2B20/* Not supported: data(03 00 00 00 07 00 00 00 0B 00 00 00 11 00 00 00 17 00 00 00 1D 00 00 00 25 00 00 00 2F 00 00 00 3B 00 00 00 47 00 00 00 59 00 00 00 6B 00 00 00 83 00 00 00 A3 00 00 00 C5 00 00 00 EF 00 00 00 25 01 00 00 61 01 00 00 AF 01 00 00 09 02 00 00 77 02 00 00 F9 02 00 00 97 03 00 00 4F 04 00 00 2F 05 00 00 3D 06 00 00 8B 07 00 00 1D 09 00 00 F1 0A 00 00 2B 0D 00 00 D1 0F 00 00 FD 12 00 00 CF 16 00 00 65 1B 00 00 E3 20 00 00 77 27 00 00 6F 2F 00 00 FF 38 00 00 6F 44 00 00 1F 52 00 00 8D 62 00 00 55 76 00 00 01 8E 00 00 6B AA 00 00 89 CC 00 00 83 F5 00 00 A7 26 01 00 9B 61 01 00 57 A8 01 00 3B FD 01 00 15 63 02 00 67 DD 02 00 1B 70 03 00 23 20 04 00 61 F3 04 00 ED F0 05 00 25 21 07 00 31 8E 08 00 3B 44 0A 00 EB 51 0C 00 C1 C8 0E 00 BF BD 11 00 3F 4A 15 00 4F 8C 19 00 67 A8 1E 00 19 CA 24 00 C1 25 2C 00 1B FA 34 00 8F 92 3F 00 87 49 4C 00 6F 8B 5B 00 89 DA 6D 00) */;
}

View file

@ -0,0 +1,20 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[CompilerGenerated]
internal sealed class _003Caf0d1164_002D9314_002D453b_002D9f85_002D2452dca9647d_003E_003CPrivateImplementationDetails_003E
{
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 32)]
private struct __StaticArrayInitTypeSize_003D32
{
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 288)]
private struct __StaticArrayInitTypeSize_003D288
{
}
internal static readonly __StaticArrayInitTypeSize_003D32 _5AED05C00AFE7BD291FD4D73F5200B7919E0D05C0D63DBCB4A210F74133093BD/* Not supported: data(4D 69 63 72 6F 73 6F 66 74 20 43 2F 43 2B 2B 20 4D 53 46 20 37 2E 30 30 0D 0A 1A 44 53 00 00 00) */;
internal static readonly __StaticArrayInitTypeSize_003D288 _74BCD6ED20AF2231F2BB1CDE814C5F4FF48E54BAC46029EEF90DDF4A208E2B20/* Not supported: data(03 00 00 00 07 00 00 00 0B 00 00 00 11 00 00 00 17 00 00 00 1D 00 00 00 25 00 00 00 2F 00 00 00 3B 00 00 00 47 00 00 00 59 00 00 00 6B 00 00 00 83 00 00 00 A3 00 00 00 C5 00 00 00 EF 00 00 00 25 01 00 00 61 01 00 00 AF 01 00 00 09 02 00 00 77 02 00 00 F9 02 00 00 97 03 00 00 4F 04 00 00 2F 05 00 00 3D 06 00 00 8B 07 00 00 1D 09 00 00 F1 0A 00 00 2B 0D 00 00 D1 0F 00 00 FD 12 00 00 CF 16 00 00 65 1B 00 00 E3 20 00 00 77 27 00 00 6F 2F 00 00 FF 38 00 00 6F 44 00 00 1F 52 00 00 8D 62 00 00 55 76 00 00 01 8E 00 00 6B AA 00 00 89 CC 00 00 83 F5 00 00 A7 26 01 00 9B 61 01 00 57 A8 01 00 3B FD 01 00 15 63 02 00 67 DD 02 00 1B 70 03 00 23 20 04 00 61 F3 04 00 ED F0 05 00 25 21 07 00 31 8E 08 00 3B 44 0A 00 EB 51 0C 00 C1 C8 0E 00 BF BD 11 00 3F 4A 15 00 4F 8C 19 00 67 A8 1E 00 19 CA 24 00 C1 25 2C 00 1B FA 34 00 8F 92 3F 00 87 49 4C 00 6F 8B 5B 00 89 DA 6D 00) */;
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,13 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[CompilerGenerated]
internal sealed class _003Ced09f563_002D263f_002D4886_002Db794_002D3686c333f40f_003E_003CPrivateImplementationDetails_003E
{
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 20)]
private struct __StaticArrayInitTypeSize_003D20
{
}
internal static readonly __StaticArrayInitTypeSize_003D20 _4F6ADDC9659D6FB90FE94B6688A79F2A1FA8D36EC43F8F3E1D9B6528C448A384/* Not supported: data(01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00) */;
}

28
0Harmony/0Harmony.csproj Normal file
View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>0Harmony</AssemblyName>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<TargetFramework>net48</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<LangVersion>12.0</LangVersion>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<None Remove="exhelper_linux_x86_64.so" />
<None Remove="exhelper_macos_x86_64.dylib" />
<None Remove="ILRepack.List" />
<EmbeddedResource Include="exhelper_linux_x86_64.so" LogicalName="exhelper_linux_x86_64.so" />
<EmbeddedResource Include="exhelper_macos_x86_64.dylib" LogicalName="exhelper_macos_x86_64.dylib" />
<EmbeddedResource Include="ILRepack.List" LogicalName="ILRepack.List" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Core">
<HintPath>../../System.Core.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

6
0Harmony/Consts.cs Normal file
View file

@ -0,0 +1,6 @@
internal static class Consts
{
public const string AssemblyName = "Mono.Cecil";
public const string PublicKey = "00240000048000009400000006020000002400005253413100040000010001002b5c9f7f04346c324a3176f8d3ee823bbf2d60efdbc35f86fd9e65ea3e6cd11bcdcba3a353e55133c8ac5c4caaba581b2c6dfff2cc2d0edc43959ddb86b973300a479a82419ef489c3225f1fe429a708507bd515835160e10bc743d20ca33ab9570cfd68d479fcf0bc797a763bec5d1000f0159ef619e709d915975e87beebaf";
}

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype"><value>text/microsoft-resx</value></resheader><resheader name="version"><value>1.3</value></resheader><resheader name="reader"><value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value></resheader><resheader name="writer"><value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value></resheader><data name="ArgumentException_ValueTupleIncorrectType" xml:space="preserve"><value>The parameter should be a ValueTuple type of appropriate arity.</value></data>
<data name="ArgumentException_ValueTupleLastArgumentNotAValueTuple" xml:space="preserve"><value>The TRest type argument of ValueTuple`8 must be a ValueTuple.</value></data>
</root>

View file

@ -0,0 +1,5 @@
namespace FxResources.System.ValueTuple;
internal static class SR
{
}

View file

@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace HarmonyLib;
internal class AccessCache
{
internal enum MemberType
{
Any,
Static,
Instance
}
private const BindingFlags BasicFlags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty;
private static readonly Dictionary<MemberType, BindingFlags> declaredOnlyBindingFlags = new Dictionary<MemberType, BindingFlags>
{
{
MemberType.Any,
BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty
},
{
MemberType.Instance,
BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty
},
{
MemberType.Static,
BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty
}
};
private readonly Dictionary<Type, Dictionary<string, FieldInfo>> declaredFields = new Dictionary<Type, Dictionary<string, FieldInfo>>();
private readonly Dictionary<Type, Dictionary<string, PropertyInfo>> declaredProperties = new Dictionary<Type, Dictionary<string, PropertyInfo>>();
private readonly Dictionary<Type, Dictionary<string, Dictionary<int, MethodBase>>> declaredMethods = new Dictionary<Type, Dictionary<string, Dictionary<int, MethodBase>>>();
private readonly Dictionary<Type, Dictionary<string, FieldInfo>> inheritedFields = new Dictionary<Type, Dictionary<string, FieldInfo>>();
private readonly Dictionary<Type, Dictionary<string, PropertyInfo>> inheritedProperties = new Dictionary<Type, Dictionary<string, PropertyInfo>>();
private readonly Dictionary<Type, Dictionary<string, Dictionary<int, MethodBase>>> inheritedMethods = new Dictionary<Type, Dictionary<string, Dictionary<int, MethodBase>>>();
private static T Get<T>(Dictionary<Type, Dictionary<string, T>> dict, Type type, string name, Func<T> fetcher)
{
lock (dict)
{
if (!dict.TryGetValue(type, out var value))
{
value = (dict[type] = new Dictionary<string, T>());
}
if (!value.TryGetValue(name, out var value2))
{
value2 = (value[name] = fetcher());
}
return value2;
}
}
private static T Get<T>(Dictionary<Type, Dictionary<string, Dictionary<int, T>>> dict, Type type, string name, Type[] arguments, Func<T> fetcher)
{
lock (dict)
{
if (!dict.TryGetValue(type, out var value))
{
value = (dict[type] = new Dictionary<string, Dictionary<int, T>>());
}
if (!value.TryGetValue(name, out var value2))
{
value2 = (value[name] = new Dictionary<int, T>());
}
int key = AccessTools.CombinedHashCode(arguments);
if (!value2.TryGetValue(key, out var value3))
{
value3 = (value2[key] = fetcher());
}
return value3;
}
}
internal FieldInfo GetFieldInfo(Type type, string name, MemberType memberType = MemberType.Any, bool declaredOnly = false)
{
FieldInfo fieldInfo = Get(declaredFields, type, name, () => type.GetField(name, declaredOnlyBindingFlags[memberType]));
if ((object)fieldInfo == null && !declaredOnly)
{
fieldInfo = Get(inheritedFields, type, name, () => AccessTools.FindIncludingBaseTypes(type, (Type t) => t.GetField(name, AccessTools.all)));
}
return fieldInfo;
}
internal PropertyInfo GetPropertyInfo(Type type, string name, MemberType memberType = MemberType.Any, bool declaredOnly = false)
{
PropertyInfo propertyInfo = Get(declaredProperties, type, name, () => type.GetProperty(name, declaredOnlyBindingFlags[memberType]));
if ((object)propertyInfo == null && !declaredOnly)
{
propertyInfo = Get(inheritedProperties, type, name, () => AccessTools.FindIncludingBaseTypes(type, (Type t) => t.GetProperty(name, AccessTools.all)));
}
return propertyInfo;
}
internal MethodBase GetMethodInfo(Type type, string name, Type[] arguments, MemberType memberType = MemberType.Any, bool declaredOnly = false)
{
MethodBase methodBase = Get(declaredMethods, type, name, arguments, () => type.GetMethod(name, declaredOnlyBindingFlags[memberType], null, arguments, null));
if ((object)methodBase == null && !declaredOnly)
{
methodBase = Get(inheritedMethods, type, name, arguments, () => AccessTools.Method(type, name, arguments));
}
return methodBase;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,245 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
namespace HarmonyLib;
public static class AccessToolsExtensions
{
public static IEnumerable<Type> InnerTypes(this Type type)
{
return AccessTools.InnerTypes(type);
}
public static T FindIncludingBaseTypes<T>(this Type type, Func<Type, T> func) where T : class
{
return AccessTools.FindIncludingBaseTypes(type, func);
}
public static T FindIncludingInnerTypes<T>(this Type type, Func<Type, T> func) where T : class
{
return AccessTools.FindIncludingInnerTypes(type, func);
}
public static FieldInfo DeclaredField(this Type type, string name)
{
return AccessTools.DeclaredField(type, name);
}
public static FieldInfo Field(this Type type, string name)
{
return AccessTools.Field(type, name);
}
public static FieldInfo DeclaredField(this Type type, int idx)
{
return AccessTools.DeclaredField(type, idx);
}
public static PropertyInfo DeclaredProperty(this Type type, string name)
{
return AccessTools.DeclaredProperty(type, name);
}
public static PropertyInfo DeclaredIndexer(this Type type, Type[] parameters = null)
{
return AccessTools.DeclaredIndexer(type, parameters);
}
public static MethodInfo DeclaredPropertyGetter(this Type type, string name)
{
return AccessTools.DeclaredPropertyGetter(type, name);
}
public static MethodInfo DeclaredIndexerGetter(this Type type, Type[] parameters = null)
{
return AccessTools.DeclaredIndexerGetter(type, parameters);
}
public static MethodInfo DeclaredPropertySetter(this Type type, string name)
{
return AccessTools.DeclaredPropertySetter(type, name);
}
public static MethodInfo DeclaredIndexerSetter(this Type type, Type[] parameters)
{
return AccessTools.DeclaredIndexerSetter(type, parameters);
}
public static PropertyInfo Property(this Type type, string name)
{
return AccessTools.Property(type, name);
}
public static PropertyInfo Indexer(this Type type, Type[] parameters = null)
{
return AccessTools.Indexer(type, parameters);
}
public static MethodInfo PropertyGetter(this Type type, string name)
{
return AccessTools.PropertyGetter(type, name);
}
public static MethodInfo IndexerGetter(this Type type, Type[] parameters = null)
{
return AccessTools.IndexerGetter(type, parameters);
}
public static MethodInfo PropertySetter(this Type type, string name)
{
return AccessTools.PropertySetter(type, name);
}
public static MethodInfo IndexerSetter(this Type type, Type[] parameters = null)
{
return AccessTools.IndexerSetter(type, parameters);
}
public static MethodInfo DeclaredMethod(this Type type, string name, Type[] parameters = null, Type[] generics = null)
{
return AccessTools.DeclaredMethod(type, name, parameters, generics);
}
public static MethodInfo Method(this Type type, string name, Type[] parameters = null, Type[] generics = null)
{
return AccessTools.Method(type, name, parameters, generics);
}
public static List<string> GetMethodNames(this Type type)
{
return AccessTools.GetMethodNames(type);
}
public static List<string> GetFieldNames(this Type type)
{
return AccessTools.GetFieldNames(type);
}
public static List<string> GetPropertyNames(this Type type)
{
return AccessTools.GetPropertyNames(type);
}
public static ConstructorInfo DeclaredConstructor(this Type type, Type[] parameters = null, bool searchForStatic = false)
{
return AccessTools.DeclaredConstructor(type, parameters, searchForStatic);
}
public static ConstructorInfo Constructor(this Type type, Type[] parameters = null, bool searchForStatic = false)
{
return AccessTools.Constructor(type, parameters, searchForStatic);
}
public static List<ConstructorInfo> GetDeclaredConstructors(this Type type, bool? searchForStatic = null)
{
return AccessTools.GetDeclaredConstructors(type, searchForStatic);
}
public static List<MethodInfo> GetDeclaredMethods(this Type type)
{
return AccessTools.GetDeclaredMethods(type);
}
public static List<PropertyInfo> GetDeclaredProperties(this Type type)
{
return AccessTools.GetDeclaredProperties(type);
}
public static List<FieldInfo> GetDeclaredFields(this Type type)
{
return AccessTools.GetDeclaredFields(type);
}
public static Type Inner(this Type type, string name)
{
return AccessTools.Inner(type, name);
}
public static Type FirstInner(this Type type, Func<Type, bool> predicate)
{
return AccessTools.FirstInner(type, predicate);
}
public static MethodInfo FirstMethod(this Type type, Func<MethodInfo, bool> predicate)
{
return AccessTools.FirstMethod(type, predicate);
}
public static ConstructorInfo FirstConstructor(this Type type, Func<ConstructorInfo, bool> predicate)
{
return AccessTools.FirstConstructor(type, predicate);
}
public static PropertyInfo FirstProperty(this Type type, Func<PropertyInfo, bool> predicate)
{
return AccessTools.FirstProperty(type, predicate);
}
public static AccessTools.FieldRef<object, F> FieldRefAccess<F>(this Type type, string fieldName)
{
return AccessTools.FieldRefAccess<F>(type, fieldName);
}
public static ref F StaticFieldRefAccess<F>(this Type type, string fieldName)
{
return ref AccessTools.StaticFieldRefAccess<F>(type, fieldName);
}
public static void ThrowMissingMemberException(this Type type, params string[] names)
{
AccessTools.ThrowMissingMemberException(type, names);
}
public static object GetDefaultValue(this Type type)
{
return AccessTools.GetDefaultValue(type);
}
public static object CreateInstance(this Type type)
{
return AccessTools.CreateInstance(type);
}
public static bool IsStruct(this Type type)
{
return AccessTools.IsStruct(type);
}
public static bool IsClass(this Type type)
{
return AccessTools.IsClass(type);
}
public static bool IsValue(this Type type)
{
return AccessTools.IsValue(type);
}
public static bool IsInteger(this Type type)
{
return AccessTools.IsInteger(type);
}
public static bool IsFloatingPoint(this Type type)
{
return AccessTools.IsFloatingPoint(type);
}
public static bool IsNumber(this Type type)
{
return AccessTools.IsNumber(type);
}
public static bool IsVoid(this Type type)
{
return AccessTools.IsVoid(type);
}
[EditorBrowsable(EditorBrowsableState.Never)]
public static bool IsStatic(this Type type)
{
return AccessTools.IsStatic(type);
}
}

View file

@ -0,0 +1,9 @@
namespace HarmonyLib;
public enum ArgumentType
{
Normal,
Ref,
Out,
Pointer
}

View file

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace HarmonyLib;
internal class AttributePatch
{
private static readonly HarmonyPatchType[] allPatchTypes = new HarmonyPatchType[5]
{
HarmonyPatchType.Prefix,
HarmonyPatchType.Postfix,
HarmonyPatchType.Transpiler,
HarmonyPatchType.Finalizer,
HarmonyPatchType.ReversePatch
};
internal HarmonyMethod info;
internal HarmonyPatchType? type;
internal static AttributePatch Create(MethodInfo patch)
{
if ((object)patch == null)
{
throw new NullReferenceException("Patch method cannot be null");
}
object[] customAttributes = patch.GetCustomAttributes(inherit: true);
string name = patch.Name;
HarmonyPatchType? patchType = GetPatchType(name, customAttributes);
if (!patchType.HasValue)
{
return null;
}
if (patchType != HarmonyPatchType.ReversePatch && !patch.IsStatic)
{
throw new ArgumentException("Patch method " + patch.FullDescription() + " must be static");
}
List<HarmonyMethod> attributes = (from harmonyInfo in customAttributes.Where((object attr) => attr.GetType().BaseType.FullName == PatchTools.harmonyAttributeFullName).Select(delegate(object attr)
{
FieldInfo fieldInfo = AccessTools.Field(attr.GetType(), "info");
return fieldInfo.GetValue(attr);
})
select AccessTools.MakeDeepCopy<HarmonyMethod>(harmonyInfo)).ToList();
HarmonyMethod harmonyMethod = HarmonyMethod.Merge(attributes);
harmonyMethod.method = patch;
return new AttributePatch
{
info = harmonyMethod,
type = patchType
};
}
private static HarmonyPatchType? GetPatchType(string methodName, object[] allAttributes)
{
HashSet<string> hashSet = new HashSet<string>(from attr in allAttributes
select attr.GetType().FullName into name
where name.StartsWith("Harmony")
select name);
HarmonyPatchType? result = null;
HarmonyPatchType[] array = allPatchTypes;
for (int num = 0; num < array.Length; num++)
{
HarmonyPatchType value = array[num];
string text = value.ToString();
if (text == methodName || hashSet.Contains("HarmonyLib.Harmony" + text))
{
result = value;
break;
}
}
return result;
}
}

View file

@ -0,0 +1,92 @@
using System;
namespace HarmonyLib;
internal class ByteBuffer
{
internal byte[] buffer;
internal int position;
internal ByteBuffer(byte[] buffer)
{
this.buffer = buffer;
}
internal byte ReadByte()
{
CheckCanRead(1);
return buffer[position++];
}
internal byte[] ReadBytes(int length)
{
CheckCanRead(length);
byte[] array = new byte[length];
Buffer.BlockCopy(buffer, position, array, 0, length);
position += length;
return array;
}
internal short ReadInt16()
{
CheckCanRead(2);
short result = (short)(buffer[position] | (buffer[position + 1] << 8));
position += 2;
return result;
}
internal int ReadInt32()
{
CheckCanRead(4);
int result = buffer[position] | (buffer[position + 1] << 8) | (buffer[position + 2] << 16) | (buffer[position + 3] << 24);
position += 4;
return result;
}
internal long ReadInt64()
{
CheckCanRead(8);
uint num = (uint)(buffer[position] | (buffer[position + 1] << 8) | (buffer[position + 2] << 16) | (buffer[position + 3] << 24));
uint num2 = (uint)(buffer[position + 4] | (buffer[position + 5] << 8) | (buffer[position + 6] << 16) | (buffer[position + 7] << 24));
long result = (long)(((ulong)num2 << 32) | num);
position += 8;
return result;
}
internal float ReadSingle()
{
if (!BitConverter.IsLittleEndian)
{
byte[] array = ReadBytes(4);
Array.Reverse((Array)array);
return BitConverter.ToSingle(array, 0);
}
CheckCanRead(4);
float result = BitConverter.ToSingle(buffer, position);
position += 4;
return result;
}
internal double ReadDouble()
{
if (!BitConverter.IsLittleEndian)
{
byte[] array = ReadBytes(8);
Array.Reverse((Array)array);
return BitConverter.ToDouble(array, 0);
}
CheckCanRead(8);
double result = BitConverter.ToDouble(buffer, position);
position += 8;
return result;
}
private void CheckCanRead(int count)
{
if (position + count > buffer.Length)
{
throw new ArgumentOutOfRangeException("count", $"position({position}) + count({count}) > buffer.Length({buffer.Length})");
}
}
}

2273
0Harmony/HarmonyLib/Code.cs Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,298 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using MonoMod.Utils;
namespace HarmonyLib;
public class CodeInstruction
{
internal static class State
{
internal static readonly Dictionary<int, Delegate> closureCache = new Dictionary<int, Delegate>();
}
public OpCode opcode;
public object operand;
public List<Label> labels = new List<Label>();
public List<ExceptionBlock> blocks = new List<ExceptionBlock>();
internal CodeInstruction()
{
}
public CodeInstruction(OpCode opcode, object operand = null)
{
this.opcode = opcode;
this.operand = operand;
}
public CodeInstruction(CodeInstruction instruction)
{
opcode = instruction.opcode;
operand = instruction.operand;
List<Label> list = instruction.labels;
List<Label> list2 = new List<Label>(list.Count);
list2.AddRange(list);
labels = list2;
List<ExceptionBlock> list3 = instruction.blocks;
List<ExceptionBlock> list4 = new List<ExceptionBlock>(list3.Count);
list4.AddRange(list3);
blocks = list4;
}
public CodeInstruction Clone()
{
return new CodeInstruction(this)
{
labels = new List<Label>(),
blocks = new List<ExceptionBlock>()
};
}
public CodeInstruction Clone(OpCode opcode)
{
CodeInstruction codeInstruction = Clone();
codeInstruction.opcode = opcode;
return codeInstruction;
}
public CodeInstruction Clone(object operand)
{
CodeInstruction codeInstruction = Clone();
codeInstruction.operand = operand;
return codeInstruction;
}
public static CodeInstruction Call(Type type, string name, Type[] parameters = null, Type[] generics = null)
{
MethodInfo methodInfo = AccessTools.Method(type, name, parameters, generics);
if ((object)methodInfo == null)
{
throw new ArgumentException($"No method found for type={type}, name={name}, parameters={parameters.Description()}, generics={generics.Description()}");
}
return new CodeInstruction(OpCodes.Call, methodInfo);
}
public static CodeInstruction Call(string typeColonMethodname, Type[] parameters = null, Type[] generics = null)
{
MethodInfo methodInfo = AccessTools.Method(typeColonMethodname, parameters, generics);
if ((object)methodInfo == null)
{
throw new ArgumentException($"No method found for {typeColonMethodname}, parameters={parameters.Description()}, generics={generics.Description()}");
}
return new CodeInstruction(OpCodes.Call, methodInfo);
}
public static CodeInstruction Call(Expression<Action> expression)
{
return new CodeInstruction(OpCodes.Call, SymbolExtensions.GetMethodInfo(expression));
}
public static CodeInstruction Call<T>(Expression<Action<T>> expression)
{
return new CodeInstruction(OpCodes.Call, SymbolExtensions.GetMethodInfo(expression));
}
public static CodeInstruction Call<T, TResult>(Expression<Func<T, TResult>> expression)
{
return new CodeInstruction(OpCodes.Call, SymbolExtensions.GetMethodInfo(expression));
}
public static CodeInstruction Call(LambdaExpression expression)
{
return new CodeInstruction(OpCodes.Call, SymbolExtensions.GetMethodInfo(expression));
}
public static CodeInstruction CallClosure<T>(T closure) where T : Delegate
{
if (closure.Method.IsStatic && closure.Target == null)
{
return new CodeInstruction(OpCodes.Call, closure.Method);
}
Type[] array = (from x in closure.Method.GetParameters()
select x.ParameterType).ToArray();
DynamicMethodDefinition dynamicMethodDefinition = new DynamicMethodDefinition(closure.Method.Name, closure.Method.ReturnType, array);
ILGenerator iLGenerator = dynamicMethodDefinition.GetILGenerator();
Type type = closure.Target.GetType();
if (closure.Target != null && type.GetFields().Any((FieldInfo x) => !x.IsStatic))
{
int count = State.closureCache.Count;
State.closureCache[count] = closure;
iLGenerator.Emit(OpCodes.Ldsfld, AccessTools.Field(typeof(Transpilers), "closureCache"));
iLGenerator.Emit(OpCodes.Ldc_I4, count);
iLGenerator.Emit(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Dictionary<int, Delegate>), "Item"));
}
else
{
if (closure.Target == null)
{
iLGenerator.Emit(OpCodes.Ldnull);
}
else
{
iLGenerator.Emit(OpCodes.Newobj, AccessTools.FirstConstructor(type, (ConstructorInfo x) => !x.IsStatic && x.GetParameters().Length == 0));
}
iLGenerator.Emit(OpCodes.Ldftn, closure.Method);
iLGenerator.Emit(OpCodes.Newobj, AccessTools.Constructor(typeof(T), new Type[2]
{
typeof(object),
typeof(IntPtr)
}));
}
for (int num = 0; num < array.Length; num++)
{
iLGenerator.Emit(OpCodes.Ldarg, num);
}
iLGenerator.Emit(OpCodes.Callvirt, AccessTools.Method(typeof(T), "Invoke"));
iLGenerator.Emit(OpCodes.Ret);
return new CodeInstruction(OpCodes.Call, dynamicMethodDefinition.Generate());
}
public static CodeInstruction LoadField(Type type, string name, bool useAddress = false)
{
FieldInfo fieldInfo = AccessTools.Field(type, name);
if ((object)fieldInfo == null)
{
throw new ArgumentException($"No field found for {type} and {name}");
}
return new CodeInstruction((!useAddress) ? (fieldInfo.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld) : (fieldInfo.IsStatic ? OpCodes.Ldsflda : OpCodes.Ldflda), fieldInfo);
}
public static CodeInstruction StoreField(Type type, string name)
{
FieldInfo fieldInfo = AccessTools.Field(type, name);
if ((object)fieldInfo == null)
{
throw new ArgumentException($"No field found for {type} and {name}");
}
return new CodeInstruction(fieldInfo.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, fieldInfo);
}
public static CodeInstruction LoadLocal(int index, bool useAddress = false)
{
if (useAddress)
{
if (index < 256)
{
return new CodeInstruction(OpCodes.Ldloca_S, Convert.ToByte(index));
}
return new CodeInstruction(OpCodes.Ldloca, index);
}
if (index == 0)
{
return new CodeInstruction(OpCodes.Ldloc_0);
}
if (index == 1)
{
return new CodeInstruction(OpCodes.Ldloc_1);
}
if (index == 2)
{
return new CodeInstruction(OpCodes.Ldloc_2);
}
if (index == 3)
{
return new CodeInstruction(OpCodes.Ldloc_3);
}
if (index < 256)
{
return new CodeInstruction(OpCodes.Ldloc_S, Convert.ToByte(index));
}
return new CodeInstruction(OpCodes.Ldloc, index);
}
public static CodeInstruction StoreLocal(int index)
{
if (index == 0)
{
return new CodeInstruction(OpCodes.Stloc_0);
}
if (index == 1)
{
return new CodeInstruction(OpCodes.Stloc_1);
}
if (index == 2)
{
return new CodeInstruction(OpCodes.Stloc_2);
}
if (index == 3)
{
return new CodeInstruction(OpCodes.Stloc_3);
}
if (index < 256)
{
return new CodeInstruction(OpCodes.Stloc_S, Convert.ToByte(index));
}
return new CodeInstruction(OpCodes.Stloc, index);
}
public static CodeInstruction LoadArgument(int index, bool useAddress = false)
{
if (useAddress)
{
if (index < 256)
{
return new CodeInstruction(OpCodes.Ldarga_S, Convert.ToByte(index));
}
return new CodeInstruction(OpCodes.Ldarga, index);
}
if (index == 0)
{
return new CodeInstruction(OpCodes.Ldarg_0);
}
if (index == 1)
{
return new CodeInstruction(OpCodes.Ldarg_1);
}
if (index == 2)
{
return new CodeInstruction(OpCodes.Ldarg_2);
}
if (index == 3)
{
return new CodeInstruction(OpCodes.Ldarg_3);
}
if (index < 256)
{
return new CodeInstruction(OpCodes.Ldarg_S, Convert.ToByte(index));
}
return new CodeInstruction(OpCodes.Ldarg, index);
}
public static CodeInstruction StoreArgument(int index)
{
if (index < 256)
{
return new CodeInstruction(OpCodes.Starg_S, Convert.ToByte(index));
}
return new CodeInstruction(OpCodes.Starg, index);
}
public override string ToString()
{
List<string> list = new List<string>();
foreach (Label label in labels)
{
list.Add($"Label{label.GetHashCode()}");
}
foreach (ExceptionBlock block in blocks)
{
list.Add("EX_" + block.blockType.ToString().Replace("Block", ""));
}
string text = ((list.Count > 0) ? (" [" + string.Join(", ", list.ToArray()) + "]") : "");
string text2 = Emitter.FormatArgument(operand);
if (text2.Length > 0)
{
text2 = " " + text2;
}
OpCode opCode = opcode;
return opCode.ToString() + text2 + text;
}
}

View file

@ -0,0 +1,514 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Reflection.Emit;
namespace HarmonyLib;
public static class CodeInstructionExtensions
{
internal static readonly HashSet<OpCode> opcodesCalling = new HashSet<OpCode>
{
OpCodes.Call,
OpCodes.Callvirt
};
internal static readonly HashSet<OpCode> opcodesLoadingLocalByAddress = new HashSet<OpCode>
{
OpCodes.Ldloca_S,
OpCodes.Ldloca
};
internal static readonly HashSet<OpCode> opcodesLoadingLocalNormal = new HashSet<OpCode>
{
OpCodes.Ldloc_0,
OpCodes.Ldloc_1,
OpCodes.Ldloc_2,
OpCodes.Ldloc_3,
OpCodes.Ldloc_S,
OpCodes.Ldloc
};
internal static readonly HashSet<OpCode> opcodesStoringLocal = new HashSet<OpCode>
{
OpCodes.Stloc_0,
OpCodes.Stloc_1,
OpCodes.Stloc_2,
OpCodes.Stloc_3,
OpCodes.Stloc_S,
OpCodes.Stloc
};
internal static readonly HashSet<OpCode> opcodesLoadingArgumentByAddress = new HashSet<OpCode>
{
OpCodes.Ldarga_S,
OpCodes.Ldarga
};
internal static readonly HashSet<OpCode> opcodesLoadingArgumentNormal = new HashSet<OpCode>
{
OpCodes.Ldarg_0,
OpCodes.Ldarg_1,
OpCodes.Ldarg_2,
OpCodes.Ldarg_3,
OpCodes.Ldarg_S,
OpCodes.Ldarg
};
internal static readonly HashSet<OpCode> opcodesStoringArgument = new HashSet<OpCode>
{
OpCodes.Starg_S,
OpCodes.Starg
};
internal static readonly HashSet<OpCode> opcodesBranching = new HashSet<OpCode>
{
OpCodes.Br_S,
OpCodes.Brfalse_S,
OpCodes.Brtrue_S,
OpCodes.Beq_S,
OpCodes.Bge_S,
OpCodes.Bgt_S,
OpCodes.Ble_S,
OpCodes.Blt_S,
OpCodes.Bne_Un_S,
OpCodes.Bge_Un_S,
OpCodes.Bgt_Un_S,
OpCodes.Ble_Un_S,
OpCodes.Blt_Un_S,
OpCodes.Br,
OpCodes.Brfalse,
OpCodes.Brtrue,
OpCodes.Beq,
OpCodes.Bge,
OpCodes.Bgt,
OpCodes.Ble,
OpCodes.Blt,
OpCodes.Bne_Un,
OpCodes.Bge_Un,
OpCodes.Bgt_Un,
OpCodes.Ble_Un,
OpCodes.Blt_Un
};
private static readonly HashSet<OpCode> constantLoadingCodes = new HashSet<OpCode>
{
OpCodes.Ldc_I4_M1,
OpCodes.Ldc_I4_0,
OpCodes.Ldc_I4_1,
OpCodes.Ldc_I4_2,
OpCodes.Ldc_I4_3,
OpCodes.Ldc_I4_4,
OpCodes.Ldc_I4_5,
OpCodes.Ldc_I4_6,
OpCodes.Ldc_I4_7,
OpCodes.Ldc_I4_8,
OpCodes.Ldc_I4,
OpCodes.Ldc_I4_S,
OpCodes.Ldc_I8,
OpCodes.Ldc_R4,
OpCodes.Ldc_R8
};
public static bool IsValid(this OpCode code)
{
return code.Size > 0;
}
public static bool OperandIs(this CodeInstruction code, object value)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
if (code.operand == null)
{
return false;
}
Type type = value.GetType();
Type type2 = code.operand.GetType();
if (AccessTools.IsInteger(type) && AccessTools.IsNumber(type2))
{
return Convert.ToInt64(code.operand) == Convert.ToInt64(value);
}
if (AccessTools.IsFloatingPoint(type) && AccessTools.IsNumber(type2))
{
return Convert.ToDouble(code.operand) == Convert.ToDouble(value);
}
return object.Equals(code.operand, value);
}
[EditorBrowsable(EditorBrowsableState.Never)]
public static bool OperandIs(this CodeInstruction code, MemberInfo value)
{
if ((object)value == null)
{
throw new ArgumentNullException("value");
}
return object.Equals(code.operand, value);
}
public static bool Is(this CodeInstruction code, OpCode opcode, object operand)
{
if (code.opcode == opcode)
{
return code.OperandIs(operand);
}
return false;
}
[EditorBrowsable(EditorBrowsableState.Never)]
public static bool Is(this CodeInstruction code, OpCode opcode, MemberInfo operand)
{
if (code.opcode == opcode)
{
return code.OperandIs(operand);
}
return false;
}
public static bool IsLdarg(this CodeInstruction code, int? n = null)
{
if ((!n.HasValue || n.Value == 0) && code.opcode == OpCodes.Ldarg_0)
{
return true;
}
if ((!n.HasValue || n.Value == 1) && code.opcode == OpCodes.Ldarg_1)
{
return true;
}
if ((!n.HasValue || n.Value == 2) && code.opcode == OpCodes.Ldarg_2)
{
return true;
}
if ((!n.HasValue || n.Value == 3) && code.opcode == OpCodes.Ldarg_3)
{
return true;
}
if (code.opcode == OpCodes.Ldarg && (!n.HasValue || n.Value == Convert.ToInt32(code.operand)))
{
return true;
}
if (code.opcode == OpCodes.Ldarg_S && (!n.HasValue || n.Value == Convert.ToInt32(code.operand)))
{
return true;
}
return false;
}
public static bool IsLdarga(this CodeInstruction code, int? n = null)
{
if (code.opcode != OpCodes.Ldarga && code.opcode != OpCodes.Ldarga_S)
{
return false;
}
if (n.HasValue)
{
return n.Value == Convert.ToInt32(code.operand);
}
return true;
}
public static bool IsStarg(this CodeInstruction code, int? n = null)
{
if (code.opcode != OpCodes.Starg && code.opcode != OpCodes.Starg_S)
{
return false;
}
if (n.HasValue)
{
return n.Value == Convert.ToInt32(code.operand);
}
return true;
}
public static bool IsLdloc(this CodeInstruction code, LocalBuilder variable = null)
{
if (!opcodesLoadingLocalNormal.Contains(code.opcode) && !opcodesLoadingLocalByAddress.Contains(code.opcode))
{
return false;
}
if (variable != null)
{
return object.Equals(variable, code.operand);
}
return true;
}
public static bool IsStloc(this CodeInstruction code, LocalBuilder variable = null)
{
if (!opcodesStoringLocal.Contains(code.opcode))
{
return false;
}
if (variable != null)
{
return object.Equals(variable, code.operand);
}
return true;
}
public static bool Branches(this CodeInstruction code, out Label? label)
{
if (opcodesBranching.Contains(code.opcode))
{
label = (Label)code.operand;
return true;
}
label = null;
return false;
}
public static bool Calls(this CodeInstruction code, MethodInfo method)
{
if ((object)method == null)
{
throw new ArgumentNullException("method");
}
if (code.opcode != OpCodes.Call && code.opcode != OpCodes.Callvirt)
{
return false;
}
return object.Equals(code.operand, method);
}
public static bool LoadsConstant(this CodeInstruction code)
{
return constantLoadingCodes.Contains(code.opcode);
}
public static bool LoadsConstant(this CodeInstruction code, long number)
{
OpCode opcode = code.opcode;
if (number == -1 && opcode == OpCodes.Ldc_I4_M1)
{
return true;
}
if (number == 0L && opcode == OpCodes.Ldc_I4_0)
{
return true;
}
if (number == 1 && opcode == OpCodes.Ldc_I4_1)
{
return true;
}
if (number == 2 && opcode == OpCodes.Ldc_I4_2)
{
return true;
}
if (number == 3 && opcode == OpCodes.Ldc_I4_3)
{
return true;
}
if (number == 4 && opcode == OpCodes.Ldc_I4_4)
{
return true;
}
if (number == 5 && opcode == OpCodes.Ldc_I4_5)
{
return true;
}
if (number == 6 && opcode == OpCodes.Ldc_I4_6)
{
return true;
}
if (number == 7 && opcode == OpCodes.Ldc_I4_7)
{
return true;
}
if (number == 8 && opcode == OpCodes.Ldc_I4_8)
{
return true;
}
if (opcode != OpCodes.Ldc_I4 && opcode != OpCodes.Ldc_I4_S && opcode != OpCodes.Ldc_I8)
{
return false;
}
return Convert.ToInt64(code.operand) == number;
}
public static bool LoadsConstant(this CodeInstruction code, double number)
{
if (code.opcode != OpCodes.Ldc_R4 && code.opcode != OpCodes.Ldc_R8)
{
return false;
}
double num = Convert.ToDouble(code.operand);
return num == number;
}
public static bool LoadsConstant(this CodeInstruction code, Enum e)
{
return code.LoadsConstant(Convert.ToInt64(e));
}
public static bool LoadsConstant(this CodeInstruction code, string str)
{
if (code.opcode != OpCodes.Ldstr)
{
return false;
}
string text = Convert.ToString(code.operand);
return text == str;
}
public static bool LoadsField(this CodeInstruction code, FieldInfo field, bool byAddress = false)
{
if ((object)field == null)
{
throw new ArgumentNullException("field");
}
OpCode opCode = (field.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld);
if (!byAddress && code.opcode == opCode && object.Equals(code.operand, field))
{
return true;
}
OpCode opCode2 = (field.IsStatic ? OpCodes.Ldsflda : OpCodes.Ldflda);
if (byAddress && code.opcode == opCode2 && object.Equals(code.operand, field))
{
return true;
}
return false;
}
public static bool StoresField(this CodeInstruction code, FieldInfo field)
{
if ((object)field == null)
{
throw new ArgumentNullException("field");
}
OpCode opCode = (field.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld);
if (code.opcode == opCode)
{
return object.Equals(code.operand, field);
}
return false;
}
public static int LocalIndex(this CodeInstruction code)
{
if (code.opcode == OpCodes.Ldloc_0 || code.opcode == OpCodes.Stloc_0)
{
return 0;
}
if (code.opcode == OpCodes.Ldloc_1 || code.opcode == OpCodes.Stloc_1)
{
return 1;
}
if (code.opcode == OpCodes.Ldloc_2 || code.opcode == OpCodes.Stloc_2)
{
return 2;
}
if (code.opcode == OpCodes.Ldloc_3 || code.opcode == OpCodes.Stloc_3)
{
return 3;
}
if (code.opcode == OpCodes.Ldloc_S || code.opcode == OpCodes.Ldloc)
{
return Convert.ToInt32(code.operand);
}
if (code.opcode == OpCodes.Stloc_S || code.opcode == OpCodes.Stloc)
{
return Convert.ToInt32(code.operand);
}
if (code.opcode == OpCodes.Ldloca_S || code.opcode == OpCodes.Ldloca)
{
return Convert.ToInt32(code.operand);
}
throw new ArgumentException("Instruction is not a load or store", "code");
}
public static int ArgumentIndex(this CodeInstruction code)
{
if (code.opcode == OpCodes.Ldarg_0)
{
return 0;
}
if (code.opcode == OpCodes.Ldarg_1)
{
return 1;
}
if (code.opcode == OpCodes.Ldarg_2)
{
return 2;
}
if (code.opcode == OpCodes.Ldarg_3)
{
return 3;
}
if (code.opcode == OpCodes.Ldarg_S || code.opcode == OpCodes.Ldarg)
{
return Convert.ToInt32(code.operand);
}
if (code.opcode == OpCodes.Starg_S || code.opcode == OpCodes.Starg)
{
return Convert.ToInt32(code.operand);
}
if (code.opcode == OpCodes.Ldarga_S || code.opcode == OpCodes.Ldarga)
{
return Convert.ToInt32(code.operand);
}
throw new ArgumentException("Instruction is not a load or store", "code");
}
public static CodeInstruction WithLabels(this CodeInstruction code, params Label[] labels)
{
code.labels.AddRange(labels);
return code;
}
public static CodeInstruction WithLabels(this CodeInstruction code, IEnumerable<Label> labels)
{
code.labels.AddRange(labels);
return code;
}
public static List<Label> ExtractLabels(this CodeInstruction code)
{
List<Label> result = new List<Label>(code.labels);
code.labels.Clear();
return result;
}
public static CodeInstruction MoveLabelsTo(this CodeInstruction code, CodeInstruction other)
{
other.WithLabels(code.ExtractLabels());
return code;
}
public static CodeInstruction MoveLabelsFrom(this CodeInstruction code, CodeInstruction other)
{
return code.WithLabels(other.ExtractLabels());
}
public static CodeInstruction WithBlocks(this CodeInstruction code, params ExceptionBlock[] blocks)
{
code.blocks.AddRange(blocks);
return code;
}
public static CodeInstruction WithBlocks(this CodeInstruction code, IEnumerable<ExceptionBlock> blocks)
{
code.blocks.AddRange(blocks);
return code;
}
public static List<ExceptionBlock> ExtractBlocks(this CodeInstruction code)
{
List<ExceptionBlock> result = new List<ExceptionBlock>(code.blocks);
code.blocks.Clear();
return result;
}
public static CodeInstruction MoveBlocksTo(this CodeInstruction code, CodeInstruction other)
{
other.WithBlocks(code.ExtractBlocks());
return code;
}
public static CodeInstruction MoveBlocksFrom(this CodeInstruction code, CodeInstruction other)
{
return code.WithBlocks(other.ExtractBlocks());
}
}

View file

@ -0,0 +1,11 @@
using System.Collections.Generic;
namespace HarmonyLib;
public static class CodeInstructionsExtensions
{
public static bool Matches(this IEnumerable<CodeInstruction> instructions, CodeMatch[] matches)
{
return new CodeMatcher(instructions).MatchStartForward(matches).IsValid;
}
}

View file

@ -0,0 +1,315 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
namespace HarmonyLib;
public class CodeMatch : CodeInstruction
{
public string name;
public HashSet<OpCode> opcodeSet = new HashSet<OpCode>();
public List<object> operands = new List<object>();
public List<int> jumpsFrom = new List<int>();
public List<int> jumpsTo = new List<int>();
public Func<CodeInstruction, bool> predicate;
[Obsolete("Use opcodeSet instead")]
public List<OpCode> opcodes
{
get
{
HashSet<OpCode> hashSet = opcodeSet;
List<OpCode> list = new List<OpCode>(hashSet.Count);
list.AddRange(hashSet);
return list;
}
set
{
opcodeSet = new HashSet<OpCode>(value);
}
}
internal CodeMatch Set(object operand, string name)
{
if (base.operand == null)
{
base.operand = operand;
}
if (operand != null)
{
operands.Add(operand);
}
if (this.name == null)
{
this.name = name;
}
return this;
}
internal CodeMatch Set(OpCode opcode, object operand, string name)
{
base.opcode = opcode;
opcodeSet.Add(opcode);
if (base.operand == null)
{
base.operand = operand;
}
if (operand != null)
{
operands.Add(operand);
}
if (this.name == null)
{
this.name = name;
}
return this;
}
public CodeMatch(OpCode? opcode = null, object operand = null, string name = null)
{
if (opcode.HasValue)
{
OpCode item = (base.opcode = opcode.GetValueOrDefault());
opcodeSet.Add(item);
}
if (operand != null)
{
operands.Add(operand);
}
base.operand = operand;
this.name = name;
}
public static CodeMatch WithOpcodes(HashSet<OpCode> opcodes, object operand = null, string name = null)
{
return new CodeMatch(null, operand, name)
{
opcodeSet = opcodes
};
}
public CodeMatch(Expression<Action> expression, string name = null)
{
opcodeSet.UnionWith(CodeInstructionExtensions.opcodesCalling);
operand = SymbolExtensions.GetMethodInfo(expression);
if (operand != null)
{
operands.Add(operand);
}
this.name = name;
}
public CodeMatch(LambdaExpression expression, string name = null)
{
opcodeSet.UnionWith(CodeInstructionExtensions.opcodesCalling);
operand = SymbolExtensions.GetMethodInfo(expression);
if (operand != null)
{
operands.Add(operand);
}
this.name = name;
}
public CodeMatch(CodeInstruction instruction, string name = null)
: this(instruction.opcode, instruction.operand, name)
{
}
public CodeMatch(Func<CodeInstruction, bool> predicate, string name = null)
{
this.predicate = predicate;
this.name = name;
}
internal bool Matches(List<CodeInstruction> codes, CodeInstruction instruction)
{
if (predicate != null)
{
return predicate(instruction);
}
if (opcodeSet.Count > 0 && !opcodeSet.Contains(instruction.opcode))
{
return false;
}
if (operands.Count > 0 && !operands.Contains(instruction.operand))
{
return false;
}
if (labels.Count > 0 && !labels.Intersect(instruction.labels).Any())
{
return false;
}
if (blocks.Count > 0 && !blocks.Intersect(instruction.blocks).Any())
{
return false;
}
if (jumpsFrom.Count > 0 && !jumpsFrom.Select((int index) => codes[index].operand).OfType<Label>().Intersect(instruction.labels)
.Any())
{
return false;
}
if (jumpsTo.Count > 0)
{
object obj = instruction.operand;
if (obj == null || obj.GetType() != typeof(Label))
{
return false;
}
Label label = (Label)obj;
IEnumerable<int> second = from idx in Enumerable.Range(0, codes.Count)
where codes[idx].labels.Contains(label)
select idx;
if (!jumpsTo.Intersect(second).Any())
{
return false;
}
}
return true;
}
public static CodeMatch IsLdarg(int? n = null)
{
return new CodeMatch((CodeInstruction instruction) => instruction.IsLdarg(n));
}
public static CodeMatch IsLdarga(int? n = null)
{
return new CodeMatch((CodeInstruction instruction) => instruction.IsLdarga(n));
}
public static CodeMatch IsStarg(int? n = null)
{
return new CodeMatch((CodeInstruction instruction) => instruction.IsStarg(n));
}
public static CodeMatch IsLdloc(LocalBuilder variable = null)
{
return new CodeMatch((CodeInstruction instruction) => instruction.IsLdloc(variable));
}
public static CodeMatch IsStloc(LocalBuilder variable = null)
{
return new CodeMatch((CodeInstruction instruction) => instruction.IsStloc(variable));
}
public static CodeMatch Calls(MethodInfo method)
{
return WithOpcodes(CodeInstructionExtensions.opcodesCalling, method);
}
public static CodeMatch LoadsConstant()
{
return new CodeMatch((CodeInstruction instruction) => instruction.LoadsConstant());
}
public static CodeMatch LoadsConstant(long number)
{
return new CodeMatch((CodeInstruction instruction) => instruction.LoadsConstant(number));
}
public static CodeMatch LoadsConstant(double number)
{
return new CodeMatch((CodeInstruction instruction) => instruction.LoadsConstant(number));
}
public static CodeMatch LoadsConstant(Enum e)
{
return new CodeMatch((CodeInstruction instruction) => instruction.LoadsConstant(e));
}
public static CodeMatch LoadsConstant(string str)
{
return new CodeMatch((CodeInstruction instruction) => instruction.LoadsConstant(str));
}
public static CodeMatch LoadsField(FieldInfo field, bool byAddress = false)
{
return new CodeMatch((CodeInstruction instruction) => instruction.LoadsField(field, byAddress));
}
public static CodeMatch StoresField(FieldInfo field)
{
return new CodeMatch((CodeInstruction instruction) => instruction.StoresField(field));
}
public static CodeMatch Calls(Expression<Action> expression)
{
return new CodeMatch(expression);
}
public static CodeMatch Calls(LambdaExpression expression)
{
return new CodeMatch(expression);
}
public static CodeMatch LoadsLocal(bool useAddress = false, string name = null)
{
return WithOpcodes(useAddress ? CodeInstructionExtensions.opcodesLoadingLocalByAddress : CodeInstructionExtensions.opcodesLoadingLocalNormal, null, name);
}
public static CodeMatch StoresLocal(string name = null)
{
return WithOpcodes(CodeInstructionExtensions.opcodesStoringLocal, null, name);
}
public static CodeMatch LoadsArgument(bool useAddress = false, string name = null)
{
return WithOpcodes(useAddress ? CodeInstructionExtensions.opcodesLoadingArgumentByAddress : CodeInstructionExtensions.opcodesLoadingArgumentNormal, null, name);
}
public static CodeMatch StoresArgument(string name = null)
{
return WithOpcodes(CodeInstructionExtensions.opcodesStoringArgument, null, name);
}
public static CodeMatch Branches(string name = null)
{
return WithOpcodes(CodeInstructionExtensions.opcodesBranching, null, name);
}
public override string ToString()
{
string text = "[";
if (name != null)
{
text = text + name + ": ";
}
if (opcodeSet.Count > 0)
{
text = text + "opcodes=" + opcodeSet.Join() + " ";
}
if (operands.Count > 0)
{
text = text + "operands=" + operands.Join() + " ";
}
if (labels.Count > 0)
{
text = text + "labels=" + labels.Join() + " ";
}
if (blocks.Count > 0)
{
text = text + "blocks=" + blocks.Join() + " ";
}
if (jumpsFrom.Count > 0)
{
text = text + "jumpsFrom=" + jumpsFrom.Join() + " ";
}
if (jumpsTo.Count > 0)
{
text = text + "jumpsTo=" + jumpsTo.Join() + " ";
}
if (predicate != null)
{
text += "predicate=yes ";
}
return text.TrimEnd(Array.Empty<char>()) + "]";
}
}

View file

@ -0,0 +1,513 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
namespace HarmonyLib;
public class CodeMatcher
{
private delegate CodeMatcher MatchDelegate();
private readonly ILGenerator generator;
private readonly List<CodeInstruction> codes = new List<CodeInstruction>();
private Dictionary<string, CodeInstruction> lastMatches = new Dictionary<string, CodeInstruction>();
private string lastError;
private MatchDelegate lastMatchCall;
public int Pos { get; private set; } = -1;
public int Length => codes.Count;
public bool IsValid
{
get
{
if (Pos >= 0)
{
return Pos < Length;
}
return false;
}
}
public bool IsInvalid
{
get
{
if (Pos >= 0)
{
return Pos >= Length;
}
return true;
}
}
public int Remaining => Length - Math.Max(0, Pos);
public ref OpCode Opcode => ref codes[Pos].opcode;
public ref object Operand => ref codes[Pos].operand;
public ref List<Label> Labels => ref codes[Pos].labels;
public ref List<ExceptionBlock> Blocks => ref codes[Pos].blocks;
public CodeInstruction Instruction => codes[Pos];
private void FixStart()
{
Pos = Math.Max(0, Pos);
}
private void SetOutOfBounds(int direction)
{
Pos = ((direction > 0) ? Length : (-1));
}
public CodeMatcher()
{
}
public CodeMatcher(IEnumerable<CodeInstruction> instructions, ILGenerator generator = null)
{
this.generator = generator;
codes = instructions.Select((CodeInstruction c) => new CodeInstruction(c)).ToList();
}
public CodeMatcher Clone()
{
return new CodeMatcher(codes, generator)
{
Pos = Pos,
lastMatches = lastMatches,
lastError = lastError,
lastMatchCall = lastMatchCall
};
}
public CodeInstruction InstructionAt(int offset)
{
return codes[Pos + offset];
}
public List<CodeInstruction> Instructions()
{
return codes;
}
public IEnumerable<CodeInstruction> InstructionEnumeration()
{
return codes.AsEnumerable();
}
public List<CodeInstruction> Instructions(int count)
{
return (from c in codes.GetRange(Pos, count)
select new CodeInstruction(c)).ToList();
}
public List<CodeInstruction> InstructionsInRange(int start, int end)
{
List<CodeInstruction> list = codes;
if (start > end)
{
int num = start;
start = end;
end = num;
}
list = list.GetRange(start, end - start + 1);
return list.Select((CodeInstruction c) => new CodeInstruction(c)).ToList();
}
public List<CodeInstruction> InstructionsWithOffsets(int startOffset, int endOffset)
{
return InstructionsInRange(Pos + startOffset, Pos + endOffset);
}
public List<Label> DistinctLabels(IEnumerable<CodeInstruction> instructions)
{
return instructions.SelectMany((CodeInstruction instruction) => instruction.labels).Distinct().ToList();
}
public bool ReportFailure(MethodBase method, Action<string> logger)
{
if (IsValid)
{
return false;
}
string value = lastError ?? "Unexpected code";
logger($"{value} in {method}");
return true;
}
public CodeMatcher ThrowIfInvalid(string explanation)
{
if (explanation == null)
{
throw new ArgumentNullException("explanation");
}
if (IsInvalid)
{
throw new InvalidOperationException(explanation + " - Current state is invalid");
}
return this;
}
public CodeMatcher ThrowIfNotMatch(string explanation, params CodeMatch[] matches)
{
ThrowIfInvalid(explanation);
if (!MatchSequence(Pos, matches))
{
throw new InvalidOperationException(explanation + " - Match failed");
}
return this;
}
private void ThrowIfNotMatch(string explanation, int direction, CodeMatch[] matches)
{
ThrowIfInvalid(explanation);
int pos = Pos;
try
{
if (Match(matches, direction, useEnd: false).IsInvalid)
{
throw new InvalidOperationException(explanation + " - Match failed");
}
}
finally
{
Pos = pos;
}
}
public CodeMatcher ThrowIfNotMatchForward(string explanation, params CodeMatch[] matches)
{
ThrowIfNotMatch(explanation, 1, matches);
return this;
}
public CodeMatcher ThrowIfNotMatchBack(string explanation, params CodeMatch[] matches)
{
ThrowIfNotMatch(explanation, -1, matches);
return this;
}
public CodeMatcher ThrowIfFalse(string explanation, Func<CodeMatcher, bool> stateCheckFunc)
{
if (stateCheckFunc == null)
{
throw new ArgumentNullException("stateCheckFunc");
}
ThrowIfInvalid(explanation);
if (!stateCheckFunc(this))
{
throw new InvalidOperationException(explanation + " - Check function returned false");
}
return this;
}
public CodeMatcher SetInstruction(CodeInstruction instruction)
{
codes[Pos] = instruction;
return this;
}
public CodeMatcher SetInstructionAndAdvance(CodeInstruction instruction)
{
SetInstruction(instruction);
Pos++;
return this;
}
public CodeMatcher Set(OpCode opcode, object operand)
{
Opcode = opcode;
Operand = operand;
return this;
}
public CodeMatcher SetAndAdvance(OpCode opcode, object operand)
{
Set(opcode, operand);
Pos++;
return this;
}
public CodeMatcher SetOpcodeAndAdvance(OpCode opcode)
{
Opcode = opcode;
Pos++;
return this;
}
public CodeMatcher SetOperandAndAdvance(object operand)
{
Operand = operand;
Pos++;
return this;
}
public CodeMatcher DeclareLocal(Type variableType, out LocalBuilder localVariable)
{
localVariable = generator.DeclareLocal(variableType);
return this;
}
public CodeMatcher DefineLabel(out Label label)
{
label = generator.DefineLabel();
return this;
}
public CodeMatcher CreateLabel(out Label label)
{
label = generator.DefineLabel();
Labels.Add(label);
return this;
}
public CodeMatcher CreateLabelAt(int position, out Label label)
{
label = generator.DefineLabel();
AddLabelsAt(position, new Label[1] { label });
return this;
}
public CodeMatcher CreateLabelWithOffsets(int offset, out Label label)
{
label = generator.DefineLabel();
return AddLabelsAt(Pos + offset, new Label[1] { label });
}
public CodeMatcher AddLabels(IEnumerable<Label> labels)
{
Labels.AddRange(labels);
return this;
}
public CodeMatcher AddLabelsAt(int position, IEnumerable<Label> labels)
{
codes[position].labels.AddRange(labels);
return this;
}
public CodeMatcher SetJumpTo(OpCode opcode, int destination, out Label label)
{
CreateLabelAt(destination, out label);
return Set(opcode, label);
}
public CodeMatcher Insert(params CodeInstruction[] instructions)
{
codes.InsertRange(Pos, instructions);
return this;
}
public CodeMatcher Insert(IEnumerable<CodeInstruction> instructions)
{
codes.InsertRange(Pos, instructions);
return this;
}
public CodeMatcher InsertBranch(OpCode opcode, int destination)
{
CreateLabelAt(destination, out var label);
codes.Insert(Pos, new CodeInstruction(opcode, label));
return this;
}
public CodeMatcher InsertAndAdvance(params CodeInstruction[] instructions)
{
foreach (CodeInstruction codeInstruction in instructions)
{
Insert(codeInstruction);
Pos++;
}
return this;
}
public CodeMatcher InsertAndAdvance(IEnumerable<CodeInstruction> instructions)
{
foreach (CodeInstruction instruction in instructions)
{
InsertAndAdvance(instruction);
}
return this;
}
public CodeMatcher InsertBranchAndAdvance(OpCode opcode, int destination)
{
InsertBranch(opcode, destination);
Pos++;
return this;
}
public CodeMatcher RemoveInstruction()
{
codes.RemoveAt(Pos);
return this;
}
public CodeMatcher RemoveInstructions(int count)
{
codes.RemoveRange(Pos, count);
return this;
}
public CodeMatcher RemoveInstructionsInRange(int start, int end)
{
if (start > end)
{
int num = start;
start = end;
end = num;
}
codes.RemoveRange(start, end - start + 1);
return this;
}
public CodeMatcher RemoveInstructionsWithOffsets(int startOffset, int endOffset)
{
return RemoveInstructionsInRange(Pos + startOffset, Pos + endOffset);
}
public CodeMatcher Advance(int offset)
{
Pos += offset;
if (!IsValid)
{
SetOutOfBounds(offset);
}
return this;
}
public CodeMatcher Start()
{
Pos = 0;
return this;
}
public CodeMatcher End()
{
Pos = Length - 1;
return this;
}
public CodeMatcher SearchForward(Func<CodeInstruction, bool> predicate)
{
return Search(predicate, 1);
}
public CodeMatcher SearchBackwards(Func<CodeInstruction, bool> predicate)
{
return Search(predicate, -1);
}
private CodeMatcher Search(Func<CodeInstruction, bool> predicate, int direction)
{
FixStart();
while (IsValid && !predicate(Instruction))
{
Pos += direction;
}
lastError = (IsInvalid ? $"Cannot find {predicate}" : null);
return this;
}
public CodeMatcher MatchStartForward(params CodeMatch[] matches)
{
return Match(matches, 1, useEnd: false);
}
public CodeMatcher MatchEndForward(params CodeMatch[] matches)
{
return Match(matches, 1, useEnd: true);
}
public CodeMatcher MatchStartBackwards(params CodeMatch[] matches)
{
return Match(matches, -1, useEnd: false);
}
public CodeMatcher MatchEndBackwards(params CodeMatch[] matches)
{
return Match(matches, -1, useEnd: true);
}
private CodeMatcher Match(CodeMatch[] matches, int direction, bool useEnd)
{
lastMatchCall = delegate
{
FixStart();
while (IsValid)
{
if (MatchSequence(Pos, matches))
{
if (useEnd)
{
Pos += matches.Length - 1;
}
break;
}
Pos += direction;
}
lastError = (IsInvalid ? ("Cannot find " + matches.Join()) : null);
return this;
};
return lastMatchCall();
}
public CodeMatcher Repeat(Action<CodeMatcher> matchAction, Action<string> notFoundAction = null)
{
int num = 0;
if (lastMatchCall == null)
{
throw new InvalidOperationException("No previous Match operation - cannot repeat");
}
while (IsValid)
{
matchAction(this);
lastMatchCall();
num++;
}
lastMatchCall = null;
if (num == 0)
{
notFoundAction?.Invoke(lastError);
}
return this;
}
public CodeInstruction NamedMatch(string name)
{
return lastMatches[name];
}
private bool MatchSequence(int start, CodeMatch[] matches)
{
if (start < 0)
{
return false;
}
lastMatches = new Dictionary<string, CodeInstruction>();
foreach (CodeMatch codeMatch in matches)
{
if (start >= Length || !codeMatch.Matches(codes, codes[start]))
{
return false;
}
if (codeMatch.name != null)
{
lastMatches.Add(codeMatch.name, codes[start]);
}
start++;
}
return true;
}
}

View file

@ -0,0 +1,337 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
namespace HarmonyLib;
internal class CodeTranspiler
{
private readonly IEnumerable<CodeInstruction> codeInstructions;
private readonly List<MethodInfo> transpilers = new List<MethodInfo>();
private static readonly Dictionary<OpCode, OpCode> allJumpCodes = new Dictionary<OpCode, OpCode>
{
{
OpCodes.Beq_S,
OpCodes.Beq
},
{
OpCodes.Bge_S,
OpCodes.Bge
},
{
OpCodes.Bge_Un_S,
OpCodes.Bge_Un
},
{
OpCodes.Bgt_S,
OpCodes.Bgt
},
{
OpCodes.Bgt_Un_S,
OpCodes.Bgt_Un
},
{
OpCodes.Ble_S,
OpCodes.Ble
},
{
OpCodes.Ble_Un_S,
OpCodes.Ble_Un
},
{
OpCodes.Blt_S,
OpCodes.Blt
},
{
OpCodes.Blt_Un_S,
OpCodes.Blt_Un
},
{
OpCodes.Bne_Un_S,
OpCodes.Bne_Un
},
{
OpCodes.Brfalse_S,
OpCodes.Brfalse
},
{
OpCodes.Brtrue_S,
OpCodes.Brtrue
},
{
OpCodes.Br_S,
OpCodes.Br
},
{
OpCodes.Leave_S,
OpCodes.Leave
}
};
internal CodeTranspiler(List<ILInstruction> ilInstructions)
{
codeInstructions = ilInstructions.Select((ILInstruction ilInstruction) => ilInstruction.GetCodeInstruction()).ToList().AsEnumerable();
}
internal void Add(MethodInfo transpiler)
{
transpilers.Add(transpiler);
}
internal static object ConvertInstruction(Type type, object instruction, out Dictionary<string, object> unassigned)
{
Dictionary<string, object> nonExisting = new Dictionary<string, object>();
object result = AccessTools.MakeDeepCopy(instruction, type, delegate(string namePath, Traverse trvSrc, Traverse trvDest)
{
object value = trvSrc.GetValue();
if (!trvDest.FieldExists())
{
nonExisting[namePath] = value;
return (object)null;
}
return (namePath == "opcode") ? ((object)ReplaceShortJumps((OpCode)value)) : value;
});
unassigned = nonExisting;
return result;
}
internal static bool ShouldAddExceptionInfo(object op, int opIndex, List<object> originalInstructions, List<object> newInstructions, Dictionary<object, Dictionary<string, object>> unassignedValues)
{
int num = originalInstructions.IndexOf(op);
if (num == -1)
{
return false;
}
if (!unassignedValues.TryGetValue(op, out var unassigned))
{
return false;
}
if (!unassigned.TryGetValue("blocks", out var blocksObject))
{
return false;
}
List<ExceptionBlock> blocks = blocksObject as List<ExceptionBlock>;
int num2 = newInstructions.Count((object instr) => instr == op);
if (num2 <= 1)
{
return true;
}
ExceptionBlock exceptionBlock = blocks.FirstOrDefault((ExceptionBlock block) => block.blockType != ExceptionBlockType.EndExceptionBlock);
ExceptionBlock exceptionBlock2 = blocks.FirstOrDefault((ExceptionBlock block) => block.blockType == ExceptionBlockType.EndExceptionBlock);
if (exceptionBlock != null && exceptionBlock2 == null)
{
object obj = originalInstructions.Skip(num + 1).FirstOrDefault(delegate(object instr)
{
if (!unassignedValues.TryGetValue(instr, out unassigned))
{
return false;
}
if (!unassigned.TryGetValue("blocks", out blocksObject))
{
return false;
}
blocks = blocksObject as List<ExceptionBlock>;
return blocks.Count > 0;
});
if (obj != null)
{
int num3 = num + 1;
int num4 = num3 + originalInstructions.Skip(num3).ToList().IndexOf(obj) - 1;
IEnumerable<object> first = originalInstructions.GetRange(num3, num4 - num3).Intersect(newInstructions);
obj = newInstructions.Skip(opIndex + 1).FirstOrDefault(delegate(object instr)
{
if (!unassignedValues.TryGetValue(instr, out unassigned))
{
return false;
}
if (!unassigned.TryGetValue("blocks", out blocksObject))
{
return false;
}
blocks = blocksObject as List<ExceptionBlock>;
return blocks.Count > 0;
});
if (obj != null)
{
num3 = opIndex + 1;
num4 = num3 + newInstructions.Skip(opIndex + 1).ToList().IndexOf(obj) - 1;
List<object> range = newInstructions.GetRange(num3, num4 - num3);
List<object> list = first.Except(range).ToList();
return list.Count == 0;
}
}
}
if (exceptionBlock == null && exceptionBlock2 != null)
{
object obj2 = originalInstructions.GetRange(0, num).LastOrDefault(delegate(object instr)
{
if (!unassignedValues.TryGetValue(instr, out unassigned))
{
return false;
}
if (!unassigned.TryGetValue("blocks", out blocksObject))
{
return false;
}
blocks = blocksObject as List<ExceptionBlock>;
return blocks.Count > 0;
});
if (obj2 != null)
{
int num5 = originalInstructions.GetRange(0, num).LastIndexOf(obj2);
int num6 = num;
IEnumerable<object> first2 = originalInstructions.GetRange(num5, num6 - num5).Intersect(newInstructions);
obj2 = newInstructions.GetRange(0, opIndex).LastOrDefault(delegate(object instr)
{
if (!unassignedValues.TryGetValue(instr, out unassigned))
{
return false;
}
if (!unassigned.TryGetValue("blocks", out blocksObject))
{
return false;
}
blocks = blocksObject as List<ExceptionBlock>;
return blocks.Count > 0;
});
if (obj2 != null)
{
num5 = newInstructions.GetRange(0, opIndex).LastIndexOf(obj2);
num6 = opIndex;
List<object> range2 = newInstructions.GetRange(num5, num6 - num5);
IEnumerable<object> source = first2.Except(range2);
return !source.Any();
}
}
}
return true;
}
internal static IEnumerable ConvertInstructionsAndUnassignedValues(Type type, IEnumerable enumerable, out Dictionary<object, Dictionary<string, object>> unassignedValues)
{
Assembly assembly = type.GetGenericTypeDefinition().Assembly;
Type type2 = assembly.GetType(typeof(List<>).FullName);
Type type3 = type.GetGenericArguments()[0];
Type type4 = type2.MakeGenericType(type3);
Type type5 = assembly.GetType(type4.FullName);
object obj = Activator.CreateInstance(type5);
MethodInfo method = obj.GetType().GetMethod("Add");
unassignedValues = new Dictionary<object, Dictionary<string, object>>();
foreach (object item in enumerable)
{
Dictionary<string, object> unassigned;
object obj2 = ConvertInstruction(type3, item, out unassigned);
unassignedValues.Add(obj2, unassigned);
method.Invoke(obj, new object[1] { obj2 });
}
return obj as IEnumerable;
}
internal static IEnumerable ConvertToOurInstructions(IEnumerable instructions, Type codeInstructionType, List<object> originalInstructions, Dictionary<object, Dictionary<string, object>> unassignedValues)
{
List<object> newInstructions = instructions.Cast<object>().ToList();
int index = -1;
foreach (object item in newInstructions)
{
index++;
object obj = AccessTools.MakeDeepCopy(item, codeInstructionType);
if (unassignedValues.TryGetValue(item, out var value))
{
bool flag = ShouldAddExceptionInfo(item, index, originalInstructions, newInstructions, unassignedValues);
Traverse traverse = Traverse.Create(obj);
foreach (KeyValuePair<string, object> item2 in value)
{
if (flag || item2.Key != "blocks")
{
traverse.Field(item2.Key).SetValue(item2.Value);
}
}
}
yield return obj;
}
}
private static bool IsCodeInstructionsParameter(Type type)
{
if (type.IsGenericType)
{
return type.GetGenericTypeDefinition().Name.StartsWith("IEnumerable", StringComparison.Ordinal);
}
return false;
}
internal static IEnumerable ConvertToGeneralInstructions(MethodInfo transpiler, IEnumerable enumerable, out Dictionary<object, Dictionary<string, object>> unassignedValues)
{
Type type = (from p in transpiler.GetParameters()
select p.ParameterType).FirstOrDefault((Type t) => IsCodeInstructionsParameter(t));
if (type == typeof(IEnumerable<CodeInstruction>))
{
unassignedValues = null;
return (enumerable as IList<CodeInstruction>) ?? ((enumerable as IEnumerable<CodeInstruction>) ?? enumerable.Cast<CodeInstruction>()).ToList();
}
return ConvertInstructionsAndUnassignedValues(type, enumerable, out unassignedValues);
}
internal static List<object> GetTranspilerCallParameters(ILGenerator generator, MethodInfo transpiler, MethodBase method, IEnumerable instructions)
{
List<object> parameter = new List<object>();
(from param in transpiler.GetParameters()
select param.ParameterType).Do(delegate(Type type)
{
if (type.IsAssignableFrom(typeof(ILGenerator)))
{
parameter.Add(generator);
}
else if (type.IsAssignableFrom(typeof(MethodBase)))
{
parameter.Add(method);
}
else if (IsCodeInstructionsParameter(type))
{
parameter.Add(instructions);
}
});
return parameter;
}
internal List<CodeInstruction> GetResult(ILGenerator generator, MethodBase method)
{
IEnumerable instructions = codeInstructions;
transpilers.ForEach(delegate(MethodInfo transpiler)
{
instructions = ConvertToGeneralInstructions(transpiler, instructions, out var unassignedValues);
List<object> originalInstructions = null;
if (unassignedValues != null)
{
originalInstructions = instructions.Cast<object>().ToList();
}
List<object> transpilerCallParameters = GetTranspilerCallParameters(generator, transpiler, method, instructions);
if (transpiler.Invoke(null, transpilerCallParameters.ToArray()) is IEnumerable enumerable)
{
instructions = enumerable;
}
if (unassignedValues != null)
{
instructions = ConvertToOurInstructions(instructions, typeof(CodeInstruction), originalInstructions, unassignedValues);
}
});
return (instructions as List<CodeInstruction>) ?? instructions.Cast<CodeInstruction>().ToList();
}
private static OpCode ReplaceShortJumps(OpCode opcode)
{
foreach (KeyValuePair<OpCode, OpCode> allJumpCode in allJumpCodes)
{
if (opcode == allJumpCode.Key)
{
return allJumpCode.Value;
}
}
return opcode;
}
}

View file

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace HarmonyLib;
public static class CollectionExtensions
{
public static void Do<T>(this IEnumerable<T> sequence, Action<T> action)
{
if (sequence != null)
{
IEnumerator<T> enumerator = sequence.GetEnumerator();
while (enumerator.MoveNext())
{
action(enumerator.Current);
}
}
}
public static void DoIf<T>(this IEnumerable<T> sequence, Func<T, bool> condition, Action<T> action)
{
sequence.Where(condition).Do(action);
}
public static IEnumerable<T> AddItem<T>(this IEnumerable<T> sequence, T item)
{
return (sequence ?? Array.Empty<T>()).Concat(new T[1] { item });
}
public static T[] AddToArray<T>(this T[] sequence, T item)
{
return sequence.AddItem(item).ToArray();
}
public static T[] AddRangeToArray<T>(this T[] sequence, T[] items)
{
return (sequence ?? Enumerable.Empty<T>()).Concat(items).ToArray();
}
internal static Dictionary<K, V> Merge<K, V>(this IEnumerable<KeyValuePair<K, V>> firstDict, params IEnumerable<KeyValuePair<K, V>>[] otherDicts)
{
Dictionary<K, V> dictionary = new Dictionary<K, V>();
foreach (KeyValuePair<K, V> item in firstDict)
{
dictionary[item.Key] = item.Value;
}
foreach (IEnumerable<KeyValuePair<K, V>> enumerable in otherDicts)
{
foreach (KeyValuePair<K, V> item2 in enumerable)
{
dictionary[item2.Key] = item2.Value;
}
}
return dictionary;
}
internal static Dictionary<K, V> TransformKeys<K, V>(this Dictionary<K, V> origDict, Func<K, K> transform)
{
Dictionary<K, V> dictionary = new Dictionary<K, V>();
foreach (KeyValuePair<K, V> item in origDict)
{
dictionary.Add(transform(item.Key), item.Value);
}
return dictionary;
}
}

View file

@ -0,0 +1,40 @@
using System;
using System.Reflection;
using System.Reflection.Emit;
namespace HarmonyLib;
public class DelegateTypeFactory
{
private readonly ModuleBuilder module;
private static int counter;
public DelegateTypeFactory()
{
counter++;
string name = $"HarmonyDTFAssembly{counter}";
AssemblyBuilder assemblyBuilder = PatchTools.DefineDynamicAssembly(name);
module = assemblyBuilder.DefineDynamicModule($"HarmonyDTFModule{counter}");
}
public Type CreateDelegateType(MethodInfo method)
{
TypeAttributes attr = TypeAttributes.Public | TypeAttributes.Sealed;
TypeBuilder typeBuilder = module.DefineType($"HarmonyDTFType{counter}", attr, typeof(MulticastDelegate));
ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.RTSpecialName, CallingConventions.Standard, new Type[2]
{
typeof(object),
typeof(IntPtr)
});
constructorBuilder.SetImplementationFlags(MethodImplAttributes.CodeTypeMask);
ParameterInfo[] parameters = method.GetParameters();
MethodBuilder methodBuilder = typeBuilder.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, method.ReturnType, parameters.Types());
methodBuilder.SetImplementationFlags(MethodImplAttributes.CodeTypeMask);
for (int i = 0; i < parameters.Length; i++)
{
methodBuilder.DefineParameter(i + 1, ParameterAttributes.None, parameters[i].Name);
}
return typeBuilder.CreateType();
}
}

View file

@ -0,0 +1,366 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using Mono.Cecil.Cil;
using MonoMod.Utils.Cil;
namespace HarmonyLib;
internal class Emitter
{
private readonly CecilILGenerator il;
private readonly Dictionary<int, CodeInstruction> instructions = new Dictionary<int, CodeInstruction>();
private readonly bool debug;
internal Emitter(ILGenerator il, bool debug)
{
this.il = il.GetProxiedShim<CecilILGenerator>();
this.debug = debug;
}
internal Dictionary<int, CodeInstruction> GetInstructions()
{
return instructions;
}
internal void AddInstruction(System.Reflection.Emit.OpCode opcode, object operand)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode, operand));
}
internal int CurrentPos()
{
return il.ILOffset;
}
internal static string CodePos(int offset)
{
return $"IL_{offset:X4}: ";
}
internal string CodePos()
{
return CodePos(CurrentPos());
}
internal void LogComment(string comment)
{
if (debug)
{
string str = $"{CodePos()}// {comment}";
FileLog.LogBuffered(str);
}
}
internal void LogIL(System.Reflection.Emit.OpCode opcode)
{
if (debug)
{
FileLog.LogBuffered($"{CodePos()}{opcode}");
}
}
internal void LogIL(System.Reflection.Emit.OpCode opcode, object arg, string extra = null)
{
if (debug)
{
string text = FormatArgument(arg, extra);
string text2 = ((text.Length > 0) ? " " : "");
string text3 = opcode.ToString();
if (opcode.FlowControl == System.Reflection.Emit.FlowControl.Branch || opcode.FlowControl == System.Reflection.Emit.FlowControl.Cond_Branch)
{
text3 += " =>";
}
text3 = text3.PadRight(10);
FileLog.LogBuffered($"{CodePos()}{text3}{text2}{text}");
}
}
internal void LogAllLocalVariables()
{
if (debug)
{
il.IL.Body.Variables.Do(delegate(VariableDefinition v)
{
string str = string.Format("{0}Local var {1}: {2}{3}", CodePos(0), v.Index, v.VariableType.FullName, v.IsPinned ? "(pinned)" : "");
FileLog.LogBuffered(str);
});
}
}
internal static string FormatArgument(object argument, string extra = null)
{
if (argument == null)
{
return "NULL";
}
Type type = argument.GetType();
if (argument is MethodBase member)
{
return member.FullDescription() + ((extra != null) ? (" " + extra) : "");
}
if (argument is FieldInfo fieldInfo)
{
return $"{fieldInfo.FieldType.FullDescription()} {fieldInfo.DeclaringType.FullDescription()}::{fieldInfo.Name}";
}
if (type == typeof(Label))
{
return $"Label{((Label)argument/*cast due to .constrained prefix*/).GetHashCode()}";
}
if (type == typeof(Label[]))
{
return "Labels" + string.Join(",", ((Label[])argument).Select((Label l) => l.GetHashCode().ToString()).ToArray());
}
if (type == typeof(LocalBuilder))
{
return $"{((LocalBuilder)argument).LocalIndex} ({((LocalBuilder)argument).LocalType})";
}
if (type == typeof(string))
{
return argument.ToString().ToLiteral();
}
return argument.ToString().Trim();
}
internal void MarkLabel(Label label)
{
if (debug)
{
FileLog.LogBuffered(CodePos() + FormatArgument(label));
}
il.MarkLabel(label);
}
internal void MarkBlockBefore(ExceptionBlock block, out Label? label)
{
label = null;
switch (block.blockType)
{
case ExceptionBlockType.BeginExceptionBlock:
if (debug)
{
FileLog.LogBuffered(".try");
FileLog.LogBuffered("{");
FileLog.ChangeIndent(1);
}
label = il.BeginExceptionBlock();
break;
case ExceptionBlockType.BeginCatchBlock:
if (debug)
{
LogIL(System.Reflection.Emit.OpCodes.Leave, new LeaveTry());
FileLog.ChangeIndent(-1);
FileLog.LogBuffered("} // end try");
FileLog.LogBuffered($".catch {block.catchType}");
FileLog.LogBuffered("{");
FileLog.ChangeIndent(1);
}
il.BeginCatchBlock(block.catchType);
break;
case ExceptionBlockType.BeginExceptFilterBlock:
if (debug)
{
LogIL(System.Reflection.Emit.OpCodes.Leave, new LeaveTry());
FileLog.ChangeIndent(-1);
FileLog.LogBuffered("} // end try");
FileLog.LogBuffered(".filter");
FileLog.LogBuffered("{");
FileLog.ChangeIndent(1);
}
il.BeginExceptFilterBlock();
break;
case ExceptionBlockType.BeginFaultBlock:
if (debug)
{
LogIL(System.Reflection.Emit.OpCodes.Leave, new LeaveTry());
FileLog.ChangeIndent(-1);
FileLog.LogBuffered("} // end try");
FileLog.LogBuffered(".fault");
FileLog.LogBuffered("{");
FileLog.ChangeIndent(1);
}
il.BeginFaultBlock();
break;
case ExceptionBlockType.BeginFinallyBlock:
if (debug)
{
LogIL(System.Reflection.Emit.OpCodes.Leave, new LeaveTry());
FileLog.ChangeIndent(-1);
FileLog.LogBuffered("} // end try");
FileLog.LogBuffered(".finally");
FileLog.LogBuffered("{");
FileLog.ChangeIndent(1);
}
il.BeginFinallyBlock();
break;
}
}
internal void MarkBlockAfter(ExceptionBlock block)
{
ExceptionBlockType blockType = block.blockType;
if (blockType == ExceptionBlockType.EndExceptionBlock)
{
if (debug)
{
LogIL(System.Reflection.Emit.OpCodes.Leave, new LeaveTry());
FileLog.ChangeIndent(-1);
FileLog.LogBuffered("} // end handler");
}
il.EndExceptionBlock();
}
}
internal void Emit(System.Reflection.Emit.OpCode opcode)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode));
LogIL(opcode);
il.Emit(opcode);
}
internal void Emit(System.Reflection.Emit.OpCode opcode, LocalBuilder local)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode, local));
LogIL(opcode, local);
il.Emit(opcode, local);
}
internal void Emit(System.Reflection.Emit.OpCode opcode, FieldInfo field)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode, field));
LogIL(opcode, field);
il.Emit(opcode, field);
}
internal void Emit(System.Reflection.Emit.OpCode opcode, Label[] labels)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode, labels));
LogIL(opcode, labels);
il.Emit(opcode, labels);
}
internal void Emit(System.Reflection.Emit.OpCode opcode, Label label)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode, label));
LogIL(opcode, label);
il.Emit(opcode, label);
}
internal void Emit(System.Reflection.Emit.OpCode opcode, string str)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode, str));
LogIL(opcode, str);
il.Emit(opcode, str);
}
internal void Emit(System.Reflection.Emit.OpCode opcode, float arg)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode, arg));
LogIL(opcode, arg);
il.Emit(opcode, arg);
}
internal void Emit(System.Reflection.Emit.OpCode opcode, byte arg)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode, arg));
LogIL(opcode, arg);
il.Emit(opcode, arg);
}
internal void Emit(System.Reflection.Emit.OpCode opcode, sbyte arg)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode, arg));
LogIL(opcode, arg);
il.Emit(opcode, arg);
}
internal void Emit(System.Reflection.Emit.OpCode opcode, double arg)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode, arg));
LogIL(opcode, arg);
il.Emit(opcode, arg);
}
internal void Emit(System.Reflection.Emit.OpCode opcode, int arg)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode, arg));
LogIL(opcode, arg);
il.Emit(opcode, arg);
}
internal void Emit(System.Reflection.Emit.OpCode opcode, MethodInfo meth)
{
if (opcode.Equals(System.Reflection.Emit.OpCodes.Call) || opcode.Equals(System.Reflection.Emit.OpCodes.Callvirt) || opcode.Equals(System.Reflection.Emit.OpCodes.Newobj))
{
EmitCall(opcode, meth, null);
return;
}
instructions.Add(CurrentPos(), new CodeInstruction(opcode, meth));
LogIL(opcode, meth);
il.Emit(opcode, meth);
}
internal void Emit(System.Reflection.Emit.OpCode opcode, short arg)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode, arg));
LogIL(opcode, arg);
il.Emit(opcode, arg);
}
internal void Emit(System.Reflection.Emit.OpCode opcode, SignatureHelper signature)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode, signature));
LogIL(opcode, signature);
il.Emit(opcode, signature);
}
internal void Emit(System.Reflection.Emit.OpCode opcode, ConstructorInfo con)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode, con));
LogIL(opcode, con);
il.Emit(opcode, con);
}
internal void Emit(System.Reflection.Emit.OpCode opcode, Type cls)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode, cls));
LogIL(opcode, cls);
il.Emit(opcode, cls);
}
internal void Emit(System.Reflection.Emit.OpCode opcode, long arg)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode, arg));
LogIL(opcode, arg);
il.Emit(opcode, arg);
}
internal void EmitCall(System.Reflection.Emit.OpCode opcode, MethodInfo methodInfo, Type[] optionalParameterTypes)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode, methodInfo));
string extra = ((optionalParameterTypes != null && optionalParameterTypes.Length != 0) ? optionalParameterTypes.Description() : null);
LogIL(opcode, methodInfo, extra);
il.EmitCall(opcode, methodInfo, optionalParameterTypes);
}
internal void EmitCalli(System.Reflection.Emit.OpCode opcode, CallingConvention unmanagedCallConv, Type returnType, Type[] parameterTypes)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode, unmanagedCallConv));
string extra = returnType.FullName + " " + parameterTypes.Description();
LogIL(opcode, unmanagedCallConv, extra);
il.EmitCalli(opcode, unmanagedCallConv, returnType, parameterTypes);
}
internal void EmitCalli(System.Reflection.Emit.OpCode opcode, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, Type[] optionalParameterTypes)
{
instructions.Add(CurrentPos(), new CodeInstruction(opcode, callingConvention));
string extra = returnType.FullName + " " + parameterTypes.Description() + " " + optionalParameterTypes.Description();
LogIL(opcode, callingConvention, extra);
il.EmitCalli(opcode, callingConvention, returnType, parameterTypes, optionalParameterTypes);
}
}

View file

@ -0,0 +1,17 @@
using System;
namespace HarmonyLib;
public class ExceptionBlock
{
public ExceptionBlockType blockType;
public Type catchType;
public ExceptionBlock(ExceptionBlockType blockType, Type catchType = null)
{
this.blockType = blockType;
this.catchType = catchType ?? typeof(object);
base._002Ector();
}
}

View file

@ -0,0 +1,11 @@
namespace HarmonyLib;
public enum ExceptionBlockType
{
BeginExceptionBlock,
BeginCatchBlock,
BeginExceptFilterBlock,
BeginFaultBlock,
BeginFinallyBlock,
EndExceptionBlock
}

View file

@ -0,0 +1,104 @@
using System;
using System.Reflection;
using System.Reflection.Emit;
using MonoMod.Utils;
namespace HarmonyLib;
public static class FastAccess
{
public static InstantiationHandler<T> CreateInstantiationHandler<T>()
{
ConstructorInfo constructor = typeof(T).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Array.Empty<Type>(), null);
if ((object)constructor == null)
{
throw new ApplicationException($"The type {typeof(T)} must declare an empty constructor (the constructor may be private, internal, protected, protected internal, or public).");
}
DynamicMethodDefinition dynamicMethodDefinition = new DynamicMethodDefinition("InstantiateObject_" + typeof(T).Name, typeof(T), null);
ILGenerator iLGenerator = dynamicMethodDefinition.GetILGenerator();
iLGenerator.Emit(OpCodes.Newobj, constructor);
iLGenerator.Emit(OpCodes.Ret);
return (InstantiationHandler<T>)dynamicMethodDefinition.Generate().CreateDelegate(typeof(InstantiationHandler<T>));
}
[Obsolete("Use AccessTools.MethodDelegate<Func<T, S>>(PropertyInfo.GetGetMethod(true))")]
public static GetterHandler<T, S> CreateGetterHandler<T, S>(PropertyInfo propertyInfo)
{
MethodInfo getMethod = propertyInfo.GetGetMethod(nonPublic: true);
DynamicMethodDefinition dynamicMethodDefinition = CreateGetDynamicMethod<T, S>(propertyInfo.DeclaringType);
ILGenerator iLGenerator = dynamicMethodDefinition.GetILGenerator();
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Call, getMethod);
iLGenerator.Emit(OpCodes.Ret);
return (GetterHandler<T, S>)dynamicMethodDefinition.Generate().CreateDelegate(typeof(GetterHandler<T, S>));
}
[Obsolete("Use AccessTools.FieldRefAccess<T, S>(fieldInfo)")]
public static GetterHandler<T, S> CreateGetterHandler<T, S>(FieldInfo fieldInfo)
{
DynamicMethodDefinition dynamicMethodDefinition = CreateGetDynamicMethod<T, S>(fieldInfo.DeclaringType);
ILGenerator iLGenerator = dynamicMethodDefinition.GetILGenerator();
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Ldfld, fieldInfo);
iLGenerator.Emit(OpCodes.Ret);
return (GetterHandler<T, S>)dynamicMethodDefinition.Generate().CreateDelegate(typeof(GetterHandler<T, S>));
}
[Obsolete("Use AccessTools.FieldRefAccess<T, S>(name) for fields and AccessTools.MethodDelegate<Func<T, S>>(AccessTools.PropertyGetter(typeof(T), name)) for properties")]
public static GetterHandler<T, S> CreateFieldGetter<T, S>(params string[] names)
{
foreach (string name in names)
{
FieldInfo field = typeof(T).GetField(name, AccessTools.all);
if ((object)field != null)
{
return CreateGetterHandler<T, S>(field);
}
PropertyInfo property = typeof(T).GetProperty(name, AccessTools.all);
if ((object)property != null)
{
return CreateGetterHandler<T, S>(property);
}
}
return null;
}
[Obsolete("Use AccessTools.MethodDelegate<Action<T, S>>(PropertyInfo.GetSetMethod(true))")]
public static SetterHandler<T, S> CreateSetterHandler<T, S>(PropertyInfo propertyInfo)
{
MethodInfo setMethod = propertyInfo.GetSetMethod(nonPublic: true);
DynamicMethodDefinition dynamicMethodDefinition = CreateSetDynamicMethod<T, S>(propertyInfo.DeclaringType);
ILGenerator iLGenerator = dynamicMethodDefinition.GetILGenerator();
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Ldarg_1);
iLGenerator.Emit(OpCodes.Call, setMethod);
iLGenerator.Emit(OpCodes.Ret);
return (SetterHandler<T, S>)dynamicMethodDefinition.Generate().CreateDelegate(typeof(SetterHandler<T, S>));
}
[Obsolete("Use AccessTools.FieldRefAccess<T, S>(fieldInfo)")]
public static SetterHandler<T, S> CreateSetterHandler<T, S>(FieldInfo fieldInfo)
{
DynamicMethodDefinition dynamicMethodDefinition = CreateSetDynamicMethod<T, S>(fieldInfo.DeclaringType);
ILGenerator iLGenerator = dynamicMethodDefinition.GetILGenerator();
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Ldarg_1);
iLGenerator.Emit(OpCodes.Stfld, fieldInfo);
iLGenerator.Emit(OpCodes.Ret);
return (SetterHandler<T, S>)dynamicMethodDefinition.Generate().CreateDelegate(typeof(SetterHandler<T, S>));
}
private static DynamicMethodDefinition CreateGetDynamicMethod<T, S>(Type type)
{
return new DynamicMethodDefinition("DynamicGet_" + type.Name, typeof(S), new Type[1] { typeof(T) });
}
private static DynamicMethodDefinition CreateSetDynamicMethod<T, S>(Type type)
{
return new DynamicMethodDefinition("DynamicSet_" + type.Name, typeof(void), new Type[2]
{
typeof(T),
typeof(S)
});
}
}

View file

@ -0,0 +1,3 @@
namespace HarmonyLib;
public delegate object FastInvokeHandler(object target, params object[] parameters);

View file

@ -0,0 +1,211 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
namespace HarmonyLib;
public static class FileLog
{
private static readonly object fileLock = new object();
private static bool _logPathInited;
private static string _logPath;
public static char indentChar = '\t';
public static int indentLevel = 0;
private static List<string> buffer = new List<string>();
public static StreamWriter LogWriter { get; set; }
public static string LogPath
{
get
{
lock (fileLock)
{
if (!_logPathInited)
{
_logPathInited = true;
string environmentVariable = Environment.GetEnvironmentVariable("HARMONY_NO_LOG");
if (!string.IsNullOrEmpty(environmentVariable))
{
return null;
}
_logPath = Environment.GetEnvironmentVariable("HARMONY_LOG_FILE");
if (string.IsNullOrEmpty(_logPath))
{
string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
Directory.CreateDirectory(folderPath);
_logPath = Path.Combine(folderPath, "harmony.log.txt");
}
}
return _logPath;
}
}
}
private static string IndentString()
{
return new string(indentChar, indentLevel);
}
public static void ChangeIndent(int delta)
{
lock (fileLock)
{
indentLevel = Math.Max(0, indentLevel + delta);
}
}
public static void LogBuffered(string str)
{
lock (fileLock)
{
buffer.Add(IndentString() + str);
}
}
public static void LogBuffered(List<string> strings)
{
lock (fileLock)
{
buffer.AddRange(strings);
}
}
public static List<string> GetBuffer(bool clear)
{
lock (fileLock)
{
List<string> result = buffer;
if (clear)
{
buffer = new List<string>();
}
return result;
}
}
public static void SetBuffer(List<string> buffer)
{
lock (fileLock)
{
FileLog.buffer = buffer;
}
}
public static void FlushBuffer()
{
if (LogWriter != null)
{
foreach (string item in buffer)
{
LogWriter.WriteLine(item);
}
buffer.Clear();
}
else
{
if (LogPath == null)
{
return;
}
lock (fileLock)
{
if (buffer.Count <= 0)
{
return;
}
using StreamWriter streamWriter = File.AppendText(LogPath);
foreach (string item2 in buffer)
{
streamWriter.WriteLine(item2);
}
buffer.Clear();
}
}
}
public static void Log(string str)
{
if (LogWriter != null)
{
LogWriter.WriteLine(IndentString() + str);
}
else
{
if (LogPath == null)
{
return;
}
lock (fileLock)
{
using StreamWriter streamWriter = File.AppendText(LogPath);
streamWriter.WriteLine(IndentString() + str);
}
}
}
public static void Debug(string str)
{
if (Harmony.DEBUG)
{
Log(str);
}
}
public static void Reset()
{
lock (fileLock)
{
string path = $"{Environment.GetFolderPath(Environment.SpecialFolder.Desktop)}{Path.DirectorySeparatorChar}harmony.log.txt";
File.Delete(path);
}
}
public unsafe static void LogBytes(long ptr, int len)
{
lock (fileLock)
{
byte* ptr2 = (byte*)ptr;
string text = "";
for (int i = 1; i <= len; i++)
{
if (text.Length == 0)
{
text = "# ";
}
text += $"{*ptr2:X2} ";
if (i > 1 || len == 1)
{
if (i % 8 == 0 || i == len)
{
Log(text);
text = "";
}
else if (i % 4 == 0)
{
text += " ";
}
}
ptr2++;
}
byte[] destination = new byte[len];
Marshal.Copy((IntPtr)ptr, destination, 0, len);
MD5 mD = MD5.Create();
byte[] array = mD.ComputeHash(destination);
StringBuilder stringBuilder = new StringBuilder();
for (int j = 0; j < array.Length; j++)
{
stringBuilder.Append(array[j].ToString("X2"));
}
Log($"HASH: {stringBuilder}");
}
}
}

View file

@ -0,0 +1,165 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace HarmonyLib;
public static class GeneralExtensions
{
public static string Join<T>(this IEnumerable<T> enumeration, Func<T, string> converter = null, string delimiter = ", ")
{
if (converter == null)
{
converter = (T t) => t.ToString();
}
return enumeration.Aggregate("", (string prev, T curr) => prev + ((prev.Length > 0) ? delimiter : "") + converter(curr));
}
public static string Description(this Type[] parameters)
{
if (parameters == null)
{
return "NULL";
}
return "(" + parameters.Join((Type p) => p.FullDescription()) + ")";
}
public static string FullDescription(this Type type)
{
if ((object)type == null)
{
return "null";
}
string text = type.Namespace;
if (!string.IsNullOrEmpty(text))
{
text += ".";
}
string text2 = text + type.Name;
if (type.IsGenericType)
{
text2 += "<";
Type[] genericArguments = type.GetGenericArguments();
for (int i = 0; i < genericArguments.Length; i++)
{
if (!text2.EndsWith("<", StringComparison.Ordinal))
{
text2 += ", ";
}
text2 += genericArguments[i].FullDescription();
}
text2 += ">";
}
return text2;
}
public static string FullDescription(this MethodBase member)
{
if ((object)member == null)
{
return "null";
}
Type returnedType = AccessTools.GetReturnedType(member);
StringBuilder stringBuilder = new StringBuilder();
if (member.IsStatic)
{
stringBuilder.Append("static ");
}
if (member.IsAbstract)
{
stringBuilder.Append("abstract ");
}
if (member.IsVirtual)
{
stringBuilder.Append("virtual ");
}
stringBuilder.Append(returnedType.FullDescription() + " ");
if ((object)member.DeclaringType != null)
{
stringBuilder.Append(member.DeclaringType.FullDescription() + "::");
}
string text = member.GetParameters().Join((ParameterInfo p) => p.ParameterType.FullDescription() + " " + p.Name);
stringBuilder.Append(member.Name + "(" + text + ")");
return stringBuilder.ToString();
}
public static Type[] Types(this ParameterInfo[] pinfo)
{
return pinfo.Select((ParameterInfo pi) => pi.ParameterType).ToArray();
}
public static T GetValueSafe<S, T>(this Dictionary<S, T> dictionary, S key)
{
if (dictionary.TryGetValue(key, out var value))
{
return value;
}
return default(T);
}
public static T GetTypedValue<T>(this Dictionary<string, object> dictionary, string key)
{
if (dictionary.TryGetValue(key, out var value) && value is T)
{
return (T)value;
}
return default(T);
}
public static string ToLiteral(this string input, string quoteChar = "\"")
{
StringBuilder stringBuilder = new StringBuilder(input.Length + 2);
stringBuilder.Append(quoteChar);
foreach (char c in input)
{
switch (c)
{
case '\'':
stringBuilder.Append("\\'");
continue;
case '"':
stringBuilder.Append("\\\"");
continue;
case '\\':
stringBuilder.Append("\\\\");
continue;
case '\0':
stringBuilder.Append("\\0");
continue;
case '\a':
stringBuilder.Append("\\a");
continue;
case '\b':
stringBuilder.Append("\\b");
continue;
case '\f':
stringBuilder.Append("\\f");
continue;
case '\n':
stringBuilder.Append("\\n");
continue;
case '\r':
stringBuilder.Append("\\r");
continue;
case '\t':
stringBuilder.Append("\\t");
continue;
case '\v':
stringBuilder.Append("\\v");
continue;
}
if (c >= ' ' && c <= '~')
{
stringBuilder.Append(c);
continue;
}
stringBuilder.Append("\\u");
int num = c;
stringBuilder.Append(num.ToString("x4"));
}
stringBuilder.Append(quoteChar);
return stringBuilder.ToString();
}
}

View file

@ -0,0 +1,6 @@
using System;
namespace HarmonyLib;
[Obsolete("Use AccessTools.FieldRefAccess<T, S> for fields and AccessTools.MethodDelegate<Func<T, S>> for property getters")]
public delegate S GetterHandler<in T, out S>(T source);

View file

@ -0,0 +1,251 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using MonoMod.Core.Platforms;
namespace HarmonyLib;
public class Harmony
{
public static bool DEBUG;
public string Id { get; private set; }
public Harmony(string id)
{
if (string.IsNullOrEmpty(id))
{
throw new ArgumentException("id cannot be null or empty");
}
try
{
string environmentVariable = Environment.GetEnvironmentVariable("HARMONY_DEBUG");
if (environmentVariable != null && environmentVariable.Length > 0)
{
environmentVariable = environmentVariable.Trim();
DEBUG = environmentVariable == "1" || bool.Parse(environmentVariable);
}
}
catch
{
}
if (DEBUG)
{
Assembly assembly = typeof(Harmony).Assembly;
Version version = assembly.GetName().Version;
string value = assembly.Location;
string value2 = Environment.Version.ToString();
string value3 = Environment.OSVersion.Platform.ToString();
if (string.IsNullOrEmpty(value))
{
value = new Uri(assembly.CodeBase).LocalPath;
}
FileLog.Log($"### Harmony id={id}, version={version}, location={value}, env/clr={value2}, platform={value3}");
MethodBase outsideCaller = AccessTools.GetOutsideCaller();
if ((object)outsideCaller.DeclaringType != null)
{
Assembly assembly2 = outsideCaller.DeclaringType.Assembly;
value = assembly2.Location;
if (string.IsNullOrEmpty(value))
{
value = new Uri(assembly2.CodeBase).LocalPath;
}
FileLog.Log("### Started from " + outsideCaller.FullDescription() + ", location " + value);
FileLog.Log($"### At {DateTime.Now:yyyy-MM-dd hh.mm.ss}");
}
}
Id = id;
}
public void PatchAll()
{
MethodBase method = new StackTrace().GetFrame(1).GetMethod();
Assembly assembly = method.ReflectedType.Assembly;
PatchAll(assembly);
}
public PatchProcessor CreateProcessor(MethodBase original)
{
return new PatchProcessor(this, original);
}
public PatchClassProcessor CreateClassProcessor(Type type)
{
return new PatchClassProcessor(this, type);
}
public ReversePatcher CreateReversePatcher(MethodBase original, HarmonyMethod standin)
{
return new ReversePatcher(this, original, standin);
}
public void PatchAll(Assembly assembly)
{
AccessTools.GetTypesFromAssembly(assembly).Do(delegate(Type type)
{
CreateClassProcessor(type).Patch();
});
}
public void PatchAllUncategorized()
{
MethodBase method = new StackTrace().GetFrame(1).GetMethod();
Assembly assembly = method.ReflectedType.Assembly;
PatchAllUncategorized(assembly);
}
public void PatchAllUncategorized(Assembly assembly)
{
PatchClassProcessor[] sequence = AccessTools.GetTypesFromAssembly(assembly).Select(CreateClassProcessor).ToArray();
sequence.DoIf((PatchClassProcessor patchClass) => string.IsNullOrEmpty(patchClass.Category), delegate(PatchClassProcessor patchClass)
{
patchClass.Patch();
});
}
public void PatchCategory(string category)
{
MethodBase method = new StackTrace().GetFrame(1).GetMethod();
Assembly assembly = method.ReflectedType.Assembly;
PatchCategory(assembly, category);
}
public void PatchCategory(Assembly assembly, string category)
{
AccessTools.GetTypesFromAssembly(assembly).Where(delegate(Type type)
{
List<HarmonyMethod> fromType = HarmonyMethodExtensions.GetFromType(type);
HarmonyMethod harmonyMethod = HarmonyMethod.Merge(fromType);
return harmonyMethod.category == category;
}).Do(delegate(Type type)
{
CreateClassProcessor(type).Patch();
});
}
public MethodInfo Patch(MethodBase original, HarmonyMethod prefix = null, HarmonyMethod postfix = null, HarmonyMethod transpiler = null, HarmonyMethod finalizer = null)
{
PatchProcessor patchProcessor = CreateProcessor(original);
patchProcessor.AddPrefix(prefix);
patchProcessor.AddPostfix(postfix);
patchProcessor.AddTranspiler(transpiler);
patchProcessor.AddFinalizer(finalizer);
return patchProcessor.Patch();
}
public static MethodInfo ReversePatch(MethodBase original, HarmonyMethod standin, MethodInfo transpiler = null)
{
return PatchFunctions.ReversePatch(standin, original, transpiler);
}
public void UnpatchAll(string harmonyID = null)
{
List<MethodBase> list = GetAllPatchedMethods().ToList();
foreach (MethodBase original in list)
{
bool flag = original.HasMethodBody();
Patches patchInfo = GetPatchInfo(original);
if (flag)
{
patchInfo.Postfixes.DoIf(IDCheck, delegate(Patch patch)
{
Unpatch(original, patch.PatchMethod);
});
patchInfo.Prefixes.DoIf(IDCheck, delegate(Patch patch)
{
Unpatch(original, patch.PatchMethod);
});
}
patchInfo.Transpilers.DoIf(IDCheck, delegate(Patch patch)
{
Unpatch(original, patch.PatchMethod);
});
if (flag)
{
patchInfo.Finalizers.DoIf(IDCheck, delegate(Patch patch)
{
Unpatch(original, patch.PatchMethod);
});
}
}
bool IDCheck(Patch patch)
{
if (harmonyID != null)
{
return patch.owner == harmonyID;
}
return true;
}
}
public void Unpatch(MethodBase original, HarmonyPatchType type, string harmonyID = "*")
{
PatchProcessor patchProcessor = CreateProcessor(original);
patchProcessor.Unpatch(type, harmonyID);
}
public void Unpatch(MethodBase original, MethodInfo patch)
{
PatchProcessor patchProcessor = CreateProcessor(original);
patchProcessor.Unpatch(patch);
}
public static bool HasAnyPatches(string harmonyID)
{
return (from original in GetAllPatchedMethods()
select GetPatchInfo(original)).Any((Patches info) => info.Owners.Contains(harmonyID));
}
public static Patches GetPatchInfo(MethodBase method)
{
return PatchProcessor.GetPatchInfo(method);
}
public IEnumerable<MethodBase> GetPatchedMethods()
{
return from original in GetAllPatchedMethods()
where GetPatchInfo(original).Owners.Contains(Id)
select original;
}
public static IEnumerable<MethodBase> GetAllPatchedMethods()
{
return PatchProcessor.GetAllPatchedMethods();
}
public static MethodBase GetOriginalMethod(MethodInfo replacement)
{
if (replacement == null)
{
throw new ArgumentNullException("replacement");
}
MethodInfo replacement2 = PlatformTriple.Current.GetIdentifiable(replacement) as MethodInfo;
return HarmonySharedState.GetOriginal(replacement2);
}
public static MethodBase GetMethodFromStackframe(StackFrame frame)
{
if (frame == null)
{
throw new ArgumentNullException("frame");
}
return HarmonySharedState.FindReplacement(frame) ?? frame.GetMethod();
}
public static MethodBase GetOriginalMethodFromStackframe(StackFrame frame)
{
MethodBase methodBase = GetMethodFromStackframe(frame);
if (methodBase is MethodInfo replacement)
{
methodBase = GetOriginalMethod(replacement) ?? methodBase;
}
return methodBase;
}
public static Dictionary<string, Version> VersionInfo(out Version currentVersion)
{
return PatchProcessor.VersionInfo(out currentVersion);
}
}

View file

@ -0,0 +1,12 @@
using System;
namespace HarmonyLib;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class HarmonyAfter : HarmonyAttribute
{
public HarmonyAfter(params string[] after)
{
info.after = after;
}
}

View file

@ -0,0 +1,37 @@
using System;
namespace HarmonyLib;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Parameter, AllowMultiple = true)]
public class HarmonyArgument : Attribute
{
public string OriginalName { get; private set; }
public int Index { get; private set; }
public string NewName { get; private set; }
public HarmonyArgument(string originalName)
: this(originalName, null)
{
}
public HarmonyArgument(int index)
: this(index, null)
{
}
public HarmonyArgument(string originalName, string newName)
{
OriginalName = originalName;
Index = -1;
NewName = newName;
}
public HarmonyArgument(int index, string name)
{
OriginalName = null;
Index = index;
NewName = name;
}
}

View file

@ -0,0 +1,8 @@
using System;
namespace HarmonyLib;
public class HarmonyAttribute : Attribute
{
public HarmonyMethod info = new HarmonyMethod();
}

View file

@ -0,0 +1,12 @@
using System;
namespace HarmonyLib;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class HarmonyBefore : HarmonyAttribute
{
public HarmonyBefore(params string[] before)
{
info.before = before;
}
}

View file

@ -0,0 +1,8 @@
using System;
namespace HarmonyLib;
[AttributeUsage(AttributeTargets.Method)]
public class HarmonyCleanup : Attribute
{
}

View file

@ -0,0 +1,12 @@
using System;
namespace HarmonyLib;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class HarmonyDebug : HarmonyAttribute
{
public HarmonyDebug()
{
info.debug = true;
}
}

View file

@ -0,0 +1,104 @@
using System;
namespace HarmonyLib;
[AttributeUsage(AttributeTargets.Delegate, AllowMultiple = true)]
public class HarmonyDelegate : HarmonyPatch
{
public HarmonyDelegate(Type declaringType)
: base(declaringType)
{
}
public HarmonyDelegate(Type declaringType, Type[] argumentTypes)
: base(declaringType, argumentTypes)
{
}
public HarmonyDelegate(Type declaringType, string methodName)
: base(declaringType, methodName)
{
}
public HarmonyDelegate(Type declaringType, string methodName, params Type[] argumentTypes)
: base(declaringType, methodName, argumentTypes)
{
}
public HarmonyDelegate(Type declaringType, string methodName, Type[] argumentTypes, ArgumentType[] argumentVariations)
: base(declaringType, methodName, argumentTypes, argumentVariations)
{
}
public HarmonyDelegate(Type declaringType, MethodDispatchType methodDispatchType)
: base(declaringType, MethodType.Normal)
{
info.nonVirtualDelegate = methodDispatchType == MethodDispatchType.Call;
}
public HarmonyDelegate(Type declaringType, MethodDispatchType methodDispatchType, params Type[] argumentTypes)
: base(declaringType, MethodType.Normal, argumentTypes)
{
info.nonVirtualDelegate = methodDispatchType == MethodDispatchType.Call;
}
public HarmonyDelegate(Type declaringType, MethodDispatchType methodDispatchType, Type[] argumentTypes, ArgumentType[] argumentVariations)
: base(declaringType, MethodType.Normal, argumentTypes, argumentVariations)
{
info.nonVirtualDelegate = methodDispatchType == MethodDispatchType.Call;
}
public HarmonyDelegate(Type declaringType, string methodName, MethodDispatchType methodDispatchType)
: base(declaringType, methodName, MethodType.Normal)
{
info.nonVirtualDelegate = methodDispatchType == MethodDispatchType.Call;
}
public HarmonyDelegate(string methodName)
: base(methodName)
{
}
public HarmonyDelegate(string methodName, params Type[] argumentTypes)
: base(methodName, argumentTypes)
{
}
public HarmonyDelegate(string methodName, Type[] argumentTypes, ArgumentType[] argumentVariations)
: base(methodName, argumentTypes, argumentVariations)
{
}
public HarmonyDelegate(string methodName, MethodDispatchType methodDispatchType)
: base(methodName, MethodType.Normal)
{
info.nonVirtualDelegate = methodDispatchType == MethodDispatchType.Call;
}
public HarmonyDelegate(MethodDispatchType methodDispatchType)
{
info.nonVirtualDelegate = methodDispatchType == MethodDispatchType.Call;
}
public HarmonyDelegate(MethodDispatchType methodDispatchType, params Type[] argumentTypes)
: base(MethodType.Normal, argumentTypes)
{
info.nonVirtualDelegate = methodDispatchType == MethodDispatchType.Call;
}
public HarmonyDelegate(MethodDispatchType methodDispatchType, Type[] argumentTypes, ArgumentType[] argumentVariations)
: base(MethodType.Normal, argumentTypes, argumentVariations)
{
info.nonVirtualDelegate = methodDispatchType == MethodDispatchType.Call;
}
public HarmonyDelegate(Type[] argumentTypes)
: base(argumentTypes)
{
}
public HarmonyDelegate(Type[] argumentTypes, ArgumentType[] argumentVariations)
: base(argumentTypes, argumentVariations)
{
}
}

View file

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
namespace HarmonyLib;
[Serializable]
public class HarmonyException : Exception
{
private Dictionary<int, CodeInstruction> instructions;
private int errorOffset;
internal HarmonyException()
{
}
internal HarmonyException(string message)
: base(message)
{
}
internal HarmonyException(string message, Exception innerException)
: base(message, innerException)
{
}
protected HarmonyException(SerializationInfo serializationInfo, StreamingContext streamingContext)
{
throw new NotImplementedException();
}
internal HarmonyException(Exception innerException, Dictionary<int, CodeInstruction> instructions, int errorOffset)
: base("IL Compile Error", innerException)
{
this.instructions = instructions;
this.errorOffset = errorOffset;
}
internal static Exception Create(Exception ex, Dictionary<int, CodeInstruction> finalInstructions)
{
Match match = Regex.Match(ex.Message.TrimEnd(Array.Empty<char>()), "Reason: Invalid IL code in.+: IL_(\\d{4}): (.+)$");
if (!match.Success)
{
return ex;
}
int num = int.Parse(match.Groups[1].Value, NumberStyles.HexNumber);
Regex.Replace(match.Groups[2].Value, " {2,}", " ");
if (ex is HarmonyException ex2)
{
ex2.instructions = finalInstructions;
ex2.errorOffset = num;
return ex2;
}
return new HarmonyException(ex, finalInstructions, num);
}
public List<KeyValuePair<int, CodeInstruction>> GetInstructionsWithOffsets()
{
List<KeyValuePair<int, CodeInstruction>> list = new List<KeyValuePair<int, CodeInstruction>>();
list.AddRange(instructions.OrderBy((KeyValuePair<int, CodeInstruction> ins) => ins.Key));
return list;
}
public List<CodeInstruction> GetInstructions()
{
return (from ins in instructions
orderby ins.Key
select ins.Value).ToList();
}
public int GetErrorOffset()
{
return errorOffset;
}
public int GetErrorIndex()
{
if (instructions.TryGetValue(errorOffset, out var value))
{
return GetInstructions().IndexOf(value);
}
return -1;
}
}

View file

@ -0,0 +1,8 @@
using System;
namespace HarmonyLib;
[AttributeUsage(AttributeTargets.Method)]
public class HarmonyFinalizer : Attribute
{
}

View file

@ -0,0 +1,156 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace HarmonyLib;
public class HarmonyMethod
{
public MethodInfo method;
public string category;
public Type declaringType;
public string methodName;
public MethodType? methodType;
public Type[] argumentTypes;
public int priority = -1;
public string[] before;
public string[] after;
public HarmonyReversePatchType? reversePatchType;
public bool? debug;
public bool nonVirtualDelegate;
public HarmonyMethod()
{
}
private void ImportMethod(MethodInfo theMethod)
{
method = theMethod;
if ((object)method != null)
{
List<HarmonyMethod> fromMethod = HarmonyMethodExtensions.GetFromMethod(method);
if (fromMethod != null)
{
Merge(fromMethod).CopyTo(this);
}
}
}
public HarmonyMethod(MethodInfo method)
{
if ((object)method == null)
{
throw new ArgumentNullException("method");
}
ImportMethod(method);
}
public HarmonyMethod(Delegate @delegate)
: this(@delegate.Method)
{
}
public HarmonyMethod(MethodInfo method, int priority = -1, string[] before = null, string[] after = null, bool? debug = null)
{
if ((object)method == null)
{
throw new ArgumentNullException("method");
}
ImportMethod(method);
this.priority = priority;
this.before = before;
this.after = after;
this.debug = debug;
}
public HarmonyMethod(Delegate @delegate, int priority = -1, string[] before = null, string[] after = null, bool? debug = null)
: this(@delegate.Method, priority, before, after, debug)
{
}
public HarmonyMethod(Type methodType, string methodName, Type[] argumentTypes = null)
{
MethodInfo methodInfo = AccessTools.Method(methodType, methodName, argumentTypes);
if ((object)methodInfo == null)
{
throw new ArgumentException($"Cannot not find method for type {methodType} and name {methodName} and parameters {argumentTypes?.Description()}");
}
ImportMethod(methodInfo);
}
public static List<string> HarmonyFields()
{
return (from s in AccessTools.GetFieldNames(typeof(HarmonyMethod))
where s != "method"
select s).ToList();
}
public static HarmonyMethod Merge(List<HarmonyMethod> attributes)
{
HarmonyMethod harmonyMethod = new HarmonyMethod();
if (attributes == null || attributes.Count == 0)
{
return harmonyMethod;
}
Traverse resultTrv = Traverse.Create(harmonyMethod);
attributes.ForEach(delegate(HarmonyMethod attribute)
{
Traverse trv = Traverse.Create(attribute);
HarmonyFields().ForEach(delegate(string f)
{
object value = trv.Field(f).GetValue();
if (value != null && (f != "priority" || (int)value != -1))
{
HarmonyMethodExtensions.SetValue(resultTrv, f, value);
}
});
});
return harmonyMethod;
}
public override string ToString()
{
string result = "";
Traverse trv = Traverse.Create(this);
HarmonyFields().ForEach(delegate(string f)
{
if (result.Length > 0)
{
result += ", ";
}
result += $"{f}={trv.Field(f).GetValue()}";
});
return "HarmonyMethod[" + result + "]";
}
internal string Description()
{
string value = (((object)declaringType != null) ? declaringType.FullName : "undefined");
string value2 = methodName ?? "undefined";
string value3 = (methodType.HasValue ? methodType.Value.ToString() : "undefined");
string value4 = ((argumentTypes != null) ? argumentTypes.Description() : "undefined");
return $"(class={value}, methodname={value2}, type={value3}, args={value4})";
}
public static implicit operator HarmonyMethod(MethodInfo method)
{
return new HarmonyMethod(method);
}
public static implicit operator HarmonyMethod(Delegate @delegate)
{
return new HarmonyMethod(@delegate);
}
}

View file

@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace HarmonyLib;
public static class HarmonyMethodExtensions
{
internal static void SetValue(Traverse trv, string name, object val)
{
if (val != null)
{
Traverse traverse = trv.Field(name);
if (name == "methodType" || name == "reversePatchType")
{
Type underlyingType = Nullable.GetUnderlyingType(traverse.GetValueType());
val = Enum.ToObject(underlyingType, (int)val);
}
traverse.SetValue(val);
}
}
public static void CopyTo(this HarmonyMethod from, HarmonyMethod to)
{
if (to == null)
{
return;
}
Traverse fromTrv = Traverse.Create(from);
Traverse toTrv = Traverse.Create(to);
HarmonyMethod.HarmonyFields().ForEach(delegate(string f)
{
object value = fromTrv.Field(f).GetValue();
if (value != null)
{
SetValue(toTrv, f, value);
}
});
}
public static HarmonyMethod Clone(this HarmonyMethod original)
{
HarmonyMethod harmonyMethod = new HarmonyMethod();
original.CopyTo(harmonyMethod);
return harmonyMethod;
}
public static HarmonyMethod Merge(this HarmonyMethod master, HarmonyMethod detail)
{
if (detail == null)
{
return master;
}
HarmonyMethod harmonyMethod = new HarmonyMethod();
Traverse resultTrv = Traverse.Create(harmonyMethod);
Traverse masterTrv = Traverse.Create(master);
Traverse detailTrv = Traverse.Create(detail);
HarmonyMethod.HarmonyFields().ForEach(delegate(string f)
{
object value = masterTrv.Field(f).GetValue();
object value2 = detailTrv.Field(f).GetValue();
if (f != "priority" || (int)value2 != -1)
{
SetValue(resultTrv, f, value2 ?? value);
}
});
return harmonyMethod;
}
private static HarmonyMethod GetHarmonyMethodInfo(object attribute)
{
FieldInfo field = attribute.GetType().GetField("info", AccessTools.all);
if ((object)field == null)
{
return null;
}
if (field.FieldType.FullName != PatchTools.harmonyMethodFullName)
{
return null;
}
object value = field.GetValue(attribute);
return AccessTools.MakeDeepCopy<HarmonyMethod>(value);
}
public static List<HarmonyMethod> GetFromType(Type type)
{
return (from attr in type.GetCustomAttributes(inherit: true)
select GetHarmonyMethodInfo(attr) into info
where info != null
select info).ToList();
}
public static HarmonyMethod GetMergedFromType(Type type)
{
return HarmonyMethod.Merge(GetFromType(type));
}
public static List<HarmonyMethod> GetFromMethod(MethodBase method)
{
return (from attr in method.GetCustomAttributes(inherit: true)
select GetHarmonyMethodInfo(attr) into info
where info != null
select info).ToList();
}
public static HarmonyMethod GetMergedFromMethod(MethodBase method)
{
return HarmonyMethod.Merge(GetFromMethod(method));
}
}

View file

@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
namespace HarmonyLib;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Delegate, AllowMultiple = true)]
public class HarmonyPatch : HarmonyAttribute
{
public HarmonyPatch()
{
}
public HarmonyPatch(Type declaringType)
{
info.declaringType = declaringType;
}
public HarmonyPatch(Type declaringType, Type[] argumentTypes)
{
info.declaringType = declaringType;
info.argumentTypes = argumentTypes;
}
public HarmonyPatch(Type declaringType, string methodName)
{
info.declaringType = declaringType;
info.methodName = methodName;
}
public HarmonyPatch(Type declaringType, string methodName, params Type[] argumentTypes)
{
info.declaringType = declaringType;
info.methodName = methodName;
info.argumentTypes = argumentTypes;
}
public HarmonyPatch(Type declaringType, string methodName, Type[] argumentTypes, ArgumentType[] argumentVariations)
{
info.declaringType = declaringType;
info.methodName = methodName;
ParseSpecialArguments(argumentTypes, argumentVariations);
}
public HarmonyPatch(Type declaringType, MethodType methodType)
{
info.declaringType = declaringType;
info.methodType = methodType;
}
public HarmonyPatch(Type declaringType, MethodType methodType, params Type[] argumentTypes)
{
info.declaringType = declaringType;
info.methodType = methodType;
info.argumentTypes = argumentTypes;
}
public HarmonyPatch(Type declaringType, MethodType methodType, Type[] argumentTypes, ArgumentType[] argumentVariations)
{
info.declaringType = declaringType;
info.methodType = methodType;
ParseSpecialArguments(argumentTypes, argumentVariations);
}
public HarmonyPatch(Type declaringType, string methodName, MethodType methodType)
{
info.declaringType = declaringType;
info.methodName = methodName;
info.methodType = methodType;
}
public HarmonyPatch(string methodName)
{
info.methodName = methodName;
}
public HarmonyPatch(string methodName, params Type[] argumentTypes)
{
info.methodName = methodName;
info.argumentTypes = argumentTypes;
}
public HarmonyPatch(string methodName, Type[] argumentTypes, ArgumentType[] argumentVariations)
{
info.methodName = methodName;
ParseSpecialArguments(argumentTypes, argumentVariations);
}
public HarmonyPatch(string methodName, MethodType methodType)
{
info.methodName = methodName;
info.methodType = methodType;
}
public HarmonyPatch(MethodType methodType)
{
info.methodType = methodType;
}
public HarmonyPatch(MethodType methodType, params Type[] argumentTypes)
{
info.methodType = methodType;
info.argumentTypes = argumentTypes;
}
public HarmonyPatch(MethodType methodType, Type[] argumentTypes, ArgumentType[] argumentVariations)
{
info.methodType = methodType;
ParseSpecialArguments(argumentTypes, argumentVariations);
}
public HarmonyPatch(Type[] argumentTypes)
{
info.argumentTypes = argumentTypes;
}
public HarmonyPatch(Type[] argumentTypes, ArgumentType[] argumentVariations)
{
ParseSpecialArguments(argumentTypes, argumentVariations);
}
public HarmonyPatch(string typeName, string methodName, MethodType methodType = MethodType.Normal)
{
info.declaringType = AccessTools.TypeByName(typeName);
info.methodName = methodName;
info.methodType = methodType;
}
private void ParseSpecialArguments(Type[] argumentTypes, ArgumentType[] argumentVariations)
{
if (argumentVariations == null || argumentVariations.Length == 0)
{
info.argumentTypes = argumentTypes;
return;
}
if (argumentTypes.Length < argumentVariations.Length)
{
throw new ArgumentException("argumentVariations contains more elements than argumentTypes", "argumentVariations");
}
List<Type> list = new List<Type>();
for (int i = 0; i < argumentTypes.Length; i++)
{
Type type = argumentTypes[i];
switch (argumentVariations[i])
{
case ArgumentType.Ref:
case ArgumentType.Out:
type = type.MakeByRefType();
break;
case ArgumentType.Pointer:
type = type.MakePointerType();
break;
}
list.Add(type);
}
info.argumentTypes = list.ToArray();
}
}

View file

@ -0,0 +1,8 @@
using System;
namespace HarmonyLib;
[AttributeUsage(AttributeTargets.Class)]
public class HarmonyPatchAll : HarmonyAttribute
{
}

View file

@ -0,0 +1,12 @@
using System;
namespace HarmonyLib;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class HarmonyPatchCategory : HarmonyAttribute
{
public HarmonyPatchCategory(string category)
{
info.category = category;
}
}

View file

@ -0,0 +1,11 @@
namespace HarmonyLib;
public enum HarmonyPatchType
{
All,
Prefix,
Postfix,
Transpiler,
Finalizer,
ReversePatch
}

View file

@ -0,0 +1,8 @@
using System;
namespace HarmonyLib;
[AttributeUsage(AttributeTargets.Method)]
public class HarmonyPostfix : Attribute
{
}

View file

@ -0,0 +1,8 @@
using System;
namespace HarmonyLib;
[AttributeUsage(AttributeTargets.Method)]
public class HarmonyPrefix : Attribute
{
}

View file

@ -0,0 +1,8 @@
using System;
namespace HarmonyLib;
[AttributeUsage(AttributeTargets.Method)]
public class HarmonyPrepare : Attribute
{
}

View file

@ -0,0 +1,12 @@
using System;
namespace HarmonyLib;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class HarmonyPriority : HarmonyAttribute
{
public HarmonyPriority(int priority)
{
info.priority = priority;
}
}

View file

@ -0,0 +1,12 @@
using System;
namespace HarmonyLib;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class HarmonyReversePatch : HarmonyAttribute
{
public HarmonyReversePatch(HarmonyReversePatchType type = HarmonyReversePatchType.Original)
{
info.reversePatchType = type;
}
}

View file

@ -0,0 +1,7 @@
namespace HarmonyLib;
public enum HarmonyReversePatchType
{
Original,
Snapshot
}

View file

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Mono.Cecil;
using MonoMod.Utils;
namespace HarmonyLib;
internal static class HarmonySharedState
{
private const string name = "HarmonySharedState";
internal const int internalVersion = 102;
private static readonly Dictionary<MethodBase, byte[]> state;
private static readonly Dictionary<MethodInfo, MethodBase> originals;
internal static readonly int actualVersion;
static HarmonySharedState()
{
Type orCreateSharedStateType = GetOrCreateSharedStateType();
FieldInfo field = orCreateSharedStateType.GetField("version");
if ((int)field.GetValue(null) == 0)
{
field.SetValue(null, 102);
}
actualVersion = (int)field.GetValue(null);
FieldInfo field2 = orCreateSharedStateType.GetField("state");
if (field2.GetValue(null) == null)
{
field2.SetValue(null, new Dictionary<MethodBase, byte[]>());
}
FieldInfo field3 = orCreateSharedStateType.GetField("originals");
if (field3 != null && field3.GetValue(null) == null)
{
field3.SetValue(null, new Dictionary<MethodInfo, MethodBase>());
}
state = (Dictionary<MethodBase, byte[]>)field2.GetValue(null);
originals = new Dictionary<MethodInfo, MethodBase>();
if (field3 != null)
{
originals = (Dictionary<MethodInfo, MethodBase>)field3.GetValue(null);
}
}
private static Type GetOrCreateSharedStateType()
{
Type type = Type.GetType("HarmonySharedState", throwOnError: false);
if (type != null)
{
return type;
}
using ModuleDefinition moduleDefinition = ModuleDefinition.CreateModule("HarmonySharedState", new ModuleParameters
{
Kind = ModuleKind.Dll,
ReflectionImporterProvider = MMReflectionImporter.Provider
});
Mono.Cecil.TypeAttributes attributes = Mono.Cecil.TypeAttributes.Public | Mono.Cecil.TypeAttributes.Abstract | Mono.Cecil.TypeAttributes.Sealed;
TypeDefinition typeDefinition = new TypeDefinition("", "HarmonySharedState", attributes)
{
BaseType = moduleDefinition.TypeSystem.Object
};
moduleDefinition.Types.Add(typeDefinition);
typeDefinition.Fields.Add(new FieldDefinition("state", Mono.Cecil.FieldAttributes.Public | Mono.Cecil.FieldAttributes.Static, moduleDefinition.ImportReference(typeof(Dictionary<MethodBase, byte[]>))));
typeDefinition.Fields.Add(new FieldDefinition("originals", Mono.Cecil.FieldAttributes.Public | Mono.Cecil.FieldAttributes.Static, moduleDefinition.ImportReference(typeof(Dictionary<MethodInfo, MethodBase>))));
typeDefinition.Fields.Add(new FieldDefinition("version", Mono.Cecil.FieldAttributes.Public | Mono.Cecil.FieldAttributes.Static, moduleDefinition.ImportReference(typeof(int))));
return ReflectionHelper.Load(moduleDefinition).GetType("HarmonySharedState");
}
internal static PatchInfo GetPatchInfo(MethodBase method)
{
byte[] valueSafe;
lock (state)
{
valueSafe = state.GetValueSafe(method);
}
if (valueSafe == null)
{
return null;
}
return PatchInfoSerialization.Deserialize(valueSafe);
}
internal static IEnumerable<MethodBase> GetPatchedMethods()
{
lock (state)
{
return state.Keys.ToArray();
}
}
internal static void UpdatePatchInfo(MethodBase original, MethodInfo replacement, PatchInfo patchInfo)
{
byte[] value = patchInfo.Serialize();
lock (state)
{
state[original] = value;
}
lock (originals)
{
originals[replacement] = original;
}
}
internal static MethodBase GetOriginal(MethodInfo replacement)
{
lock (originals)
{
return originals.GetValueSafe(replacement);
}
}
internal static MethodBase FindReplacement(StackFrame frame)
{
MethodInfo methodInfo = frame.GetMethod() as MethodInfo;
if (methodInfo == null)
{
return null;
}
return GetOriginal(methodInfo);
}
}

View file

@ -0,0 +1,8 @@
using System;
namespace HarmonyLib;
[AttributeUsage(AttributeTargets.Method)]
public class HarmonyTargetMethod : Attribute
{
}

View file

@ -0,0 +1,8 @@
using System;
namespace HarmonyLib;
[AttributeUsage(AttributeTargets.Method)]
public class HarmonyTargetMethods : Attribute
{
}

View file

@ -0,0 +1,8 @@
using System;
namespace HarmonyLib;
[AttributeUsage(AttributeTargets.Method)]
public class HarmonyTranspiler : Attribute
{
}

View file

@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Reflection.Emit;
namespace HarmonyLib;
internal class ILInstruction
{
internal int offset;
internal OpCode opcode;
internal object operand;
internal object argument;
internal List<Label> labels = new List<Label>();
internal List<ExceptionBlock> blocks = new List<ExceptionBlock>();
internal ILInstruction(OpCode opcode, object operand = null)
{
this.opcode = opcode;
this.operand = operand;
argument = operand;
}
internal CodeInstruction GetCodeInstruction()
{
CodeInstruction codeInstruction = new CodeInstruction(opcode, argument);
if (opcode.OperandType == OperandType.InlineNone)
{
codeInstruction.operand = null;
}
codeInstruction.labels = labels;
codeInstruction.blocks = blocks;
return codeInstruction;
}
internal int GetSize()
{
int num = opcode.Size;
switch (opcode.OperandType)
{
case OperandType.InlineSwitch:
num += (1 + ((Array)operand).Length) * 4;
break;
case OperandType.InlineI8:
case OperandType.InlineR:
num += 8;
break;
case OperandType.InlineBrTarget:
case OperandType.InlineField:
case OperandType.InlineI:
case OperandType.InlineMethod:
case OperandType.InlineSig:
case OperandType.InlineString:
case OperandType.InlineTok:
case OperandType.InlineType:
case OperandType.ShortInlineR:
num += 4;
break;
case OperandType.InlineVar:
num += 2;
break;
case OperandType.ShortInlineBrTarget:
case OperandType.ShortInlineI:
case OperandType.ShortInlineVar:
num++;
break;
}
return num;
}
public override string ToString()
{
string str = "";
AppendLabel(ref str, this);
str = str + ": " + opcode.Name;
if (operand == null)
{
return str;
}
str += " ";
switch (opcode.OperandType)
{
case OperandType.InlineBrTarget:
case OperandType.ShortInlineBrTarget:
AppendLabel(ref str, operand);
break;
case OperandType.InlineSwitch:
{
ILInstruction[] array = (ILInstruction[])operand;
for (int i = 0; i < array.Length; i++)
{
if (i > 0)
{
str += ",";
}
AppendLabel(ref str, array[i]);
}
break;
}
case OperandType.InlineString:
str += $"\"{operand}\"";
break;
default:
str += operand;
break;
}
return str;
}
private static void AppendLabel(ref string str, object argument)
{
ILInstruction iLInstruction = argument as ILInstruction;
str += $"IL_{iLInstruction?.offset.ToString("X4") ?? argument}";
}
}

View file

@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Mono.Cecil;
using MonoMod.Utils;
namespace HarmonyLib;
internal class InlineSignature : ICallSiteGenerator
{
public class ModifierType
{
public bool IsOptional;
public Type Modifier;
public object Type;
public override string ToString()
{
return $"{((Type is Type type) ? type.FullDescription() : Type?.ToString())} mod{(IsOptional ? "opt" : "req")}({Modifier?.FullDescription()})";
}
internal TypeReference ToTypeReference(ModuleDefinition module)
{
if (IsOptional)
{
return new OptionalModifierType(module.ImportReference(Modifier), GetTypeReference(module, Type));
}
return new RequiredModifierType(module.ImportReference(Modifier), GetTypeReference(module, Type));
}
}
public bool HasThis { get; set; }
public bool ExplicitThis { get; set; }
public CallingConvention CallingConvention { get; set; } = CallingConvention.Winapi;
public List<object> Parameters { get; set; } = new List<object>();
public object ReturnType { get; set; } = typeof(void);
public override string ToString()
{
return ((ReturnType is Type type) ? type.FullDescription() : ReturnType?.ToString()) + " (" + Parameters.Join((object p) => (!(p is Type type2)) ? p?.ToString() : type2.FullDescription()) + ")";
}
internal static TypeReference GetTypeReference(ModuleDefinition module, object param)
{
if (!(param is Type type))
{
if (!(param is InlineSignature inlineSignature))
{
if (param is ModifierType modifierType)
{
return modifierType.ToTypeReference(module);
}
throw new NotSupportedException($"Unsupported inline signature parameter type: {param} ({param?.GetType().FullDescription()})");
}
return inlineSignature.ToFunctionPointer(module);
}
return module.ImportReference(type);
}
CallSite ICallSiteGenerator.ToCallSite(ModuleDefinition module)
{
CallSite callSite = new CallSite(GetTypeReference(module, ReturnType))
{
HasThis = HasThis,
ExplicitThis = ExplicitThis,
CallingConvention = (MethodCallingConvention)((byte)CallingConvention - 1)
};
foreach (object parameter in Parameters)
{
callSite.Parameters.Add(new ParameterDefinition(GetTypeReference(module, parameter)));
}
return callSite;
}
private FunctionPointerType ToFunctionPointer(ModuleDefinition module)
{
FunctionPointerType functionPointerType = new FunctionPointerType
{
ReturnType = GetTypeReference(module, ReturnType),
HasThis = HasThis,
ExplicitThis = ExplicitThis,
CallingConvention = (MethodCallingConvention)((byte)CallingConvention - 1)
};
foreach (object parameter in Parameters)
{
functionPointerType.Parameters.Add(new ParameterDefinition(GetTypeReference(module, parameter)));
}
return functionPointerType;
}
}

View file

@ -0,0 +1,205 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using Mono.Cecil;
namespace HarmonyLib;
internal static class InlineSignatureParser
{
internal static InlineSignature ImportCallSite(Module moduleFrom, byte[] data)
{
InlineSignature inlineSignature = new InlineSignature();
BinaryReader reader;
using (MemoryStream input = new MemoryStream(data, writable: false))
{
reader = new BinaryReader(input);
try
{
ReadMethodSignature(inlineSignature);
return inlineSignature;
}
finally
{
if (reader != null)
{
((IDisposable)reader).Dispose();
}
}
}
Type GetTypeDefOrRef()
{
uint num = ReadCompressedUInt();
uint num2 = num >> 2;
return moduleFrom.ResolveType((num & 3) switch
{
0u => (int)(0x2000000 | num2),
1u => (int)(0x1000000 | num2),
2u => (int)(0x1B000000 | num2),
_ => 0,
});
}
int ReadCompressedInt()
{
byte b = reader.ReadByte();
reader.BaseStream.Seek(-1L, SeekOrigin.Current);
int num = (int)ReadCompressedUInt();
int num2 = num >> 1;
if ((num & 1) == 0)
{
return num2;
}
switch (b & 0xC0)
{
case 0:
case 64:
return num2 - 64;
case 128:
return num2 - 8192;
default:
return num2 - 268435456;
}
}
uint ReadCompressedUInt()
{
byte b = reader.ReadByte();
if ((b & 0x80) == 0)
{
return b;
}
if ((b & 0x40) == 0)
{
return (uint)(((b & -129) << 8) | reader.ReadByte());
}
return (uint)(((b & -193) << 24) | (reader.ReadByte() << 16) | (reader.ReadByte() << 8) | reader.ReadByte());
}
void ReadMethodSignature(InlineSignature method)
{
byte b = reader.ReadByte();
if ((b & 0x20) != 0)
{
method.HasThis = true;
b = (byte)(b & -33);
}
if ((b & 0x40) != 0)
{
method.ExplicitThis = true;
b = (byte)(b & -65);
}
method.CallingConvention = (CallingConvention)(b + 1);
if ((b & 0x10) != 0)
{
ReadCompressedUInt();
}
uint num = ReadCompressedUInt();
method.ReturnType = ReadTypeSignature();
for (int i = 0; i < num; i++)
{
method.Parameters.Add(ReadTypeSignature());
}
}
object ReadTypeSignature()
{
MetadataType metadataType = (MetadataType)reader.ReadByte();
switch (metadataType)
{
case MetadataType.ValueType:
case MetadataType.Class:
return GetTypeDefOrRef();
case MetadataType.Pointer:
return ((Type)ReadTypeSignature()).MakePointerType();
case MetadataType.FunctionPointer:
{
InlineSignature inlineSignature2 = new InlineSignature();
ReadMethodSignature(inlineSignature2);
return inlineSignature2;
}
case MetadataType.ByReference:
return ((Type)ReadTypeSignature()).MakePointerType();
case (MetadataType)29:
return ((Type)ReadTypeSignature()).MakeArrayType();
case MetadataType.Array:
{
Type type2 = (Type)ReadTypeSignature();
uint rank = ReadCompressedUInt();
uint num = ReadCompressedUInt();
for (int num2 = 0; num2 < num; num2++)
{
ReadCompressedUInt();
}
uint num3 = ReadCompressedUInt();
for (int num4 = 0; num4 < num3; num4++)
{
ReadCompressedInt();
}
return type2.MakeArrayType((int)rank);
}
case MetadataType.OptionalModifier:
return new InlineSignature.ModifierType
{
IsOptional = true,
Modifier = GetTypeDefOrRef(),
Type = ReadTypeSignature()
};
case MetadataType.RequiredModifier:
return new InlineSignature.ModifierType
{
IsOptional = false,
Modifier = GetTypeDefOrRef(),
Type = ReadTypeSignature()
};
case MetadataType.Var:
case MetadataType.MVar:
throw new NotSupportedException($"Unsupported generic callsite element: {metadataType}");
case MetadataType.GenericInstance:
{
reader.ReadByte();
Type type = GetTypeDefOrRef();
int count = (int)ReadCompressedUInt();
return type.MakeGenericType((from _ in Enumerable.Range(0, count)
select (Type)ReadTypeSignature()).ToArray());
}
case MetadataType.Object:
return typeof(object);
case MetadataType.Void:
return typeof(void);
case MetadataType.TypedByReference:
return typeof(TypedReference);
case MetadataType.IntPtr:
return typeof(IntPtr);
case MetadataType.UIntPtr:
return typeof(UIntPtr);
case MetadataType.Boolean:
return typeof(bool);
case MetadataType.Char:
return typeof(char);
case MetadataType.SByte:
return typeof(sbyte);
case MetadataType.Byte:
return typeof(byte);
case MetadataType.Int16:
return typeof(short);
case MetadataType.UInt16:
return typeof(ushort);
case MetadataType.Int32:
return typeof(int);
case MetadataType.UInt32:
return typeof(uint);
case MetadataType.Int64:
return typeof(long);
case MetadataType.UInt64:
return typeof(ulong);
case MetadataType.Single:
return typeof(float);
case MetadataType.Double:
return typeof(double);
case MetadataType.String:
return typeof(string);
default:
throw new NotSupportedException($"Unsupported callsite element: {metadataType}");
}
}
}
}

View file

@ -0,0 +1,3 @@
namespace HarmonyLib;
public delegate T InstantiationHandler<out T>();

View file

@ -0,0 +1,9 @@
namespace HarmonyLib;
internal class LeaveTry
{
public override string ToString()
{
return "(autogenerated)";
}
}

View file

@ -0,0 +1,11 @@
using System.Reflection;
namespace HarmonyLib;
public static class MethodBaseExtensions
{
public static bool HasMethodBody(this MethodBase member)
{
return (member.GetMethodBody()?.GetILAsByteArray()?.Length).GetValueOrDefault() > 0;
}
}

View file

@ -0,0 +1,765 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using MonoMod.Utils;
using MonoMod.Utils.Cil;
namespace HarmonyLib;
internal class MethodBodyReader
{
private class ThisParameter : ParameterInfo
{
internal ThisParameter(MethodBase method)
{
MemberImpl = method;
ClassImpl = method.DeclaringType;
NameImpl = "this";
PositionImpl = -1;
}
}
private readonly ILGenerator generator;
private readonly MethodBase method;
private bool debug;
private readonly Module module;
private readonly Type[] typeArguments;
private readonly Type[] methodArguments;
private readonly ByteBuffer ilBytes;
private readonly ParameterInfo this_parameter;
private readonly ParameterInfo[] parameters;
private readonly IList<ExceptionHandlingClause> exceptions;
private readonly List<ILInstruction> ilInstructions;
private readonly List<LocalVariableInfo> localVariables;
private LocalBuilder[] variables;
private static readonly Dictionary<OpCode, OpCode> shortJumps;
private static readonly OpCode[] one_byte_opcodes;
private static readonly OpCode[] two_bytes_opcodes;
internal static List<ILInstruction> GetInstructions(ILGenerator generator, MethodBase method)
{
if ((object)method == null)
{
throw new ArgumentNullException("method");
}
MethodBodyReader methodBodyReader = new MethodBodyReader(method, generator);
methodBodyReader.DeclareVariables(null);
methodBodyReader.GenerateInstructions();
return methodBodyReader.ilInstructions;
}
internal MethodBodyReader(MethodBase method, ILGenerator generator)
{
this.generator = generator;
this.method = method;
module = method.Module;
MethodBody methodBody = method.GetMethodBody();
if ((methodBody?.GetILAsByteArray()?.Length).GetValueOrDefault() == 0)
{
ilBytes = new ByteBuffer(Array.Empty<byte>());
ilInstructions = new List<ILInstruction>();
}
else
{
byte[] iLAsByteArray = methodBody.GetILAsByteArray();
if (iLAsByteArray == null)
{
throw new ArgumentException("Can not get IL bytes of method " + method.FullDescription());
}
ilBytes = new ByteBuffer(iLAsByteArray);
ilInstructions = new List<ILInstruction>((iLAsByteArray.Length + 1) / 2);
}
Type declaringType = method.DeclaringType;
if ((object)declaringType != null && declaringType.IsGenericType)
{
try
{
typeArguments = declaringType.GetGenericArguments();
}
catch
{
typeArguments = null;
}
}
if (method.IsGenericMethod)
{
try
{
methodArguments = method.GetGenericArguments();
}
catch
{
methodArguments = null;
}
}
if (!method.IsStatic)
{
this_parameter = new ThisParameter(method);
}
parameters = method.GetParameters();
localVariables = methodBody?.LocalVariables?.ToList() ?? new List<LocalVariableInfo>();
exceptions = methodBody?.ExceptionHandlingClauses ?? new List<ExceptionHandlingClause>();
}
internal void SetDebugging(bool debug)
{
this.debug = debug;
}
internal void GenerateInstructions()
{
while (ilBytes.position < ilBytes.buffer.Length)
{
int position = ilBytes.position;
ILInstruction iLInstruction = new ILInstruction(ReadOpCode())
{
offset = position
};
ReadOperand(iLInstruction);
ilInstructions.Add(iLInstruction);
}
HandleNativeMethod();
ResolveBranches();
ParseExceptions();
}
internal void HandleNativeMethod()
{
if (!(method is MethodInfo methodInfo))
{
return;
}
DllImportAttribute dllImportAttribute = methodInfo.GetCustomAttributes(inherit: false).OfType<DllImportAttribute>().FirstOrDefault();
if (dllImportAttribute != null)
{
string assemblyName = (methodInfo.DeclaringType?.FullName ?? "").Replace(".", "_") + "_" + methodInfo.Name;
AssemblyName assemblyName2 = new AssemblyName(assemblyName);
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName2, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName2.Name);
TypeBuilder typeBuilder = moduleBuilder.DefineType("NativeMethodHolder", TypeAttributes.Public | TypeAttributes.UnicodeClass);
MethodBuilder methodBuilder = typeBuilder.DefinePInvokeMethod(methodInfo.Name, dllImportAttribute.Value, MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.PinvokeImpl, CallingConventions.Standard, methodInfo.ReturnType, (from x in methodInfo.GetParameters()
select x.ParameterType).ToArray(), dllImportAttribute.CallingConvention, dllImportAttribute.CharSet);
methodBuilder.SetImplementationFlags(methodBuilder.GetMethodImplementationFlags() | MethodImplAttributes.PreserveSig);
Type type = typeBuilder.CreateType();
MethodInfo operand = type.GetMethod(methodInfo.Name);
int num = method.GetParameters().Length;
for (int num2 = 0; num2 < num; num2++)
{
ilInstructions.Add(new ILInstruction(OpCodes.Ldarg, num2)
{
offset = 0
});
}
ilInstructions.Add(new ILInstruction(OpCodes.Call, operand)
{
offset = num
});
ilInstructions.Add(new ILInstruction(OpCodes.Ret)
{
offset = num + 5
});
}
}
internal void DeclareVariables(LocalBuilder[] existingVariables)
{
if (generator == null)
{
return;
}
if (existingVariables != null)
{
variables = existingVariables;
return;
}
variables = localVariables.Select((LocalVariableInfo lvi) => generator.DeclareLocal(lvi.LocalType, lvi.IsPinned)).ToArray();
}
private void ResolveBranches()
{
foreach (ILInstruction ilInstruction in ilInstructions)
{
switch (ilInstruction.opcode.OperandType)
{
case OperandType.InlineBrTarget:
case OperandType.ShortInlineBrTarget:
ilInstruction.operand = GetInstruction((int)ilInstruction.operand, isEndOfInstruction: false);
break;
case OperandType.InlineSwitch:
{
int[] array = (int[])ilInstruction.operand;
ILInstruction[] array2 = new ILInstruction[array.Length];
for (int i = 0; i < array.Length; i++)
{
array2[i] = GetInstruction(array[i], isEndOfInstruction: false);
}
ilInstruction.operand = array2;
break;
}
}
}
}
private void ParseExceptions()
{
foreach (ExceptionHandlingClause exception in exceptions)
{
int tryOffset = exception.TryOffset;
int handlerOffset = exception.HandlerOffset;
int offset = exception.HandlerOffset + exception.HandlerLength - 1;
ILInstruction instruction = GetInstruction(tryOffset, isEndOfInstruction: false);
instruction.blocks.Add(new ExceptionBlock(ExceptionBlockType.BeginExceptionBlock));
ILInstruction instruction2 = GetInstruction(offset, isEndOfInstruction: true);
instruction2.blocks.Add(new ExceptionBlock(ExceptionBlockType.EndExceptionBlock));
switch (exception.Flags)
{
case ExceptionHandlingClauseOptions.Filter:
{
ILInstruction instruction6 = GetInstruction(exception.FilterOffset, isEndOfInstruction: false);
instruction6.blocks.Add(new ExceptionBlock(ExceptionBlockType.BeginExceptFilterBlock));
break;
}
case ExceptionHandlingClauseOptions.Finally:
{
ILInstruction instruction5 = GetInstruction(handlerOffset, isEndOfInstruction: false);
instruction5.blocks.Add(new ExceptionBlock(ExceptionBlockType.BeginFinallyBlock));
break;
}
case ExceptionHandlingClauseOptions.Clause:
{
ILInstruction instruction4 = GetInstruction(handlerOffset, isEndOfInstruction: false);
instruction4.blocks.Add(new ExceptionBlock(ExceptionBlockType.BeginCatchBlock, exception.CatchType));
break;
}
case ExceptionHandlingClauseOptions.Fault:
{
ILInstruction instruction3 = GetInstruction(handlerOffset, isEndOfInstruction: false);
instruction3.blocks.Add(new ExceptionBlock(ExceptionBlockType.BeginFaultBlock));
break;
}
}
}
}
private bool EndsInDeadCode(List<CodeInstruction> list)
{
int count = list.Count;
if (count < 2 || list.Last().opcode != OpCodes.Throw)
{
return false;
}
return list.GetRange(0, count - 1).All((CodeInstruction code) => code.opcode != OpCodes.Ret);
}
internal List<CodeInstruction> FinalizeILCodes(Emitter emitter, List<MethodInfo> transpilers, List<Label> endLabels, out bool hasReturnCode, out bool methodEndsInDeadCode)
{
hasReturnCode = false;
methodEndsInDeadCode = false;
if (generator == null)
{
return null;
}
foreach (ILInstruction ilInstruction in ilInstructions)
{
switch (ilInstruction.opcode.OperandType)
{
case OperandType.InlineSwitch:
if (ilInstruction.operand is ILInstruction[] array)
{
List<Label> list = new List<Label>();
ILInstruction[] array2 = array;
foreach (ILInstruction iLInstruction2 in array2)
{
Label item = generator.DefineLabel();
iLInstruction2.labels.Add(item);
list.Add(item);
}
ilInstruction.argument = list.ToArray();
}
break;
case OperandType.InlineBrTarget:
case OperandType.ShortInlineBrTarget:
if (ilInstruction.operand is ILInstruction iLInstruction)
{
Label label = generator.DefineLabel();
iLInstruction.labels.Add(label);
ilInstruction.argument = label;
}
break;
}
}
CodeTranspiler codeTranspiler = new CodeTranspiler(ilInstructions);
transpilers.Do(delegate(MethodInfo transpiler)
{
codeTranspiler.Add(transpiler);
});
List<CodeInstruction> result = codeTranspiler.GetResult(generator, method);
if (emitter == null)
{
return result;
}
emitter.LogComment("start original");
if (debug)
{
List<string> buffer = FileLog.GetBuffer(clear: true);
emitter.LogAllLocalVariables();
FileLog.LogBuffered(buffer);
}
hasReturnCode = result.Any((CodeInstruction code) => code.opcode == OpCodes.Ret);
methodEndsInDeadCode = EndsInDeadCode(result);
while (true)
{
CodeInstruction codeInstruction = result.LastOrDefault();
if (codeInstruction == null || codeInstruction.opcode != OpCodes.Ret)
{
break;
}
endLabels.AddRange(codeInstruction.labels);
result.RemoveAt(result.Count - 1);
}
result.Do(delegate(CodeInstruction codeInstruction2)
{
codeInstruction2.labels.Do(delegate(Label label3)
{
emitter.MarkLabel(label3);
});
codeInstruction2.blocks.Do(delegate(ExceptionBlock block)
{
emitter.MarkBlockBefore(block, out var _);
});
OpCode opCode = codeInstruction2.opcode;
object obj = codeInstruction2.operand;
if (opCode == OpCodes.Ret)
{
Label label2 = generator.DefineLabel();
opCode = OpCodes.Br;
obj = label2;
endLabels.Add(label2);
}
if (shortJumps.TryGetValue(opCode, out var value))
{
opCode = value;
}
switch (opCode.OperandType)
{
case OperandType.InlineNone:
emitter.Emit(opCode);
break;
case OperandType.InlineSig:
{
CecilILGenerator proxiedShim = generator.GetProxiedShim<CecilILGenerator>();
if (proxiedShim == null)
{
throw new NotSupportedException();
}
if (obj == null)
{
throw new Exception($"Wrong null argument: {codeInstruction2}");
}
if (!(obj is ICallSiteGenerator))
{
throw new Exception($"Wrong Emit argument type {obj.GetType()} in {codeInstruction2}");
}
emitter.AddInstruction(opCode, obj);
emitter.LogIL(opCode, obj);
proxiedShim.Emit(opCode, (ICallSiteGenerator)obj);
break;
}
default:
if (obj == null)
{
throw new Exception($"Wrong null argument: {codeInstruction2}");
}
emitter.AddInstruction(opCode, obj);
emitter.LogIL(opCode, obj);
generator.DynEmit(opCode, obj);
break;
}
codeInstruction2.blocks.Do(delegate(ExceptionBlock block)
{
emitter.MarkBlockAfter(block);
});
});
emitter.LogComment("end original" + (methodEndsInDeadCode ? " (has dead code end)" : ""));
return result;
}
private static void GetMemberInfoValue(MemberInfo info, out object result)
{
result = null;
switch (info.MemberType)
{
case MemberTypes.Constructor:
result = (ConstructorInfo)info;
break;
case MemberTypes.Event:
result = (EventInfo)info;
break;
case MemberTypes.Field:
result = (FieldInfo)info;
break;
case MemberTypes.Method:
result = (MethodInfo)info;
break;
case MemberTypes.TypeInfo:
case MemberTypes.NestedType:
result = (Type)info;
break;
case MemberTypes.Property:
result = (PropertyInfo)info;
break;
}
}
private void ReadOperand(ILInstruction instruction)
{
switch (instruction.opcode.OperandType)
{
case OperandType.InlineNone:
instruction.argument = null;
break;
case OperandType.InlineSwitch:
{
int num3 = ilBytes.ReadInt32();
int num4 = ilBytes.position + 4 * num3;
int[] array2 = new int[num3];
for (int i = 0; i < num3; i++)
{
array2[i] = ilBytes.ReadInt32() + num4;
}
instruction.operand = array2;
break;
}
case OperandType.ShortInlineBrTarget:
{
sbyte b = (sbyte)ilBytes.ReadByte();
instruction.operand = b + ilBytes.position;
break;
}
case OperandType.InlineBrTarget:
{
int num5 = ilBytes.ReadInt32();
instruction.operand = num5 + ilBytes.position;
break;
}
case OperandType.ShortInlineI:
if (instruction.opcode == OpCodes.Ldc_I4_S)
{
sbyte b3 = (sbyte)ilBytes.ReadByte();
instruction.operand = b3;
instruction.argument = (sbyte)instruction.operand;
}
else
{
byte b4 = ilBytes.ReadByte();
instruction.operand = b4;
instruction.argument = (byte)instruction.operand;
}
break;
case OperandType.InlineI:
{
int num8 = ilBytes.ReadInt32();
instruction.operand = num8;
instruction.argument = (int)instruction.operand;
break;
}
case OperandType.ShortInlineR:
{
float num2 = ilBytes.ReadSingle();
instruction.operand = num2;
instruction.argument = (float)instruction.operand;
break;
}
case OperandType.InlineR:
{
double num7 = ilBytes.ReadDouble();
instruction.operand = num7;
instruction.argument = (double)instruction.operand;
break;
}
case OperandType.InlineI8:
{
long num6 = ilBytes.ReadInt64();
instruction.operand = num6;
instruction.argument = (long)instruction.operand;
break;
}
case OperandType.InlineSig:
{
int metadataToken3 = ilBytes.ReadInt32();
byte[] array3 = module.ResolveSignature(metadataToken3);
InlineSignature value = (InlineSignature)(instruction.argument = (instruction.operand = InlineSignatureParser.ImportCallSite(module, array3)));
Debugger.Log(0, "TEST", "METHOD " + method.FullDescription() + "\n");
Debugger.Log(0, "TEST", "Signature Blob = " + array3.Select((byte b5) => $"0x{b5:x02}").Aggregate((string a, string text) => a + " " + text) + "\n");
Debugger.Log(0, "TEST", $"Signature = {value}\n");
Debugger.Break();
break;
}
case OperandType.InlineString:
{
int metadataToken6 = ilBytes.ReadInt32();
instruction.operand = module.ResolveString(metadataToken6);
instruction.argument = (string)instruction.operand;
break;
}
case OperandType.InlineTok:
{
int metadataToken5 = ilBytes.ReadInt32();
instruction.operand = module.ResolveMember(metadataToken5, typeArguments, methodArguments);
((MemberInfo)instruction.operand).DeclaringType?.FixReflectionCacheAuto();
GetMemberInfoValue((MemberInfo)instruction.operand, out instruction.argument);
break;
}
case OperandType.InlineType:
{
int metadataToken4 = ilBytes.ReadInt32();
instruction.operand = module.ResolveType(metadataToken4, typeArguments, methodArguments);
((Type)instruction.operand).FixReflectionCacheAuto();
instruction.argument = (Type)instruction.operand;
break;
}
case OperandType.InlineMethod:
{
int metadataToken2 = ilBytes.ReadInt32();
instruction.operand = module.ResolveMethod(metadataToken2, typeArguments, methodArguments);
((MemberInfo)instruction.operand).DeclaringType?.FixReflectionCacheAuto();
if (instruction.operand is ConstructorInfo)
{
instruction.argument = (ConstructorInfo)instruction.operand;
}
else
{
instruction.argument = (MethodInfo)instruction.operand;
}
break;
}
case OperandType.InlineField:
{
int metadataToken = ilBytes.ReadInt32();
instruction.operand = module.ResolveField(metadataToken, typeArguments, methodArguments);
((MemberInfo)instruction.operand).DeclaringType?.FixReflectionCacheAuto();
instruction.argument = (FieldInfo)instruction.operand;
break;
}
case OperandType.ShortInlineVar:
{
byte b2 = ilBytes.ReadByte();
if (TargetsLocalVariable(instruction.opcode))
{
LocalVariableInfo localVariable2 = GetLocalVariable(b2);
if (localVariable2 == null)
{
instruction.argument = b2;
break;
}
instruction.operand = localVariable2;
LocalBuilder[] array4 = variables;
instruction.argument = ((array4 != null) ? array4[localVariable2.LocalIndex] : null) ?? localVariable2;
}
else
{
instruction.operand = GetParameter(b2);
instruction.argument = b2;
}
break;
}
case OperandType.InlineVar:
{
short num = ilBytes.ReadInt16();
if (TargetsLocalVariable(instruction.opcode))
{
LocalVariableInfo localVariable = GetLocalVariable(num);
if (localVariable == null)
{
instruction.argument = num;
break;
}
instruction.operand = localVariable;
LocalBuilder[] array = variables;
instruction.argument = ((array != null) ? array[localVariable.LocalIndex] : null) ?? localVariable;
}
else
{
instruction.operand = GetParameter(num);
instruction.argument = num;
}
break;
}
default:
throw new NotSupportedException();
}
}
private ILInstruction GetInstruction(int offset, bool isEndOfInstruction)
{
if (offset < 0)
{
throw new ArgumentOutOfRangeException("offset", offset, $"Instruction offset {offset} is less than 0");
}
int num = ilInstructions.Count - 1;
ILInstruction iLInstruction = ilInstructions[num];
if (offset > iLInstruction.offset + iLInstruction.GetSize() - 1)
{
throw new ArgumentOutOfRangeException("offset", offset, $"Instruction offset {offset} is outside valid range 0 - {iLInstruction.offset + iLInstruction.GetSize() - 1}");
}
int num2 = 0;
int num3 = num;
while (num2 <= num3)
{
int num4 = num2 + (num3 - num2) / 2;
iLInstruction = ilInstructions[num4];
if (isEndOfInstruction)
{
if (offset == iLInstruction.offset + iLInstruction.GetSize() - 1)
{
return iLInstruction;
}
}
else if (offset == iLInstruction.offset)
{
return iLInstruction;
}
if (offset < iLInstruction.offset)
{
num3 = num4 - 1;
}
else
{
num2 = num4 + 1;
}
}
throw new Exception($"Cannot find instruction for {offset:X4}");
}
private static bool TargetsLocalVariable(OpCode opcode)
{
return opcode.Name.Contains("loc");
}
private LocalVariableInfo GetLocalVariable(int index)
{
return localVariables?[index];
}
private ParameterInfo GetParameter(int index)
{
if (index == 0)
{
return this_parameter;
}
return parameters[index - 1];
}
private OpCode ReadOpCode()
{
byte b = ilBytes.ReadByte();
if (b == 254)
{
return two_bytes_opcodes[ilBytes.ReadByte()];
}
return one_byte_opcodes[b];
}
[MethodImpl(MethodImplOptions.Synchronized)]
static MethodBodyReader()
{
shortJumps = new Dictionary<OpCode, OpCode>
{
{
OpCodes.Leave_S,
OpCodes.Leave
},
{
OpCodes.Brfalse_S,
OpCodes.Brfalse
},
{
OpCodes.Brtrue_S,
OpCodes.Brtrue
},
{
OpCodes.Beq_S,
OpCodes.Beq
},
{
OpCodes.Bge_S,
OpCodes.Bge
},
{
OpCodes.Bgt_S,
OpCodes.Bgt
},
{
OpCodes.Ble_S,
OpCodes.Ble
},
{
OpCodes.Blt_S,
OpCodes.Blt
},
{
OpCodes.Bne_Un_S,
OpCodes.Bne_Un
},
{
OpCodes.Bge_Un_S,
OpCodes.Bge_Un
},
{
OpCodes.Bgt_Un_S,
OpCodes.Bgt_Un
},
{
OpCodes.Ble_Un_S,
OpCodes.Ble_Un
},
{
OpCodes.Br_S,
OpCodes.Br
},
{
OpCodes.Blt_Un_S,
OpCodes.Blt_Un
}
};
one_byte_opcodes = new OpCode[225];
two_bytes_opcodes = new OpCode[31];
FieldInfo[] fields = typeof(OpCodes).GetFields(BindingFlags.Static | BindingFlags.Public);
FieldInfo[] array = fields;
foreach (FieldInfo fieldInfo in array)
{
OpCode opCode = (OpCode)fieldInfo.GetValue(null);
if (opCode.OpCodeType != OpCodeType.Nternal)
{
if (opCode.Size == 1)
{
one_byte_opcodes[opCode.Value] = opCode;
}
else
{
two_bytes_opcodes[opCode.Value & 0xFF] = opCode;
}
}
}
}
}

View file

@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Reflection.Emit;
namespace HarmonyLib;
internal class MethodCopier
{
private readonly MethodBodyReader reader;
private readonly List<MethodInfo> transpilers = new List<MethodInfo>();
internal MethodCopier(MethodBase fromMethod, ILGenerator toILGenerator, LocalBuilder[] existingVariables = null)
{
if ((object)fromMethod == null)
{
throw new ArgumentNullException("fromMethod");
}
reader = new MethodBodyReader(fromMethod, toILGenerator);
reader.DeclareVariables(existingVariables);
reader.GenerateInstructions();
}
internal void SetDebugging(bool debug)
{
reader.SetDebugging(debug);
}
internal void AddTranspiler(MethodInfo transpiler)
{
transpilers.Add(transpiler);
}
internal List<CodeInstruction> Finalize(Emitter emitter, List<Label> endLabels, out bool hasReturnCode, out bool methodEndsInDeadCode)
{
return reader.FinalizeILCodes(emitter, transpilers, endLabels, out hasReturnCode, out methodEndsInDeadCode);
}
internal static List<CodeInstruction> GetInstructions(ILGenerator generator, MethodBase method, int maxTranspilers)
{
if (generator == null)
{
throw new ArgumentNullException("generator");
}
if ((object)method == null)
{
throw new ArgumentNullException("method");
}
LocalBuilder[] existingVariables = MethodPatcher.DeclareOriginalLocalVariables(generator, method);
MethodCopier methodCopier = new MethodCopier(method, generator, existingVariables);
Patches patchInfo = Harmony.GetPatchInfo(method);
if (patchInfo != null)
{
ReadOnlyCollection<Patch> readOnlyCollection = patchInfo.Transpilers;
int num = 0;
Patch[] array = new Patch[readOnlyCollection.Count];
foreach (Patch item in readOnlyCollection)
{
array[num] = item;
num++;
}
List<MethodInfo> sortedPatchMethods = PatchFunctions.GetSortedPatchMethods(method, array, debug: false);
for (int i = 0; i < maxTranspilers && i < sortedPatchMethods.Count; i++)
{
methodCopier.AddTranspiler(sortedPatchMethods[i]);
}
}
bool hasReturnCode;
bool methodEndsInDeadCode;
return methodCopier.Finalize(null, null, out hasReturnCode, out methodEndsInDeadCode);
}
}

View file

@ -0,0 +1,7 @@
namespace HarmonyLib;
public enum MethodDispatchType
{
VirtualCall,
Call
}

View file

@ -0,0 +1,169 @@
using System;
using System.Reflection;
using System.Reflection.Emit;
using MonoMod.Utils;
namespace HarmonyLib;
public static class MethodInvoker
{
public static FastInvokeHandler GetHandler(MethodInfo methodInfo, bool directBoxValueAccess = false)
{
DynamicMethodDefinition dynamicMethodDefinition = new DynamicMethodDefinition("FastInvoke_" + methodInfo.Name + "_" + (directBoxValueAccess ? "direct" : "indirect"), typeof(object), new Type[2]
{
typeof(object),
typeof(object[])
});
ILGenerator iLGenerator = dynamicMethodDefinition.GetILGenerator();
if (!methodInfo.IsStatic)
{
Emit(iLGenerator, OpCodes.Ldarg_0);
EmitUnboxIfNeeded(iLGenerator, methodInfo.DeclaringType);
}
bool flag = true;
ParameterInfo[] parameters = methodInfo.GetParameters();
for (int i = 0; i < parameters.Length; i++)
{
Type type = parameters[i].ParameterType;
bool isByRef = type.IsByRef;
if (isByRef)
{
type = type.GetElementType();
}
bool isValueType = type.IsValueType;
if (isByRef && isValueType && !directBoxValueAccess)
{
Emit(iLGenerator, OpCodes.Ldarg_1);
EmitFastInt(iLGenerator, i);
}
Emit(iLGenerator, OpCodes.Ldarg_1);
EmitFastInt(iLGenerator, i);
if (isByRef && !isValueType)
{
Emit(iLGenerator, OpCodes.Ldelema, typeof(object));
continue;
}
Emit(iLGenerator, OpCodes.Ldelem_Ref);
if (!isValueType)
{
continue;
}
if (!isByRef || !directBoxValueAccess)
{
Emit(iLGenerator, OpCodes.Unbox_Any, type);
if (isByRef)
{
Emit(iLGenerator, OpCodes.Box, type);
Emit(iLGenerator, OpCodes.Dup);
if (flag)
{
flag = false;
iLGenerator.DeclareLocal(typeof(object), pinned: false);
}
Emit(iLGenerator, OpCodes.Stloc_0);
Emit(iLGenerator, OpCodes.Stelem_Ref);
Emit(iLGenerator, OpCodes.Ldloc_0);
Emit(iLGenerator, OpCodes.Unbox, type);
}
}
else
{
Emit(iLGenerator, OpCodes.Unbox, type);
}
}
if (methodInfo.IsStatic)
{
EmitCall(iLGenerator, OpCodes.Call, methodInfo);
}
else
{
EmitCall(iLGenerator, OpCodes.Callvirt, methodInfo);
}
if (methodInfo.ReturnType == typeof(void))
{
Emit(iLGenerator, OpCodes.Ldnull);
}
else
{
EmitBoxIfNeeded(iLGenerator, methodInfo.ReturnType);
}
Emit(iLGenerator, OpCodes.Ret);
return (FastInvokeHandler)dynamicMethodDefinition.Generate().CreateDelegate(typeof(FastInvokeHandler));
}
internal static void Emit(ILGenerator il, OpCode opcode)
{
il.Emit(opcode);
}
internal static void Emit(ILGenerator il, OpCode opcode, Type type)
{
il.Emit(opcode, type);
}
internal static void EmitCall(ILGenerator il, OpCode opcode, MethodInfo methodInfo)
{
il.EmitCall(opcode, methodInfo, null);
}
private static void EmitUnboxIfNeeded(ILGenerator il, Type type)
{
if (type.IsValueType)
{
Emit(il, OpCodes.Unbox_Any, type);
}
}
private static void EmitBoxIfNeeded(ILGenerator il, Type type)
{
if (type.IsValueType)
{
Emit(il, OpCodes.Box, type);
}
}
internal static void EmitFastInt(ILGenerator il, int value)
{
switch (value)
{
case -1:
il.Emit(OpCodes.Ldc_I4_M1);
return;
case 0:
il.Emit(OpCodes.Ldc_I4_0);
return;
case 1:
il.Emit(OpCodes.Ldc_I4_1);
return;
case 2:
il.Emit(OpCodes.Ldc_I4_2);
return;
case 3:
il.Emit(OpCodes.Ldc_I4_3);
return;
case 4:
il.Emit(OpCodes.Ldc_I4_4);
return;
case 5:
il.Emit(OpCodes.Ldc_I4_5);
return;
case 6:
il.Emit(OpCodes.Ldc_I4_6);
return;
case 7:
il.Emit(OpCodes.Ldc_I4_7);
return;
case 8:
il.Emit(OpCodes.Ldc_I4_8);
return;
}
if (value > -129 && value < 128)
{
il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
}
else
{
il.Emit(OpCodes.Ldc_I4, value);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,12 @@
namespace HarmonyLib;
public enum MethodType
{
Normal,
Getter,
Setter,
Constructor,
StaticConstructor,
Enumerator,
Async
}

View file

@ -0,0 +1,124 @@
using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
namespace HarmonyLib;
[Serializable]
public class Patch : IComparable
{
public readonly int index;
public readonly string owner;
public readonly int priority;
public readonly string[] before;
public readonly string[] after;
public readonly bool debug;
[NonSerialized]
private MethodInfo patchMethod;
private int methodToken;
private string moduleGUID;
public MethodInfo PatchMethod
{
get
{
if ((object)patchMethod == null)
{
Module module = (from a in AppDomain.CurrentDomain.GetAssemblies()
where !a.FullName.StartsWith("Microsoft.VisualStudio")
select a).SelectMany((Assembly a) => a.GetLoadedModules()).First((Module m) => m.ModuleVersionId.ToString() == moduleGUID);
patchMethod = (MethodInfo)module.ResolveMethod(methodToken);
}
return patchMethod;
}
set
{
patchMethod = value;
methodToken = patchMethod.MetadataToken;
moduleGUID = patchMethod.Module.ModuleVersionId.ToString();
}
}
public Patch(MethodInfo patch, int index, string owner, int priority, string[] before, string[] after, bool debug)
{
if (patch is DynamicMethod)
{
throw new Exception("Cannot directly reference dynamic method \"" + patch.FullDescription() + "\" in Harmony. Use a factory method instead that will return the dynamic method.");
}
this.index = index;
this.owner = owner;
this.priority = ((priority == -1) ? 400 : priority);
this.before = before ?? Array.Empty<string>();
this.after = after ?? Array.Empty<string>();
this.debug = debug;
PatchMethod = patch;
}
public Patch(HarmonyMethod method, int index, string owner)
: this(method.method, index, owner, method.priority, method.before, method.after, method.debug == true)
{
}
internal Patch(int index, string owner, int priority, string[] before, string[] after, bool debug, int methodToken, string moduleGUID)
{
this.index = index;
this.owner = owner;
this.priority = ((priority == -1) ? 400 : priority);
this.before = before ?? Array.Empty<string>();
this.after = after ?? Array.Empty<string>();
this.debug = debug;
this.methodToken = methodToken;
this.moduleGUID = moduleGUID;
}
public MethodInfo GetMethod(MethodBase original)
{
MethodInfo methodInfo = PatchMethod;
if (methodInfo.ReturnType != typeof(DynamicMethod) && methodInfo.ReturnType != typeof(MethodInfo))
{
return methodInfo;
}
if (!methodInfo.IsStatic)
{
return methodInfo;
}
ParameterInfo[] parameters = methodInfo.GetParameters();
if (parameters.Length != 1)
{
return methodInfo;
}
if (parameters[0].ParameterType != typeof(MethodBase))
{
return methodInfo;
}
return methodInfo.Invoke(null, new object[1] { original }) as MethodInfo;
}
public override bool Equals(object obj)
{
if (obj != null && obj is Patch)
{
return PatchMethod == ((Patch)obj).PatchMethod;
}
return false;
}
public int CompareTo(object obj)
{
return PatchInfoSerialization.PriorityComparer(obj, index, priority);
}
public override int GetHashCode()
{
return PatchMethod.GetHashCode();
}
}

View file

@ -0,0 +1,123 @@
using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
namespace HarmonyLib;
internal static class PatchArgumentExtensions
{
private static HarmonyArgument[] AllHarmonyArguments(object[] attributes)
{
return (from attr in attributes
select (attr.GetType().Name != "HarmonyArgument") ? null : AccessTools.MakeDeepCopy<HarmonyArgument>(attr) into harg
where harg != null
select harg).ToArray();
}
private static HarmonyArgument GetArgumentAttribute(this ParameterInfo parameter)
{
object[] customAttributes = parameter.GetCustomAttributes(inherit: false);
return AllHarmonyArguments(customAttributes).FirstOrDefault();
}
private static HarmonyArgument[] GetArgumentAttributes(this MethodInfo method)
{
if ((object)method == null || method is DynamicMethod)
{
return null;
}
object[] customAttributes = method.GetCustomAttributes(inherit: false);
return AllHarmonyArguments(customAttributes);
}
private static HarmonyArgument[] GetArgumentAttributes(this Type type)
{
object[] customAttributes = type.GetCustomAttributes(inherit: false);
return AllHarmonyArguments(customAttributes);
}
private static string GetOriginalArgumentName(this ParameterInfo parameter, string[] originalParameterNames)
{
HarmonyArgument argumentAttribute = parameter.GetArgumentAttribute();
if (argumentAttribute == null)
{
return null;
}
if (!string.IsNullOrEmpty(argumentAttribute.OriginalName))
{
return argumentAttribute.OriginalName;
}
if (argumentAttribute.Index >= 0 && argumentAttribute.Index < originalParameterNames.Length)
{
return originalParameterNames[argumentAttribute.Index];
}
return null;
}
private static string GetOriginalArgumentName(HarmonyArgument[] attributes, string name, string[] originalParameterNames)
{
if (((attributes != null && attributes.Length != 0) ? 1 : 0) <= (false ? 1 : 0))
{
return null;
}
HarmonyArgument harmonyArgument = attributes.SingleOrDefault((HarmonyArgument p) => p.NewName == name);
if (harmonyArgument == null)
{
return null;
}
if (!string.IsNullOrEmpty(harmonyArgument.OriginalName))
{
return harmonyArgument.OriginalName;
}
if (originalParameterNames != null && harmonyArgument.Index >= 0 && harmonyArgument.Index < originalParameterNames.Length)
{
return originalParameterNames[harmonyArgument.Index];
}
return null;
}
private static string GetOriginalArgumentName(this MethodInfo method, string[] originalParameterNames, string name)
{
string originalArgumentName = GetOriginalArgumentName(((object)method != null) ? method.GetArgumentAttributes() : null, name, originalParameterNames);
if (originalArgumentName != null)
{
return originalArgumentName;
}
object attributes;
if ((object)method == null)
{
attributes = null;
}
else
{
Type declaringType = method.DeclaringType;
attributes = (((object)declaringType != null) ? declaringType.GetArgumentAttributes() : null);
}
originalArgumentName = GetOriginalArgumentName((HarmonyArgument[])attributes, name, originalParameterNames);
if (originalArgumentName != null)
{
return originalArgumentName;
}
return name;
}
internal static int GetArgumentIndex(this MethodInfo patch, string[] originalParameterNames, ParameterInfo patchParam)
{
if (patch is DynamicMethod)
{
return Array.IndexOf(originalParameterNames, patchParam.Name);
}
string originalArgumentName = patchParam.GetOriginalArgumentName(originalParameterNames);
if (originalArgumentName != null)
{
return Array.IndexOf(originalParameterNames, originalArgumentName);
}
originalArgumentName = patch.GetOriginalArgumentName(originalParameterNames, patchParam.Name);
if (originalArgumentName != null)
{
return Array.IndexOf(originalParameterNames, originalArgumentName);
}
return -1;
}
}

View file

@ -0,0 +1,363 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace HarmonyLib;
public class PatchClassProcessor
{
private readonly Harmony instance;
private readonly Type containerType;
private readonly HarmonyMethod containerAttributes;
private readonly Dictionary<Type, MethodInfo> auxilaryMethods;
private readonly List<AttributePatch> patchMethods;
private static readonly List<Type> auxilaryTypes = new List<Type>(4)
{
typeof(HarmonyPrepare),
typeof(HarmonyCleanup),
typeof(HarmonyTargetMethod),
typeof(HarmonyTargetMethods)
};
public string Category { get; set; }
public PatchClassProcessor(Harmony instance, Type type)
{
if (instance == null)
{
throw new ArgumentNullException("instance");
}
if ((object)type == null)
{
throw new ArgumentNullException("type");
}
this.instance = instance;
containerType = type;
List<HarmonyMethod> fromType = HarmonyMethodExtensions.GetFromType(type);
if (fromType == null || fromType.Count == 0)
{
return;
}
containerAttributes = HarmonyMethod.Merge(fromType);
HarmonyMethod harmonyMethod = containerAttributes;
MethodType valueOrDefault = harmonyMethod.methodType.GetValueOrDefault();
if (!harmonyMethod.methodType.HasValue)
{
valueOrDefault = MethodType.Normal;
harmonyMethod.methodType = valueOrDefault;
}
Category = containerAttributes.category;
auxilaryMethods = new Dictionary<Type, MethodInfo>();
foreach (Type auxilaryType in auxilaryTypes)
{
MethodInfo patchMethod = PatchTools.GetPatchMethod(containerType, auxilaryType.FullName);
if ((object)patchMethod != null)
{
auxilaryMethods[auxilaryType] = patchMethod;
}
}
patchMethods = PatchTools.GetPatchMethods(containerType);
foreach (AttributePatch patchMethod2 in patchMethods)
{
MethodInfo method = patchMethod2.info.method;
patchMethod2.info = containerAttributes.Merge(patchMethod2.info);
patchMethod2.info.method = method;
}
}
public List<MethodInfo> Patch()
{
if (containerAttributes == null)
{
return null;
}
Exception exception = null;
if (!RunMethod<HarmonyPrepare, bool>(defaultIfNotExisting: true, defaultIfFailing: false, null, Array.Empty<object>()))
{
RunMethod<HarmonyCleanup>(ref exception, Array.Empty<object>());
ReportException(exception, null);
return new List<MethodInfo>();
}
List<MethodInfo> result = new List<MethodInfo>();
MethodBase lastOriginal = null;
try
{
List<MethodBase> bulkMethods = GetBulkMethods();
if (bulkMethods.Count == 1)
{
lastOriginal = bulkMethods[0];
}
ReversePatch(ref lastOriginal);
result = ((bulkMethods.Count > 0) ? BulkPatch(bulkMethods, ref lastOriginal) : PatchWithAttributes(ref lastOriginal));
}
catch (Exception ex)
{
exception = ex;
}
RunMethod<HarmonyCleanup>(ref exception, new object[1] { exception });
ReportException(exception, lastOriginal);
return result;
}
private void ReversePatch(ref MethodBase lastOriginal)
{
for (int i = 0; i < patchMethods.Count; i++)
{
AttributePatch attributePatch = patchMethods[i];
if (attributePatch.type == HarmonyPatchType.ReversePatch)
{
MethodBase originalMethod = attributePatch.info.GetOriginalMethod();
if ((object)originalMethod != null)
{
lastOriginal = originalMethod;
}
ReversePatcher reversePatcher = instance.CreateReversePatcher(lastOriginal, attributePatch.info);
lock (PatchProcessor.locker)
{
reversePatcher.Patch();
}
}
}
}
private List<MethodInfo> BulkPatch(List<MethodBase> originals, ref MethodBase lastOriginal)
{
PatchJobs<MethodInfo> patchJobs = new PatchJobs<MethodInfo>();
for (int i = 0; i < originals.Count; i++)
{
lastOriginal = originals[i];
PatchJobs<MethodInfo>.Job job = patchJobs.GetJob(lastOriginal);
foreach (AttributePatch patchMethod in patchMethods)
{
string text = "You cannot combine TargetMethod, TargetMethods or [HarmonyPatchAll] with individual annotations";
HarmonyMethod info = patchMethod.info;
if (info.methodName != null)
{
throw new ArgumentException(text + " [" + info.methodName + "]");
}
if (info.methodType.HasValue && info.methodType.Value != MethodType.Normal)
{
throw new ArgumentException($"{text} [{info.methodType}]");
}
if (info.argumentTypes != null)
{
throw new ArgumentException(text + " [" + info.argumentTypes.Description() + "]");
}
job.AddPatch(patchMethod);
}
}
foreach (PatchJobs<MethodInfo>.Job job2 in patchJobs.GetJobs())
{
lastOriginal = job2.original;
ProcessPatchJob(job2);
}
return patchJobs.GetReplacements();
}
private List<MethodInfo> PatchWithAttributes(ref MethodBase lastOriginal)
{
PatchJobs<MethodInfo> patchJobs = new PatchJobs<MethodInfo>();
foreach (AttributePatch patchMethod in patchMethods)
{
lastOriginal = patchMethod.info.GetOriginalMethod();
if ((object)lastOriginal == null)
{
throw new ArgumentException("Undefined target method for patch method " + patchMethod.info.method.FullDescription());
}
PatchJobs<MethodInfo>.Job job = patchJobs.GetJob(lastOriginal);
job.AddPatch(patchMethod);
}
foreach (PatchJobs<MethodInfo>.Job job2 in patchJobs.GetJobs())
{
lastOriginal = job2.original;
ProcessPatchJob(job2);
}
return patchJobs.GetReplacements();
}
private void ProcessPatchJob(PatchJobs<MethodInfo>.Job job)
{
MethodInfo replacement = null;
bool flag = RunMethod<HarmonyPrepare, bool>(defaultIfNotExisting: true, defaultIfFailing: false, null, new object[1] { job.original });
Exception exception = null;
if (flag)
{
lock (PatchProcessor.locker)
{
try
{
PatchInfo patchInfo = HarmonySharedState.GetPatchInfo(job.original) ?? new PatchInfo();
patchInfo.AddPrefixes(instance.Id, job.prefixes.ToArray());
patchInfo.AddPostfixes(instance.Id, job.postfixes.ToArray());
patchInfo.AddTranspilers(instance.Id, job.transpilers.ToArray());
patchInfo.AddFinalizers(instance.Id, job.finalizers.ToArray());
replacement = PatchFunctions.UpdateWrapper(job.original, patchInfo);
HarmonySharedState.UpdatePatchInfo(job.original, replacement, patchInfo);
}
catch (Exception ex)
{
exception = ex;
}
}
}
RunMethod<HarmonyCleanup>(ref exception, new object[2] { job.original, exception });
ReportException(exception, job.original);
job.replacement = replacement;
}
private List<MethodBase> GetBulkMethods()
{
if (containerType.GetCustomAttributes(inherit: true).Any((object a) => a.GetType().FullName == PatchTools.harmonyPatchAllFullName))
{
Type declaringType = containerAttributes.declaringType;
if ((object)declaringType == null)
{
throw new ArgumentException("Using " + PatchTools.harmonyPatchAllFullName + " requires an additional attribute for specifying the Class/Type");
}
List<MethodBase> list = new List<MethodBase>();
list.AddRange(AccessTools.GetDeclaredConstructors(declaringType).Cast<MethodBase>());
list.AddRange(AccessTools.GetDeclaredMethods(declaringType).Cast<MethodBase>());
List<PropertyInfo> declaredProperties = AccessTools.GetDeclaredProperties(declaringType);
list.AddRange((from prop in declaredProperties
select prop.GetGetMethod(nonPublic: true) into method
where (object)method != null
select method).Cast<MethodBase>());
list.AddRange((from prop in declaredProperties
select prop.GetSetMethod(nonPublic: true) into method
where (object)method != null
select method).Cast<MethodBase>());
return list;
}
List<MethodBase> list2 = new List<MethodBase>();
IEnumerable<MethodBase> enumerable = RunMethod<HarmonyTargetMethods, IEnumerable<MethodBase>>(null, null, null, Array.Empty<object>());
if (enumerable != null)
{
string text = null;
list2 = enumerable.ToList();
if (list2 == null)
{
text = "null";
}
else if (list2.Any((MethodBase m) => (object)m == null))
{
text = "some element was null";
}
if (text != null)
{
if (auxilaryMethods.TryGetValue(typeof(HarmonyTargetMethods), out var value))
{
throw new Exception("Method " + value.FullDescription() + " returned an unexpected result: " + text);
}
throw new Exception("Some method returned an unexpected result: " + text);
}
return list2;
}
MethodBase methodBase = RunMethod<HarmonyTargetMethod, MethodBase>(null, null, (MethodBase method) => ((object)method != null) ? null : "null", Array.Empty<object>());
if ((object)methodBase != null)
{
list2.Add(methodBase);
}
return list2;
}
private void ReportException(Exception exception, MethodBase original)
{
if (exception == null)
{
return;
}
if (containerAttributes.debug == true || Harmony.DEBUG)
{
Harmony.VersionInfo(out var currentVersion);
FileLog.indentLevel = 0;
FileLog.Log($"### Exception from user \"{instance.Id}\", Harmony v{currentVersion}");
FileLog.Log("### Original: " + (original?.FullDescription() ?? "NULL"));
FileLog.Log("### Patch class: " + containerType.FullDescription());
Exception ex = exception;
if (ex is HarmonyException ex2)
{
ex = ex2.InnerException;
}
string text = ex.ToString();
while (text.Contains("\n\n"))
{
text = text.Replace("\n\n", "\n");
}
text = text.Split(new char[1] { '\n' }).Join((string line) => "### " + line, "\n");
FileLog.Log(text.Trim());
}
if (exception is HarmonyException)
{
throw exception;
}
throw new HarmonyException("Patching exception in method " + original.FullDescription(), exception);
}
private T RunMethod<S, T>(T defaultIfNotExisting, T defaultIfFailing, Func<T, string> failOnResult = null, params object[] parameters)
{
if (auxilaryMethods.TryGetValue(typeof(S), out var value))
{
object[] inputs = (parameters ?? Array.Empty<object>()).Union(new object[1] { instance }).ToArray();
object[] parameters2 = AccessTools.ActualParameters(value, inputs);
if (value.ReturnType != typeof(void) && !typeof(T).IsAssignableFrom(value.ReturnType))
{
throw new Exception($"Method {value.FullDescription()} has wrong return type (should be assignable to {typeof(T).FullName})");
}
T val = defaultIfFailing;
try
{
if (value.ReturnType == typeof(void))
{
value.Invoke(null, parameters2);
val = defaultIfNotExisting;
}
else
{
val = (T)value.Invoke(null, parameters2);
}
if (failOnResult != null)
{
string text = failOnResult(val);
if (text != null)
{
throw new Exception("Method " + value.FullDescription() + " returned an unexpected result: " + text);
}
}
}
catch (Exception exception)
{
ReportException(exception, value);
}
return val;
}
return defaultIfNotExisting;
}
private void RunMethod<S>(ref Exception exception, params object[] parameters)
{
if (!auxilaryMethods.TryGetValue(typeof(S), out var value))
{
return;
}
object[] inputs = (parameters ?? Array.Empty<object>()).Union(new object[1] { instance }).ToArray();
object[] parameters2 = AccessTools.ActualParameters(value, inputs);
try
{
object obj = value.Invoke(null, parameters2);
if (value.ReturnType == typeof(Exception))
{
exception = obj as Exception;
}
}
catch (Exception exception2)
{
ReportException(exception2, value);
}
}
}

View file

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reflection;
namespace HarmonyLib;
internal static class PatchFunctions
{
internal static List<MethodInfo> GetSortedPatchMethods(MethodBase original, Patch[] patches, bool debug)
{
return new PatchSorter(patches, debug).Sort(original);
}
internal static MethodInfo UpdateWrapper(MethodBase original, PatchInfo patchInfo)
{
bool debug = patchInfo.Debugging || Harmony.DEBUG;
List<MethodInfo> sortedPatchMethods = GetSortedPatchMethods(original, patchInfo.prefixes, debug);
List<MethodInfo> sortedPatchMethods2 = GetSortedPatchMethods(original, patchInfo.postfixes, debug);
List<MethodInfo> sortedPatchMethods3 = GetSortedPatchMethods(original, patchInfo.transpilers, debug);
List<MethodInfo> sortedPatchMethods4 = GetSortedPatchMethods(original, patchInfo.finalizers, debug);
MethodPatcher methodPatcher = new MethodPatcher(original, null, sortedPatchMethods, sortedPatchMethods2, sortedPatchMethods3, sortedPatchMethods4, debug);
Dictionary<int, CodeInstruction> finalInstructions;
MethodInfo methodInfo = methodPatcher.CreateReplacement(out finalInstructions);
if ((object)methodInfo == null)
{
throw new MissingMethodException("Cannot create replacement for " + original.FullDescription());
}
try
{
PatchTools.DetourMethod(original, methodInfo);
return methodInfo;
}
catch (Exception ex)
{
throw HarmonyException.Create(ex, finalInstructions);
}
}
internal static MethodInfo ReversePatch(HarmonyMethod standin, MethodBase original, MethodInfo postTranspiler)
{
if (standin == null)
{
throw new ArgumentNullException("standin");
}
if ((object)standin.method == null)
{
throw new ArgumentNullException("standin", "standin.method is NULL");
}
bool debug = standin.debug == true || Harmony.DEBUG;
List<MethodInfo> list = new List<MethodInfo>();
if (standin.reversePatchType == HarmonyReversePatchType.Snapshot)
{
Patches patchInfo = Harmony.GetPatchInfo(original);
List<MethodInfo> list2 = list;
ReadOnlyCollection<Patch> transpilers = patchInfo.Transpilers;
int num = 0;
Patch[] array = new Patch[transpilers.Count];
foreach (Patch item in transpilers)
{
array[num] = item;
num++;
}
list2.AddRange(GetSortedPatchMethods(original, array, debug));
}
if ((object)postTranspiler != null)
{
list.Add(postTranspiler);
}
List<MethodInfo> list3 = new List<MethodInfo>();
MethodPatcher methodPatcher = new MethodPatcher(standin.method, original, list3, list3, list, list3, debug);
Dictionary<int, CodeInstruction> finalInstructions;
MethodInfo methodInfo = methodPatcher.CreateReplacement(out finalInstructions);
if ((object)methodInfo == null)
{
throw new MissingMethodException("Cannot create replacement for " + standin.method.FullDescription());
}
try
{
PatchTools.DetourMethod(standin.method, methodInfo);
return methodInfo;
}
catch (Exception ex)
{
throw HarmonyException.Create(ex, finalInstructions);
}
}
}

View file

@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace HarmonyLib;
[Serializable]
public class PatchInfo
{
public Patch[] prefixes = Array.Empty<Patch>();
public Patch[] postfixes = Array.Empty<Patch>();
public Patch[] transpilers = Array.Empty<Patch>();
public Patch[] finalizers = Array.Empty<Patch>();
public bool Debugging
{
get
{
if (!prefixes.Any((Patch p) => p.debug) && !postfixes.Any((Patch p) => p.debug) && !transpilers.Any((Patch p) => p.debug))
{
return finalizers.Any((Patch p) => p.debug);
}
return true;
}
}
internal void AddPrefixes(string owner, params HarmonyMethod[] methods)
{
prefixes = Add(owner, methods, prefixes);
}
[Obsolete("This method only exists for backwards compatibility since the class is public.")]
public void AddPrefix(MethodInfo patch, string owner, int priority, string[] before, string[] after, bool debug)
{
AddPrefixes(owner, new HarmonyMethod(patch, priority, before, after, debug));
}
public void RemovePrefix(string owner)
{
prefixes = Remove(owner, prefixes);
}
internal void AddPostfixes(string owner, params HarmonyMethod[] methods)
{
postfixes = Add(owner, methods, postfixes);
}
[Obsolete("This method only exists for backwards compatibility since the class is public.")]
public void AddPostfix(MethodInfo patch, string owner, int priority, string[] before, string[] after, bool debug)
{
AddPostfixes(owner, new HarmonyMethod(patch, priority, before, after, debug));
}
public void RemovePostfix(string owner)
{
postfixes = Remove(owner, postfixes);
}
internal void AddTranspilers(string owner, params HarmonyMethod[] methods)
{
transpilers = Add(owner, methods, transpilers);
}
[Obsolete("This method only exists for backwards compatibility since the class is public.")]
public void AddTranspiler(MethodInfo patch, string owner, int priority, string[] before, string[] after, bool debug)
{
AddTranspilers(owner, new HarmonyMethod(patch, priority, before, after, debug));
}
public void RemoveTranspiler(string owner)
{
transpilers = Remove(owner, transpilers);
}
internal void AddFinalizers(string owner, params HarmonyMethod[] methods)
{
finalizers = Add(owner, methods, finalizers);
}
[Obsolete("This method only exists for backwards compatibility since the class is public.")]
public void AddFinalizer(MethodInfo patch, string owner, int priority, string[] before, string[] after, bool debug)
{
AddFinalizers(owner, new HarmonyMethod(patch, priority, before, after, debug));
}
public void RemoveFinalizer(string owner)
{
finalizers = Remove(owner, finalizers);
}
public void RemovePatch(MethodInfo patch)
{
prefixes = prefixes.Where((Patch p) => p.PatchMethod != patch).ToArray();
postfixes = postfixes.Where((Patch p) => p.PatchMethod != patch).ToArray();
transpilers = transpilers.Where((Patch p) => p.PatchMethod != patch).ToArray();
finalizers = finalizers.Where((Patch p) => p.PatchMethod != patch).ToArray();
}
private static Patch[] Add(string owner, HarmonyMethod[] add, Patch[] current)
{
if (add.Length == 0)
{
return current;
}
int initialIndex = current.Length;
List<Patch> list = new List<Patch>();
list.AddRange(current);
list.AddRange(add.Where((HarmonyMethod method) => method != null).Select((HarmonyMethod method, int i) => new Patch(method, i + initialIndex, owner)));
return list.ToArray();
}
private static Patch[] Remove(string owner, Patch[] current)
{
if (!(owner == "*"))
{
return current.Where((Patch patch) => patch.owner != owner).ToArray();
}
return Array.Empty<Patch>();
}
}

View file

@ -0,0 +1,61 @@
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace HarmonyLib;
internal static class PatchInfoSerialization
{
private class Binder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
Type[] array = new Type[3]
{
typeof(PatchInfo),
typeof(Patch[]),
typeof(Patch)
};
Type[] array2 = array;
foreach (Type type in array2)
{
if (typeName == type.FullName)
{
return type;
}
}
return Type.GetType($"{typeName}, {assemblyName}");
}
}
internal static readonly BinaryFormatter binaryFormatter = new BinaryFormatter
{
Binder = new Binder()
};
internal static byte[] Serialize(this PatchInfo patchInfo)
{
using MemoryStream memoryStream = new MemoryStream();
binaryFormatter.Serialize(memoryStream, patchInfo);
return memoryStream.GetBuffer();
}
internal static PatchInfo Deserialize(byte[] bytes)
{
using MemoryStream serializationStream = new MemoryStream(bytes);
return (PatchInfo)binaryFormatter.Deserialize(serializationStream);
}
internal static int PriorityComparer(object obj, int index, int priority)
{
Traverse traverse = Traverse.Create(obj);
int value = traverse.Field("priority").GetValue<int>();
int value2 = traverse.Field("index").GetValue<int>();
if (priority != value)
{
return -priority.CompareTo(value);
}
return index.CompareTo(value2);
}
}

View file

@ -0,0 +1,75 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace HarmonyLib;
internal class PatchJobs<T>
{
internal class Job
{
internal MethodBase original;
internal T replacement;
internal List<HarmonyMethod> prefixes = new List<HarmonyMethod>();
internal List<HarmonyMethod> postfixes = new List<HarmonyMethod>();
internal List<HarmonyMethod> transpilers = new List<HarmonyMethod>();
internal List<HarmonyMethod> finalizers = new List<HarmonyMethod>();
internal void AddPatch(AttributePatch patch)
{
HarmonyPatchType? type = patch.type;
if (type.HasValue)
{
switch (type.GetValueOrDefault())
{
case HarmonyPatchType.Prefix:
prefixes.Add(patch.info);
break;
case HarmonyPatchType.Postfix:
postfixes.Add(patch.info);
break;
case HarmonyPatchType.Transpiler:
transpilers.Add(patch.info);
break;
case HarmonyPatchType.Finalizer:
finalizers.Add(patch.info);
break;
}
}
}
}
internal Dictionary<MethodBase, Job> state = new Dictionary<MethodBase, Job>();
internal Job GetJob(MethodBase method)
{
if ((object)method == null)
{
return null;
}
if (!state.TryGetValue(method, out var value))
{
value = new Job
{
original = method
};
state[method] = value;
}
return value;
}
internal List<Job> GetJobs()
{
return state.Values.Where((Job job) => job.prefixes.Count + job.postfixes.Count + job.transpilers.Count + job.finalizers.Count > 0).ToList();
}
internal List<T> GetReplacements()
{
return state.Values.Select((Job job) => job.replacement).ToList();
}
}

View file

@ -0,0 +1,264 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using MonoMod.Utils;
namespace HarmonyLib;
public class PatchProcessor(Harmony instance, MethodBase original)
{
private readonly Harmony instance = instance;
private readonly MethodBase original = original;
private HarmonyMethod prefix;
private HarmonyMethod postfix;
private HarmonyMethod transpiler;
private HarmonyMethod finalizer;
internal static readonly object locker = new object();
public PatchProcessor AddPrefix(HarmonyMethod prefix)
{
this.prefix = prefix;
return this;
}
public PatchProcessor AddPrefix(MethodInfo fixMethod)
{
prefix = new HarmonyMethod(fixMethod);
return this;
}
public PatchProcessor AddPostfix(HarmonyMethod postfix)
{
this.postfix = postfix;
return this;
}
public PatchProcessor AddPostfix(MethodInfo fixMethod)
{
postfix = new HarmonyMethod(fixMethod);
return this;
}
public PatchProcessor AddTranspiler(HarmonyMethod transpiler)
{
this.transpiler = transpiler;
return this;
}
public PatchProcessor AddTranspiler(MethodInfo fixMethod)
{
transpiler = new HarmonyMethod(fixMethod);
return this;
}
public PatchProcessor AddFinalizer(HarmonyMethod finalizer)
{
this.finalizer = finalizer;
return this;
}
public PatchProcessor AddFinalizer(MethodInfo fixMethod)
{
finalizer = new HarmonyMethod(fixMethod);
return this;
}
public static IEnumerable<MethodBase> GetAllPatchedMethods()
{
lock (locker)
{
return HarmonySharedState.GetPatchedMethods();
}
}
public MethodInfo Patch()
{
if ((object)original == null)
{
throw new NullReferenceException("Null method for " + instance.Id);
}
if (!original.IsDeclaredMember())
{
MethodBase declaredMember = original.GetDeclaredMember();
throw new ArgumentException("You can only patch implemented methods/constructors. Patch the declared method " + declaredMember.FullDescription() + " instead.");
}
lock (locker)
{
PatchInfo patchInfo = HarmonySharedState.GetPatchInfo(original) ?? new PatchInfo();
patchInfo.AddPrefixes(instance.Id, prefix);
patchInfo.AddPostfixes(instance.Id, postfix);
patchInfo.AddTranspilers(instance.Id, transpiler);
patchInfo.AddFinalizers(instance.Id, finalizer);
MethodInfo methodInfo = PatchFunctions.UpdateWrapper(original, patchInfo);
HarmonySharedState.UpdatePatchInfo(original, methodInfo, patchInfo);
return methodInfo;
}
}
public PatchProcessor Unpatch(HarmonyPatchType type, string harmonyID)
{
lock (locker)
{
PatchInfo patchInfo = HarmonySharedState.GetPatchInfo(original);
if (patchInfo == null)
{
patchInfo = new PatchInfo();
}
if (type == HarmonyPatchType.All || type == HarmonyPatchType.Prefix)
{
patchInfo.RemovePrefix(harmonyID);
}
if (type == HarmonyPatchType.All || type == HarmonyPatchType.Postfix)
{
patchInfo.RemovePostfix(harmonyID);
}
if (type == HarmonyPatchType.All || type == HarmonyPatchType.Transpiler)
{
patchInfo.RemoveTranspiler(harmonyID);
}
if (type == HarmonyPatchType.All || type == HarmonyPatchType.Finalizer)
{
patchInfo.RemoveFinalizer(harmonyID);
}
MethodInfo replacement = PatchFunctions.UpdateWrapper(original, patchInfo);
HarmonySharedState.UpdatePatchInfo(original, replacement, patchInfo);
return this;
}
}
public PatchProcessor Unpatch(MethodInfo patch)
{
lock (locker)
{
PatchInfo patchInfo = HarmonySharedState.GetPatchInfo(original);
if (patchInfo == null)
{
patchInfo = new PatchInfo();
}
patchInfo.RemovePatch(patch);
MethodInfo replacement = PatchFunctions.UpdateWrapper(original, patchInfo);
HarmonySharedState.UpdatePatchInfo(original, replacement, patchInfo);
return this;
}
}
public static Patches GetPatchInfo(MethodBase method)
{
PatchInfo patchInfo;
lock (locker)
{
patchInfo = HarmonySharedState.GetPatchInfo(method);
}
if (patchInfo == null)
{
return null;
}
return new Patches(patchInfo.prefixes, patchInfo.postfixes, patchInfo.transpilers, patchInfo.finalizers);
}
public static List<MethodInfo> GetSortedPatchMethods(MethodBase original, Patch[] patches)
{
return PatchFunctions.GetSortedPatchMethods(original, patches, debug: false);
}
public static Dictionary<string, Version> VersionInfo(out Version currentVersion)
{
currentVersion = typeof(Harmony).Assembly.GetName().Version;
Dictionary<string, Assembly> assemblies = new Dictionary<string, Assembly>();
GetAllPatchedMethods().Do(delegate(MethodBase method)
{
PatchInfo patchInfo;
lock (locker)
{
patchInfo = HarmonySharedState.GetPatchInfo(method);
}
patchInfo.prefixes.Do(delegate(Patch fix)
{
assemblies[fix.owner] = fix.PatchMethod.DeclaringType.Assembly;
});
patchInfo.postfixes.Do(delegate(Patch fix)
{
assemblies[fix.owner] = fix.PatchMethod.DeclaringType.Assembly;
});
patchInfo.transpilers.Do(delegate(Patch fix)
{
assemblies[fix.owner] = fix.PatchMethod.DeclaringType.Assembly;
});
patchInfo.finalizers.Do(delegate(Patch fix)
{
assemblies[fix.owner] = fix.PatchMethod.DeclaringType.Assembly;
});
});
Dictionary<string, Version> result = new Dictionary<string, Version>();
assemblies.Do(delegate(KeyValuePair<string, Assembly> info)
{
AssemblyName assemblyName = info.Value.GetReferencedAssemblies().FirstOrDefault((AssemblyName a) => a.FullName.StartsWith("0Harmony, Version", StringComparison.Ordinal));
if (assemblyName != null)
{
result[info.Key] = assemblyName.Version;
}
});
return result;
}
public static ILGenerator CreateILGenerator()
{
DynamicMethodDefinition dynamicMethodDefinition = new DynamicMethodDefinition($"ILGenerator_{Guid.NewGuid()}", typeof(void), Array.Empty<Type>());
return dynamicMethodDefinition.GetILGenerator();
}
public static ILGenerator CreateILGenerator(MethodBase original)
{
Type returnType = ((original is MethodInfo methodInfo) ? methodInfo.ReturnType : typeof(void));
List<Type> list = (from pi in original.GetParameters()
select pi.ParameterType).ToList();
if (!original.IsStatic)
{
list.Insert(0, original.DeclaringType);
}
DynamicMethodDefinition dynamicMethodDefinition = new DynamicMethodDefinition("ILGenerator_" + original.Name, returnType, list.ToArray());
return dynamicMethodDefinition.GetILGenerator();
}
public static List<CodeInstruction> GetOriginalInstructions(MethodBase original, ILGenerator generator = null)
{
return MethodCopier.GetInstructions(generator ?? CreateILGenerator(original), original, 0);
}
public static List<CodeInstruction> GetOriginalInstructions(MethodBase original, out ILGenerator generator)
{
generator = CreateILGenerator(original);
return MethodCopier.GetInstructions(generator, original, 0);
}
public static List<CodeInstruction> GetCurrentInstructions(MethodBase original, int maxTranspilers = int.MaxValue, ILGenerator generator = null)
{
return MethodCopier.GetInstructions(generator ?? CreateILGenerator(original), original, maxTranspilers);
}
public static List<CodeInstruction> GetCurrentInstructions(MethodBase original, out ILGenerator generator, int maxTranspilers = int.MaxValue)
{
generator = CreateILGenerator(original);
return MethodCopier.GetInstructions(generator, original, maxTranspilers);
}
public static IEnumerable<KeyValuePair<OpCode, object>> ReadMethodBody(MethodBase method)
{
return from instr in MethodBodyReader.GetInstructions(CreateILGenerator(method), method)
select new KeyValuePair<OpCode, object>(instr.opcode, instr.operand);
}
public static IEnumerable<KeyValuePair<OpCode, object>> ReadMethodBody(MethodBase method, ILGenerator generator)
{
return from instr in MethodBodyReader.GetInstructions(generator, method)
select new KeyValuePair<OpCode, object>(instr.opcode, instr.operand);
}
}

View file

@ -0,0 +1,214 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace HarmonyLib;
internal class PatchSorter
{
private class PatchSortingWrapper : IComparable
{
internal readonly HashSet<PatchSortingWrapper> after;
internal readonly HashSet<PatchSortingWrapper> before;
internal readonly Patch innerPatch;
internal PatchSortingWrapper(Patch patch)
{
innerPatch = patch;
before = new HashSet<PatchSortingWrapper>();
after = new HashSet<PatchSortingWrapper>();
}
public int CompareTo(object obj)
{
return PatchInfoSerialization.PriorityComparer((obj is PatchSortingWrapper patchSortingWrapper) ? patchSortingWrapper.innerPatch : null, innerPatch.index, innerPatch.priority);
}
public override bool Equals(object obj)
{
if (obj is PatchSortingWrapper patchSortingWrapper)
{
return innerPatch.PatchMethod == patchSortingWrapper.innerPatch.PatchMethod;
}
return false;
}
public override int GetHashCode()
{
return innerPatch.PatchMethod.GetHashCode();
}
internal void AddBeforeDependency(IEnumerable<PatchSortingWrapper> dependencies)
{
foreach (PatchSortingWrapper dependency in dependencies)
{
before.Add(dependency);
dependency.after.Add(this);
}
}
internal void AddAfterDependency(IEnumerable<PatchSortingWrapper> dependencies)
{
foreach (PatchSortingWrapper dependency in dependencies)
{
after.Add(dependency);
dependency.before.Add(this);
}
}
internal void RemoveAfterDependency(PatchSortingWrapper afterNode)
{
after.Remove(afterNode);
afterNode.before.Remove(this);
}
internal void RemoveBeforeDependency(PatchSortingWrapper beforeNode)
{
before.Remove(beforeNode);
beforeNode.after.Remove(this);
}
}
internal class PatchDetailedComparer : IEqualityComparer<Patch>
{
public bool Equals(Patch x, Patch y)
{
if (y != null && x != null && x.owner == y.owner && x.PatchMethod == y.PatchMethod && x.index == y.index && x.priority == y.priority && x.before.Length == y.before.Length && x.after.Length == y.after.Length && x.before.All(((IEnumerable<string>)y.before).Contains<string>))
{
return x.after.All(((IEnumerable<string>)y.after).Contains<string>);
}
return false;
}
public int GetHashCode(Patch obj)
{
return obj.GetHashCode();
}
}
private List<PatchSortingWrapper> patches;
private HashSet<PatchSortingWrapper> handledPatches;
private List<PatchSortingWrapper> result;
private List<PatchSortingWrapper> waitingList;
internal Patch[] sortedPatchArray;
private readonly bool debug;
internal PatchSorter(Patch[] patches, bool debug)
{
this.patches = patches.Select((Patch x) => new PatchSortingWrapper(x)).ToList();
this.debug = debug;
foreach (PatchSortingWrapper node in this.patches)
{
node.AddBeforeDependency(this.patches.Where((PatchSortingWrapper x) => node.innerPatch.before.Contains(x.innerPatch.owner)));
node.AddAfterDependency(this.patches.Where((PatchSortingWrapper x) => node.innerPatch.after.Contains(x.innerPatch.owner)));
}
this.patches.Sort();
}
internal List<MethodInfo> Sort(MethodBase original)
{
if (sortedPatchArray != null)
{
return sortedPatchArray.Select((Patch x) => x.GetMethod(original)).ToList();
}
handledPatches = new HashSet<PatchSortingWrapper>();
waitingList = new List<PatchSortingWrapper>();
result = new List<PatchSortingWrapper>(patches.Count);
Queue<PatchSortingWrapper> queue = new Queue<PatchSortingWrapper>(patches);
while (queue.Count != 0)
{
foreach (PatchSortingWrapper item in queue)
{
if (item.after.All((PatchSortingWrapper x) => handledPatches.Contains(x)))
{
AddNodeToResult(item);
if (item.before.Count != 0)
{
ProcessWaitingList();
}
}
else
{
waitingList.Add(item);
}
}
CullDependency();
queue = new Queue<PatchSortingWrapper>(waitingList);
waitingList.Clear();
}
sortedPatchArray = result.Select((PatchSortingWrapper x) => x.innerPatch).ToArray();
handledPatches = null;
waitingList = null;
patches = null;
return sortedPatchArray.Select((Patch x) => x.GetMethod(original)).ToList();
}
internal bool ComparePatchLists(Patch[] patches)
{
if (sortedPatchArray == null)
{
Sort(null);
}
if (patches != null && sortedPatchArray.Length == patches.Length)
{
return sortedPatchArray.All((Patch x) => patches.Contains(x, new PatchDetailedComparer()));
}
return false;
}
private void CullDependency()
{
for (int num = waitingList.Count - 1; num >= 0; num--)
{
foreach (PatchSortingWrapper item in waitingList[num].after)
{
if (!handledPatches.Contains(item))
{
waitingList[num].RemoveAfterDependency(item);
if (debug)
{
string text = item.innerPatch.PatchMethod.FullDescription();
string text2 = waitingList[num].innerPatch.PatchMethod.FullDescription();
FileLog.LogBuffered("Breaking dependance between " + text + " and " + text2);
}
return;
}
}
}
}
private void ProcessWaitingList()
{
int num = waitingList.Count;
int num2 = 0;
while (num2 < num)
{
PatchSortingWrapper patchSortingWrapper = waitingList[num2];
if (patchSortingWrapper.after.All(handledPatches.Contains))
{
waitingList.Remove(patchSortingWrapper);
AddNodeToResult(patchSortingWrapper);
num--;
num2 = 0;
}
else
{
num2++;
}
}
}
private void AddNodeToResult(PatchSortingWrapper node)
{
result.Add(node);
handledPatches.Add(node);
}
}

View file

@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using MonoMod.Core;
using MonoMod.Utils;
namespace HarmonyLib;
internal static class PatchTools
{
private static readonly Dictionary<MethodBase, ICoreDetour> detours = new Dictionary<MethodBase, ICoreDetour>();
internal static readonly string harmonyMethodFullName = typeof(HarmonyMethod).FullName;
internal static readonly string harmonyAttributeFullName = typeof(HarmonyAttribute).FullName;
internal static readonly string harmonyPatchAllFullName = typeof(HarmonyPatchAll).FullName;
internal static readonly MethodInfo m_GetExecutingAssemblyReplacementTranspiler = SymbolExtensions.GetMethodInfo(() => GetExecutingAssemblyTranspiler(null));
internal static readonly MethodInfo m_GetExecutingAssembly = SymbolExtensions.GetMethodInfo(() => Assembly.GetExecutingAssembly());
internal static readonly MethodInfo m_GetExecutingAssemblyReplacement = SymbolExtensions.GetMethodInfo(() => GetExecutingAssemblyReplacement());
internal static void DetourMethod(MethodBase method, MethodBase replacement)
{
lock (detours)
{
if (detours.TryGetValue(method, out var value))
{
value.Dispose();
}
detours[method] = DetourFactory.Current.CreateDetour(method, replacement);
}
}
private static Assembly GetExecutingAssemblyReplacement()
{
StackFrame stackFrame = new StackTrace().GetFrames()?.Skip(1).FirstOrDefault();
if (stackFrame != null)
{
MethodBase originalMethodFromStackframe = Harmony.GetOriginalMethodFromStackframe(stackFrame);
if ((object)originalMethodFromStackframe != null)
{
return originalMethodFromStackframe.Module.Assembly;
}
}
return Assembly.GetExecutingAssembly();
}
internal static IEnumerable<CodeInstruction> GetExecutingAssemblyTranspiler(IEnumerable<CodeInstruction> instructions)
{
return instructions.MethodReplacer(m_GetExecutingAssembly, m_GetExecutingAssemblyReplacement);
}
public static MethodInfo CreateMethod(string name, Type returnType, List<KeyValuePair<string, Type>> parameters, Action<ILGenerator> generator)
{
Type[] parameterTypes = parameters.Select((KeyValuePair<string, Type> p) => p.Value).ToArray();
DynamicMethodDefinition dynamicMethodDefinition = new DynamicMethodDefinition(name, returnType, parameterTypes);
for (int num = 0; num < parameters.Count; num++)
{
dynamicMethodDefinition.Definition.Parameters[num].Name = parameters[num].Key;
}
ILGenerator iLGenerator = dynamicMethodDefinition.GetILGenerator();
generator(iLGenerator);
return dynamicMethodDefinition.Generate();
}
internal static MethodInfo GetPatchMethod(Type patchType, string attributeName)
{
MethodInfo methodInfo = patchType.GetMethods(AccessTools.all).FirstOrDefault((MethodInfo m) => m.GetCustomAttributes(inherit: true).Any((object a) => a.GetType().FullName == attributeName));
if ((object)methodInfo == null)
{
string name = attributeName.Replace("HarmonyLib.Harmony", "");
methodInfo = patchType.GetMethod(name, AccessTools.all);
}
return methodInfo;
}
internal static AssemblyBuilder DefineDynamicAssembly(string name)
{
AssemblyName name2 = new AssemblyName(name);
return AppDomain.CurrentDomain.DefineDynamicAssembly(name2, AssemblyBuilderAccess.Run);
}
internal static List<AttributePatch> GetPatchMethods(Type type)
{
return (from method in AccessTools.GetDeclaredMethods(type)
select AttributePatch.Create(method) into attributePatch
where attributePatch != null
select attributePatch).ToList();
}
internal static MethodBase GetOriginalMethod(this HarmonyMethod attr)
{
try
{
MethodType? methodType = attr.methodType;
if (methodType.HasValue)
{
switch (methodType.GetValueOrDefault())
{
case MethodType.Normal:
if (attr.methodName == null)
{
return null;
}
return AccessTools.DeclaredMethod(attr.declaringType, attr.methodName, attr.argumentTypes);
case MethodType.Getter:
if (attr.methodName == null)
{
return AccessTools.DeclaredIndexer(attr.declaringType, attr.argumentTypes).GetGetMethod(nonPublic: true);
}
return AccessTools.DeclaredProperty(attr.declaringType, attr.methodName).GetGetMethod(nonPublic: true);
case MethodType.Setter:
if (attr.methodName == null)
{
return AccessTools.DeclaredIndexer(attr.declaringType, attr.argumentTypes).GetSetMethod(nonPublic: true);
}
return AccessTools.DeclaredProperty(attr.declaringType, attr.methodName).GetSetMethod(nonPublic: true);
case MethodType.Constructor:
return AccessTools.DeclaredConstructor(attr.declaringType, attr.argumentTypes);
case MethodType.StaticConstructor:
return (from c in AccessTools.GetDeclaredConstructors(attr.declaringType)
where c.IsStatic
select c).FirstOrDefault();
case MethodType.Enumerator:
{
if (attr.methodName == null)
{
return null;
}
MethodInfo method2 = AccessTools.DeclaredMethod(attr.declaringType, attr.methodName, attr.argumentTypes);
return AccessTools.EnumeratorMoveNext(method2);
}
case MethodType.Async:
{
if (attr.methodName == null)
{
return null;
}
MethodInfo method = AccessTools.DeclaredMethod(attr.declaringType, attr.methodName, attr.argumentTypes);
return AccessTools.AsyncMoveNext(method);
}
}
}
}
catch (AmbiguousMatchException ex)
{
throw new HarmonyException("Ambiguous match for HarmonyMethod[" + attr.Description() + "]", ex.InnerException ?? ex);
}
return null;
}
}

View file

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace HarmonyLib;
public class Patches
{
public readonly ReadOnlyCollection<Patch> Prefixes;
public readonly ReadOnlyCollection<Patch> Postfixes;
public readonly ReadOnlyCollection<Patch> Transpilers;
public readonly ReadOnlyCollection<Patch> Finalizers;
public ReadOnlyCollection<string> Owners
{
get
{
HashSet<string> hashSet = new HashSet<string>();
hashSet.UnionWith(Prefixes.Select((Patch p) => p.owner));
hashSet.UnionWith(Postfixes.Select((Patch p) => p.owner));
hashSet.UnionWith(Transpilers.Select((Patch p) => p.owner));
hashSet.UnionWith(Finalizers.Select((Patch p) => p.owner));
return hashSet.ToList().AsReadOnly();
}
}
public Patches(Patch[] prefixes, Patch[] postfixes, Patch[] transpilers, Patch[] finalizers)
{
if (prefixes == null)
{
prefixes = Array.Empty<Patch>();
}
if (postfixes == null)
{
postfixes = Array.Empty<Patch>();
}
if (transpilers == null)
{
transpilers = Array.Empty<Patch>();
}
if (finalizers == null)
{
finalizers = Array.Empty<Patch>();
}
Prefixes = prefixes.ToList().AsReadOnly();
Postfixes = postfixes.ToList().AsReadOnly();
Transpilers = transpilers.ToList().AsReadOnly();
Finalizers = finalizers.ToList().AsReadOnly();
}
}

View file

@ -0,0 +1,22 @@
namespace HarmonyLib;
public static class Priority
{
public const int Last = 0;
public const int VeryLow = 100;
public const int Low = 200;
public const int LowerThanNormal = 300;
public const int Normal = 400;
public const int HigherThanNormal = 500;
public const int High = 600;
public const int VeryHigh = 700;
public const int First = 800;
}

View file

@ -0,0 +1,3 @@
namespace HarmonyLib;
public delegate ref T RefResult<T>();

View file

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace HarmonyLib;
public class ReversePatcher(Harmony instance, MethodBase original, HarmonyMethod standin)
{
private readonly Harmony instance = instance;
private readonly MethodBase original = original;
private readonly HarmonyMethod standin = standin;
public MethodInfo Patch(HarmonyReversePatchType type = HarmonyReversePatchType.Original)
{
if ((object)original == null)
{
throw new NullReferenceException("Null method for " + instance.Id);
}
standin.reversePatchType = type;
MethodInfo transpiler = GetTranspiler(standin.method);
return PatchFunctions.ReversePatch(standin, original, transpiler);
}
internal static MethodInfo GetTranspiler(MethodInfo method)
{
string methodName = method.Name;
Type declaringType = method.DeclaringType;
List<MethodInfo> declaredMethods = AccessTools.GetDeclaredMethods(declaringType);
Type ici = typeof(IEnumerable<CodeInstruction>);
return declaredMethods.FirstOrDefault((MethodInfo m) => !(m.ReturnType != ici) && m.Name.StartsWith("<" + methodName + ">"));
}
}

View file

@ -0,0 +1,6 @@
using System;
namespace HarmonyLib;
[Obsolete("Use AccessTools.FieldRefAccess<T, S> for fields and AccessTools.MethodDelegate<Action<T, S>> for property setters")]
public delegate void SetterHandler<in T, in S>(T source, S value);

View file

@ -0,0 +1,40 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace HarmonyLib;
public static class SymbolExtensions
{
public static MethodInfo GetMethodInfo(Expression<Action> expression)
{
return GetMethodInfo((LambdaExpression)expression);
}
public static MethodInfo GetMethodInfo<T>(Expression<Action<T>> expression)
{
return GetMethodInfo((LambdaExpression)expression);
}
public static MethodInfo GetMethodInfo<T, TResult>(Expression<Func<T, TResult>> expression)
{
return GetMethodInfo((LambdaExpression)expression);
}
public static MethodInfo GetMethodInfo(LambdaExpression expression)
{
if (!(expression.Body is MethodCallExpression { Method: var method }))
{
if (expression.Body is UnaryExpression { Operand: MethodCallExpression { Object: ConstantExpression { Value: MethodInfo value } } })
{
return value;
}
throw new ArgumentException("Invalid Expression. Expression should consist of a Method call only.");
}
if ((object)method == null)
{
throw new Exception($"Cannot find method for expression {expression}");
}
return method;
}
}

View file

@ -0,0 +1,154 @@
using System;
using System.Reflection;
using System.Reflection.Emit;
using MonoMod.Utils;
namespace HarmonyLib;
internal class Tools
{
internal struct TypeAndName
{
internal Type type;
internal string name;
}
internal static readonly bool isWindows = Environment.OSVersion.Platform.Equals(PlatformID.Win32NT);
internal static TypeAndName TypColonName(string typeColonName)
{
if (typeColonName == null)
{
throw new ArgumentNullException("typeColonName");
}
string[] array = typeColonName.Split(new char[1] { ':' });
if (array.Length != 2)
{
throw new ArgumentException(" must be specified as 'Namespace.Type1.Type2:MemberName", "typeColonName");
}
return new TypeAndName
{
type = AccessTools.TypeByName(array[0]),
name = array[1]
};
}
internal static void ValidateFieldType<F>(FieldInfo fieldInfo)
{
Type typeFromHandle = typeof(F);
Type fieldType = fieldInfo.FieldType;
if (typeFromHandle == fieldType)
{
return;
}
if (fieldType.IsEnum)
{
Type underlyingType = Enum.GetUnderlyingType(fieldType);
if (typeFromHandle != underlyingType)
{
throw new ArgumentException("FieldRefAccess return type must be the same as FieldType or " + $"FieldType's underlying integral type ({underlyingType}) for enum types");
}
}
else
{
if (fieldType.IsValueType)
{
throw new ArgumentException("FieldRefAccess return type must be the same as FieldType for value types");
}
if (!typeFromHandle.IsAssignableFrom(fieldType))
{
throw new ArgumentException("FieldRefAccess return type must be assignable from FieldType for reference types");
}
}
}
internal static AccessTools.FieldRef<T, F> FieldRefAccess<T, F>(FieldInfo fieldInfo, bool needCastclass)
{
ValidateFieldType<F>(fieldInfo);
Type typeFromHandle = typeof(T);
Type declaringType = fieldInfo.DeclaringType;
DynamicMethodDefinition dynamicMethodDefinition = new DynamicMethodDefinition("__refget_" + typeFromHandle.Name + "_fi_" + fieldInfo.Name, typeof(F).MakeByRefType(), new Type[1] { typeFromHandle });
ILGenerator iLGenerator = dynamicMethodDefinition.GetILGenerator();
if (fieldInfo.IsStatic)
{
iLGenerator.Emit(OpCodes.Ldsflda, fieldInfo);
}
else
{
iLGenerator.Emit(OpCodes.Ldarg_0);
if (needCastclass)
{
iLGenerator.Emit(OpCodes.Castclass, declaringType);
}
iLGenerator.Emit(OpCodes.Ldflda, fieldInfo);
}
iLGenerator.Emit(OpCodes.Ret);
return (AccessTools.FieldRef<T, F>)dynamicMethodDefinition.Generate().CreateDelegate(typeof(AccessTools.FieldRef<T, F>));
}
internal static AccessTools.StructFieldRef<T, F> StructFieldRefAccess<T, F>(FieldInfo fieldInfo) where T : struct
{
ValidateFieldType<F>(fieldInfo);
DynamicMethodDefinition dynamicMethodDefinition = new DynamicMethodDefinition("__refget_" + typeof(T).Name + "_struct_fi_" + fieldInfo.Name, typeof(F).MakeByRefType(), new Type[1] { typeof(T).MakeByRefType() });
ILGenerator iLGenerator = dynamicMethodDefinition.GetILGenerator();
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Ldflda, fieldInfo);
iLGenerator.Emit(OpCodes.Ret);
return (AccessTools.StructFieldRef<T, F>)dynamicMethodDefinition.Generate().CreateDelegate(typeof(AccessTools.StructFieldRef<T, F>));
}
internal static AccessTools.FieldRef<F> StaticFieldRefAccess<F>(FieldInfo fieldInfo)
{
if (!fieldInfo.IsStatic)
{
throw new ArgumentException("Field must be static");
}
ValidateFieldType<F>(fieldInfo);
DynamicMethodDefinition dynamicMethodDefinition = new DynamicMethodDefinition("__refget_" + (fieldInfo.DeclaringType?.Name ?? "null") + "_static_fi_" + fieldInfo.Name, typeof(F).MakeByRefType(), Array.Empty<Type>());
ILGenerator iLGenerator = dynamicMethodDefinition.GetILGenerator();
iLGenerator.Emit(OpCodes.Ldsflda, fieldInfo);
iLGenerator.Emit(OpCodes.Ret);
return (AccessTools.FieldRef<F>)dynamicMethodDefinition.Generate().CreateDelegate(typeof(AccessTools.FieldRef<F>));
}
internal static FieldInfo GetInstanceField(Type type, string fieldName)
{
FieldInfo fieldInfo = AccessTools.Field(type, fieldName);
if ((object)fieldInfo == null)
{
throw new MissingFieldException(type.Name, fieldName);
}
if (fieldInfo.IsStatic)
{
throw new ArgumentException("Field must not be static");
}
return fieldInfo;
}
internal static bool FieldRefNeedsClasscast(Type delegateInstanceType, Type declaringType)
{
bool flag = false;
if (delegateInstanceType != declaringType)
{
flag = delegateInstanceType.IsAssignableFrom(declaringType);
if (!flag && !declaringType.IsAssignableFrom(delegateInstanceType))
{
throw new ArgumentException("FieldDeclaringType must be assignable from or to T (FieldRefAccess instance type) - \"instanceOfT is FieldDeclaringType\" must be possible");
}
}
return flag;
}
internal static void ValidateStructField<T, F>(FieldInfo fieldInfo) where T : struct
{
if (fieldInfo.IsStatic)
{
throw new ArgumentException("Field must not be static");
}
if (fieldInfo.DeclaringType != typeof(T))
{
throw new ArgumentException("FieldDeclaringType must be T (StructFieldRefAccess instance type)");
}
}
}

View file

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
namespace HarmonyLib;
public static class Transpilers
{
public static IEnumerable<CodeInstruction> MethodReplacer(this IEnumerable<CodeInstruction> instructions, MethodBase from, MethodBase to)
{
if ((object)from == null)
{
throw new ArgumentException("Unexpected null argument", "from");
}
if ((object)to == null)
{
throw new ArgumentException("Unexpected null argument", "to");
}
foreach (CodeInstruction instruction in instructions)
{
MethodBase methodBase = instruction.operand as MethodBase;
if (methodBase == from)
{
instruction.opcode = (to.IsConstructor ? OpCodes.Newobj : OpCodes.Call);
instruction.operand = to;
}
yield return instruction;
}
}
public static IEnumerable<CodeInstruction> Manipulator(this IEnumerable<CodeInstruction> instructions, Func<CodeInstruction, bool> predicate, Action<CodeInstruction> action)
{
if (predicate == null)
{
throw new ArgumentNullException("predicate");
}
if (action == null)
{
throw new ArgumentNullException("action");
}
return instructions.Select(delegate(CodeInstruction instruction)
{
if (predicate(instruction))
{
action(instruction);
}
return instruction;
}).AsEnumerable();
}
public static IEnumerable<CodeInstruction> DebugLogger(this IEnumerable<CodeInstruction> instructions, string text)
{
yield return new CodeInstruction(OpCodes.Ldstr, text);
yield return new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(FileLog), "Debug"));
foreach (CodeInstruction instruction in instructions)
{
yield return instruction;
}
}
}

View file

@ -0,0 +1,434 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace HarmonyLib;
public class Traverse<T>
{
private readonly Traverse traverse;
public T Value
{
get
{
return traverse.GetValue<T>();
}
set
{
traverse.SetValue(value);
}
}
private Traverse()
{
}
public Traverse(Traverse traverse)
{
this.traverse = traverse;
}
}
public class Traverse
{
private static readonly AccessCache Cache;
private readonly Type _type;
private readonly object _root;
private readonly MemberInfo _info;
private readonly MethodBase _method;
private readonly object[] _params;
public static Action<Traverse, Traverse> CopyFields;
[MethodImpl(MethodImplOptions.Synchronized)]
static Traverse()
{
CopyFields = delegate(Traverse from, Traverse to)
{
to.SetValue(from.GetValue());
};
if (Cache == null)
{
Cache = new AccessCache();
}
}
public static Traverse Create(Type type)
{
return new Traverse(type);
}
public static Traverse Create<T>()
{
return Create(typeof(T));
}
public static Traverse Create(object root)
{
return new Traverse(root);
}
public static Traverse CreateWithType(string name)
{
return new Traverse(AccessTools.TypeByName(name));
}
private Traverse()
{
}
public Traverse(Type type)
{
_type = type;
}
public Traverse(object root)
{
_root = root;
_type = root?.GetType();
}
private Traverse(object root, MemberInfo info, object[] index)
{
_root = root;
_type = root?.GetType() ?? info.GetUnderlyingType();
_info = info;
_params = index;
}
private Traverse(object root, MethodInfo method, object[] parameter)
{
_root = root;
_type = method.ReturnType;
_method = method;
_params = parameter;
}
public object GetValue()
{
if (_info is FieldInfo)
{
return ((FieldInfo)_info).GetValue(_root);
}
if (_info is PropertyInfo)
{
return ((PropertyInfo)_info).GetValue(_root, AccessTools.all, null, _params, CultureInfo.CurrentCulture);
}
if ((object)_method != null)
{
return _method.Invoke(_root, _params);
}
if (_root == null && (object)_type != null)
{
return _type;
}
return _root;
}
public T GetValue<T>()
{
object value = GetValue();
if (value == null)
{
return default(T);
}
return (T)value;
}
public object GetValue(params object[] arguments)
{
if ((object)_method == null)
{
throw new Exception("cannot get method value without method");
}
return _method.Invoke(_root, arguments);
}
public T GetValue<T>(params object[] arguments)
{
if ((object)_method == null)
{
throw new Exception("cannot get method value without method");
}
return (T)_method.Invoke(_root, arguments);
}
public Traverse SetValue(object value)
{
if (_info is FieldInfo)
{
((FieldInfo)_info).SetValue(_root, value, AccessTools.all, null, CultureInfo.CurrentCulture);
}
if (_info is PropertyInfo)
{
((PropertyInfo)_info).SetValue(_root, value, AccessTools.all, null, _params, CultureInfo.CurrentCulture);
}
if ((object)_method != null)
{
throw new Exception("cannot set value of method " + _method.FullDescription());
}
return this;
}
public Type GetValueType()
{
if (_info is FieldInfo)
{
return ((FieldInfo)_info).FieldType;
}
if (_info is PropertyInfo)
{
return ((PropertyInfo)_info).PropertyType;
}
return null;
}
private Traverse Resolve()
{
if (_root == null)
{
if (_info is FieldInfo { IsStatic: not false })
{
return new Traverse(GetValue());
}
if (_info is PropertyInfo propertyInfo && propertyInfo.GetGetMethod().IsStatic)
{
return new Traverse(GetValue());
}
if ((object)_method != null && _method.IsStatic)
{
return new Traverse(GetValue());
}
if ((object)_type != null)
{
return this;
}
}
return new Traverse(GetValue());
}
public Traverse Type(string name)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
if ((object)_type == null)
{
return new Traverse();
}
Type type = AccessTools.Inner(_type, name);
if ((object)type == null)
{
return new Traverse();
}
return new Traverse(type);
}
public Traverse Field(string name)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
Traverse traverse = Resolve();
if ((object)traverse._type == null)
{
return new Traverse();
}
FieldInfo fieldInfo = Cache.GetFieldInfo(traverse._type, name);
if ((object)fieldInfo == null)
{
return new Traverse();
}
if (!fieldInfo.IsStatic && traverse._root == null)
{
return new Traverse();
}
return new Traverse(traverse._root, fieldInfo, null);
}
public Traverse<T> Field<T>(string name)
{
return new Traverse<T>(Field(name));
}
public List<string> Fields()
{
Traverse traverse = Resolve();
return AccessTools.GetFieldNames(traverse._type);
}
public Traverse Property(string name, object[] index = null)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
Traverse traverse = Resolve();
if ((object)traverse._type == null)
{
return new Traverse();
}
PropertyInfo propertyInfo = Cache.GetPropertyInfo(traverse._type, name);
if ((object)propertyInfo == null)
{
return new Traverse();
}
return new Traverse(traverse._root, propertyInfo, index);
}
public Traverse<T> Property<T>(string name, object[] index = null)
{
return new Traverse<T>(Property(name, index));
}
public List<string> Properties()
{
Traverse traverse = Resolve();
return AccessTools.GetPropertyNames(traverse._type);
}
public Traverse Method(string name, params object[] arguments)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
Traverse traverse = Resolve();
if ((object)traverse._type == null)
{
return new Traverse();
}
Type[] types = AccessTools.GetTypes(arguments);
MethodBase methodInfo = Cache.GetMethodInfo(traverse._type, name, types);
if ((object)methodInfo == null)
{
return new Traverse();
}
return new Traverse(traverse._root, (MethodInfo)methodInfo, arguments);
}
public Traverse Method(string name, Type[] paramTypes, object[] arguments = null)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
Traverse traverse = Resolve();
if ((object)traverse._type == null)
{
return new Traverse();
}
MethodBase methodInfo = Cache.GetMethodInfo(traverse._type, name, paramTypes);
if ((object)methodInfo == null)
{
return new Traverse();
}
return new Traverse(traverse._root, (MethodInfo)methodInfo, arguments);
}
public List<string> Methods()
{
Traverse traverse = Resolve();
return AccessTools.GetMethodNames(traverse._type);
}
public bool FieldExists()
{
if ((object)_info != null)
{
return _info is FieldInfo;
}
return false;
}
public bool PropertyExists()
{
if ((object)_info != null)
{
return _info is PropertyInfo;
}
return false;
}
public bool MethodExists()
{
return (object)_method != null;
}
public bool TypeExists()
{
return (object)_type != null;
}
public static void IterateFields(object source, Action<Traverse> action)
{
Traverse sourceTrv = Create(source);
AccessTools.GetFieldNames(source).ForEach(delegate(string f)
{
action(sourceTrv.Field(f));
});
}
public static void IterateFields(object source, object target, Action<Traverse, Traverse> action)
{
Traverse sourceTrv = Create(source);
Traverse targetTrv = Create(target);
AccessTools.GetFieldNames(source).ForEach(delegate(string f)
{
action(sourceTrv.Field(f), targetTrv.Field(f));
});
}
public static void IterateFields(object source, object target, Action<string, Traverse, Traverse> action)
{
Traverse sourceTrv = Create(source);
Traverse targetTrv = Create(target);
AccessTools.GetFieldNames(source).ForEach(delegate(string f)
{
action(f, sourceTrv.Field(f), targetTrv.Field(f));
});
}
public static void IterateProperties(object source, Action<Traverse> action)
{
Traverse sourceTrv = Create(source);
AccessTools.GetPropertyNames(source).ForEach(delegate(string f)
{
action(sourceTrv.Property(f));
});
}
public static void IterateProperties(object source, object target, Action<Traverse, Traverse> action)
{
Traverse sourceTrv = Create(source);
Traverse targetTrv = Create(target);
AccessTools.GetPropertyNames(source).ForEach(delegate(string f)
{
action(sourceTrv.Property(f), targetTrv.Property(f));
});
}
public static void IterateProperties(object source, object target, Action<string, Traverse, Traverse> action)
{
Traverse sourceTrv = Create(source);
Traverse targetTrv = Create(target);
AccessTools.GetPropertyNames(source).ForEach(delegate(string f)
{
action(f, sourceTrv.Property(f), targetTrv.Property(f));
});
}
public override string ToString()
{
return (_method ?? GetValue())?.ToString();
}
}

BIN
0Harmony/ILRepack.List Normal file

Binary file not shown.

View file

@ -0,0 +1,120 @@
using System;
using System.Collections.Generic;
namespace Iced.Intel.BlockEncoderInternal;
internal sealed class Block
{
public readonly CodeWriterImpl CodeWriter;
public readonly ulong RIP;
public readonly List<RelocInfo>? relocInfos;
private Instr[] instructions;
private readonly List<BlockData> dataList;
private readonly ulong alignment;
private readonly List<BlockData> validData;
private ulong validDataAddress;
private ulong validDataAddressAligned;
public Instr[] Instructions => instructions;
public bool CanAddRelocInfos => relocInfos != null;
public Block(BlockEncoder blockEncoder, CodeWriter codeWriter, ulong rip, List<RelocInfo>? relocInfos)
{
CodeWriter = new CodeWriterImpl(codeWriter);
RIP = rip;
this.relocInfos = relocInfos;
instructions = Array2.Empty<Instr>();
dataList = new List<BlockData>();
alignment = (uint)blockEncoder.Bitness / 8u;
validData = new List<BlockData>();
}
internal void SetInstructions(Instr[] instructions)
{
this.instructions = instructions;
}
public BlockData AllocPointerLocation()
{
BlockData blockData = new BlockData
{
IsValid = true
};
dataList.Add(blockData);
return blockData;
}
public void InitializeData()
{
ulong num;
if (Instructions.Length != 0)
{
Instr instr = Instructions[Instructions.Length - 1];
num = instr.IP + instr.Size;
}
else
{
num = RIP;
}
validDataAddress = num;
ulong num2 = (validDataAddressAligned = (num + alignment - 1) & ~(alignment - 1));
foreach (BlockData data in dataList)
{
if (data.IsValid)
{
data.__dont_use_address = num2;
data.__dont_use_address_initd = true;
validData.Add(data);
num2 += alignment;
}
}
}
public void WriteData()
{
if (validData.Count == 0)
{
return;
}
CodeWriterImpl codeWriter = CodeWriter;
int num = (int)(validDataAddressAligned - validDataAddress);
for (int i = 0; i < num; i++)
{
codeWriter.WriteByte(204);
}
List<RelocInfo> list = relocInfos;
if ((int)alignment == 8)
{
foreach (BlockData validDatum in validData)
{
list?.Add(new RelocInfo(RelocKind.Offset64, validDatum.Address));
uint num2 = (uint)validDatum.Data;
codeWriter.WriteByte((byte)num2);
codeWriter.WriteByte((byte)(num2 >> 8));
codeWriter.WriteByte((byte)(num2 >> 16));
codeWriter.WriteByte((byte)(num2 >> 24));
num2 = (uint)(validDatum.Data >> 32);
codeWriter.WriteByte((byte)num2);
codeWriter.WriteByte((byte)(num2 >> 8));
codeWriter.WriteByte((byte)(num2 >> 16));
codeWriter.WriteByte((byte)(num2 >> 24));
}
return;
}
throw new InvalidOperationException();
}
public void AddRelocInfo(RelocInfo relocInfo)
{
relocInfos?.Add(relocInfo);
}
}

View file

@ -0,0 +1,28 @@
namespace Iced.Intel.BlockEncoderInternal;
internal sealed class BlockData
{
internal ulong __dont_use_address;
internal bool __dont_use_address_initd;
public bool IsValid;
public ulong Data;
public ulong Address
{
get
{
if (!IsValid)
{
ThrowHelper.ThrowInvalidOperationException();
}
if (!__dont_use_address_initd)
{
ThrowHelper.ThrowInvalidOperationException();
}
return __dont_use_address;
}
}
}

View file

@ -0,0 +1,111 @@
using System;
namespace Iced.Intel.BlockEncoderInternal;
internal sealed class CallInstr : Instr
{
private readonly byte bitness;
private Instruction instruction;
private TargetInstr targetInstr;
private readonly byte origInstructionSize;
private BlockData pointerData;
private bool useOrigInstruction;
public CallInstr(BlockEncoder blockEncoder, Block block, in Instruction instruction)
: base(block, instruction.IP)
{
bitness = (byte)blockEncoder.Bitness;
this.instruction = instruction;
Instruction instruction2 = instruction;
instruction2.NearBranch64 = 0uL;
origInstructionSize = (byte)blockEncoder.GetInstructionSize(in instruction2, 0uL);
if (!blockEncoder.FixBranches)
{
Size = origInstructionSize;
useOrigInstruction = true;
}
else if (blockEncoder.Bitness == 64)
{
Size = Math.Max(origInstructionSize, 6u);
}
else
{
Size = origInstructionSize;
}
}
public override void Initialize(BlockEncoder blockEncoder)
{
targetInstr = blockEncoder.GetTarget(instruction.NearBranchTarget);
}
public override bool Optimize(ulong gained)
{
return TryOptimize(gained);
}
private bool TryOptimize(ulong gained)
{
if (Done || useOrigInstruction)
{
Done = true;
return false;
}
bool flag = bitness != 64 || targetInstr.IsInBlock(Block);
if (!flag)
{
ulong address = targetInstr.GetAddress();
ulong num = IP + origInstructionSize;
long diff = (long)(address - num);
diff = Instr.CorrectDiff(targetInstr.IsInBlock(Block), diff, gained);
flag = int.MinValue <= diff && diff <= int.MaxValue;
}
if (flag)
{
if (pointerData != null)
{
pointerData.IsValid = false;
}
Size = origInstructionSize;
useOrigInstruction = true;
Done = true;
return true;
}
if (pointerData == null)
{
pointerData = Block.AllocPointerLocation();
}
return false;
}
public override string? TryEncode(Encoder encoder, out ConstantOffsets constantOffsets, out bool isOriginalInstruction)
{
uint size;
if (useOrigInstruction)
{
isOriginalInstruction = true;
instruction.NearBranch64 = targetInstr.GetAddress();
if (!encoder.TryEncode(in instruction, IP, out size, out string errorMessage))
{
constantOffsets = default(ConstantOffsets);
return Instr.CreateErrorMessage(errorMessage, in instruction);
}
constantOffsets = encoder.GetConstantOffsets();
return null;
}
isOriginalInstruction = false;
constantOffsets = default(ConstantOffsets);
pointerData.Data = targetInstr.GetAddress();
string text = EncodeBranchToPointerData(encoder, isCall: true, IP, pointerData, out size, Size);
if (text != null)
{
return Instr.CreateErrorMessage(text, in instruction);
}
return null;
}
}

View file

@ -0,0 +1,23 @@
namespace Iced.Intel.BlockEncoderInternal;
internal sealed class CodeWriterImpl : CodeWriter
{
public uint BytesWritten;
private readonly CodeWriter codeWriter;
public CodeWriterImpl(CodeWriter codeWriter)
{
if (codeWriter == null)
{
ThrowHelper.ThrowArgumentNullException_codeWriter();
}
this.codeWriter = codeWriter;
}
public override void WriteByte(byte value)
{
BytesWritten++;
codeWriter.WriteByte(value);
}
}

Some files were not shown because too many files have changed in this diff Show more