Initial commit
This commit is contained in:
commit
c75afff785
27345 changed files with 3421853 additions and 0 deletions
13
.idea/.idea.decompiled/.idea/.gitignore
generated
vendored
Normal file
13
.idea/.idea.decompiled/.idea/.gitignore
generated
vendored
Normal 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
|
||||
8
.idea/.idea.decompiled/.idea/indexLayout.xml
generated
Normal file
8
.idea/.idea.decompiled/.idea/indexLayout.xml
generated
Normal 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
6
.idea/.idea.decompiled/.idea/vcs.xml
generated
Normal 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>
|
||||
6
0Harmony/--f__AnonymousDelegate0.cs
Normal file
6
0Harmony/--f__AnonymousDelegate0.cs
Normal 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);
|
||||
6
0Harmony/--f__AnonymousDelegate1.cs
Normal file
6
0Harmony/--f__AnonymousDelegate1.cs
Normal 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);
|
||||
|
|
@ -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
|
|
@ -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) */;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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
28
0Harmony/0Harmony.csproj
Normal 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
6
0Harmony/Consts.cs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
internal static class Consts
|
||||
{
|
||||
public const string AssemblyName = "Mono.Cecil";
|
||||
|
||||
public const string PublicKey = "00240000048000009400000006020000002400005253413100040000010001002b5c9f7f04346c324a3176f8d3ee823bbf2d60efdbc35f86fd9e65ea3e6cd11bcdcba3a353e55133c8ac5c4caaba581b2c6dfff2cc2d0edc43959ddb86b973300a479a82419ef489c3225f1fe429a708507bd515835160e10bc743d20ca33ab9570cfd68d479fcf0bc797a763bec5d1000f0159ef619e709d915975e87beebaf";
|
||||
}
|
||||
32
0Harmony/FxResources.System.ValueTuple.SR.resx
Normal file
32
0Harmony/FxResources.System.ValueTuple.SR.resx
Normal 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>
|
||||
5
0Harmony/FxResources.System.ValueTuple/SR.cs
Normal file
5
0Harmony/FxResources.System.ValueTuple/SR.cs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
namespace FxResources.System.ValueTuple;
|
||||
|
||||
internal static class SR
|
||||
{
|
||||
}
|
||||
112
0Harmony/HarmonyLib/AccessCache.cs
Normal file
112
0Harmony/HarmonyLib/AccessCache.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
1533
0Harmony/HarmonyLib/AccessTools.cs
Normal file
1533
0Harmony/HarmonyLib/AccessTools.cs
Normal file
File diff suppressed because it is too large
Load diff
245
0Harmony/HarmonyLib/AccessToolsExtensions.cs
Normal file
245
0Harmony/HarmonyLib/AccessToolsExtensions.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
9
0Harmony/HarmonyLib/ArgumentType.cs
Normal file
9
0Harmony/HarmonyLib/ArgumentType.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
namespace HarmonyLib;
|
||||
|
||||
public enum ArgumentType
|
||||
{
|
||||
Normal,
|
||||
Ref,
|
||||
Out,
|
||||
Pointer
|
||||
}
|
||||
75
0Harmony/HarmonyLib/AttributePatch.cs
Normal file
75
0Harmony/HarmonyLib/AttributePatch.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
92
0Harmony/HarmonyLib/ByteBuffer.cs
Normal file
92
0Harmony/HarmonyLib/ByteBuffer.cs
Normal 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
2273
0Harmony/HarmonyLib/Code.cs
Normal file
File diff suppressed because it is too large
Load diff
298
0Harmony/HarmonyLib/CodeInstruction.cs
Normal file
298
0Harmony/HarmonyLib/CodeInstruction.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
514
0Harmony/HarmonyLib/CodeInstructionExtensions.cs
Normal file
514
0Harmony/HarmonyLib/CodeInstructionExtensions.cs
Normal 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());
|
||||
}
|
||||
}
|
||||
11
0Harmony/HarmonyLib/CodeInstructionsExtensions.cs
Normal file
11
0Harmony/HarmonyLib/CodeInstructionsExtensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
315
0Harmony/HarmonyLib/CodeMatch.cs
Normal file
315
0Harmony/HarmonyLib/CodeMatch.cs
Normal 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>()) + "]";
|
||||
}
|
||||
}
|
||||
513
0Harmony/HarmonyLib/CodeMatcher.cs
Normal file
513
0Harmony/HarmonyLib/CodeMatcher.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
337
0Harmony/HarmonyLib/CodeTranspiler.cs
Normal file
337
0Harmony/HarmonyLib/CodeTranspiler.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
67
0Harmony/HarmonyLib/CollectionExtensions.cs
Normal file
67
0Harmony/HarmonyLib/CollectionExtensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
40
0Harmony/HarmonyLib/DelegateTypeFactory.cs
Normal file
40
0Harmony/HarmonyLib/DelegateTypeFactory.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
366
0Harmony/HarmonyLib/Emitter.cs
Normal file
366
0Harmony/HarmonyLib/Emitter.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
17
0Harmony/HarmonyLib/ExceptionBlock.cs
Normal file
17
0Harmony/HarmonyLib/ExceptionBlock.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
11
0Harmony/HarmonyLib/ExceptionBlockType.cs
Normal file
11
0Harmony/HarmonyLib/ExceptionBlockType.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
namespace HarmonyLib;
|
||||
|
||||
public enum ExceptionBlockType
|
||||
{
|
||||
BeginExceptionBlock,
|
||||
BeginCatchBlock,
|
||||
BeginExceptFilterBlock,
|
||||
BeginFaultBlock,
|
||||
BeginFinallyBlock,
|
||||
EndExceptionBlock
|
||||
}
|
||||
104
0Harmony/HarmonyLib/FastAccess.cs
Normal file
104
0Harmony/HarmonyLib/FastAccess.cs
Normal 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)
|
||||
});
|
||||
}
|
||||
}
|
||||
3
0Harmony/HarmonyLib/FastInvokeHandler.cs
Normal file
3
0Harmony/HarmonyLib/FastInvokeHandler.cs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
namespace HarmonyLib;
|
||||
|
||||
public delegate object FastInvokeHandler(object target, params object[] parameters);
|
||||
211
0Harmony/HarmonyLib/FileLog.cs
Normal file
211
0Harmony/HarmonyLib/FileLog.cs
Normal 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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
165
0Harmony/HarmonyLib/GeneralExtensions.cs
Normal file
165
0Harmony/HarmonyLib/GeneralExtensions.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
6
0Harmony/HarmonyLib/GetterHandler.cs
Normal file
6
0Harmony/HarmonyLib/GetterHandler.cs
Normal 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);
|
||||
251
0Harmony/HarmonyLib/Harmony.cs
Normal file
251
0Harmony/HarmonyLib/Harmony.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
12
0Harmony/HarmonyLib/HarmonyAfter.cs
Normal file
12
0Harmony/HarmonyLib/HarmonyAfter.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
37
0Harmony/HarmonyLib/HarmonyArgument.cs
Normal file
37
0Harmony/HarmonyLib/HarmonyArgument.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
8
0Harmony/HarmonyLib/HarmonyAttribute.cs
Normal file
8
0Harmony/HarmonyLib/HarmonyAttribute.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
using System;
|
||||
|
||||
namespace HarmonyLib;
|
||||
|
||||
public class HarmonyAttribute : Attribute
|
||||
{
|
||||
public HarmonyMethod info = new HarmonyMethod();
|
||||
}
|
||||
12
0Harmony/HarmonyLib/HarmonyBefore.cs
Normal file
12
0Harmony/HarmonyLib/HarmonyBefore.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
8
0Harmony/HarmonyLib/HarmonyCleanup.cs
Normal file
8
0Harmony/HarmonyLib/HarmonyCleanup.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
using System;
|
||||
|
||||
namespace HarmonyLib;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class HarmonyCleanup : Attribute
|
||||
{
|
||||
}
|
||||
12
0Harmony/HarmonyLib/HarmonyDebug.cs
Normal file
12
0Harmony/HarmonyLib/HarmonyDebug.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
|
||||
namespace HarmonyLib;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public class HarmonyDebug : HarmonyAttribute
|
||||
{
|
||||
public HarmonyDebug()
|
||||
{
|
||||
info.debug = true;
|
||||
}
|
||||
}
|
||||
104
0Harmony/HarmonyLib/HarmonyDelegate.cs
Normal file
104
0Harmony/HarmonyLib/HarmonyDelegate.cs
Normal 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)
|
||||
{
|
||||
}
|
||||
}
|
||||
88
0Harmony/HarmonyLib/HarmonyException.cs
Normal file
88
0Harmony/HarmonyLib/HarmonyException.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
8
0Harmony/HarmonyLib/HarmonyFinalizer.cs
Normal file
8
0Harmony/HarmonyLib/HarmonyFinalizer.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
using System;
|
||||
|
||||
namespace HarmonyLib;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class HarmonyFinalizer : Attribute
|
||||
{
|
||||
}
|
||||
156
0Harmony/HarmonyLib/HarmonyMethod.cs
Normal file
156
0Harmony/HarmonyLib/HarmonyMethod.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
111
0Harmony/HarmonyLib/HarmonyMethodExtensions.cs
Normal file
111
0Harmony/HarmonyLib/HarmonyMethodExtensions.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
157
0Harmony/HarmonyLib/HarmonyPatch.cs
Normal file
157
0Harmony/HarmonyLib/HarmonyPatch.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
8
0Harmony/HarmonyLib/HarmonyPatchAll.cs
Normal file
8
0Harmony/HarmonyLib/HarmonyPatchAll.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
using System;
|
||||
|
||||
namespace HarmonyLib;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class HarmonyPatchAll : HarmonyAttribute
|
||||
{
|
||||
}
|
||||
12
0Harmony/HarmonyLib/HarmonyPatchCategory.cs
Normal file
12
0Harmony/HarmonyLib/HarmonyPatchCategory.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
11
0Harmony/HarmonyLib/HarmonyPatchType.cs
Normal file
11
0Harmony/HarmonyLib/HarmonyPatchType.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
namespace HarmonyLib;
|
||||
|
||||
public enum HarmonyPatchType
|
||||
{
|
||||
All,
|
||||
Prefix,
|
||||
Postfix,
|
||||
Transpiler,
|
||||
Finalizer,
|
||||
ReversePatch
|
||||
}
|
||||
8
0Harmony/HarmonyLib/HarmonyPostfix.cs
Normal file
8
0Harmony/HarmonyLib/HarmonyPostfix.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
using System;
|
||||
|
||||
namespace HarmonyLib;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class HarmonyPostfix : Attribute
|
||||
{
|
||||
}
|
||||
8
0Harmony/HarmonyLib/HarmonyPrefix.cs
Normal file
8
0Harmony/HarmonyLib/HarmonyPrefix.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
using System;
|
||||
|
||||
namespace HarmonyLib;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class HarmonyPrefix : Attribute
|
||||
{
|
||||
}
|
||||
8
0Harmony/HarmonyLib/HarmonyPrepare.cs
Normal file
8
0Harmony/HarmonyLib/HarmonyPrepare.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
using System;
|
||||
|
||||
namespace HarmonyLib;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class HarmonyPrepare : Attribute
|
||||
{
|
||||
}
|
||||
12
0Harmony/HarmonyLib/HarmonyPriority.cs
Normal file
12
0Harmony/HarmonyLib/HarmonyPriority.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
12
0Harmony/HarmonyLib/HarmonyReversePatch.cs
Normal file
12
0Harmony/HarmonyLib/HarmonyReversePatch.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
7
0Harmony/HarmonyLib/HarmonyReversePatchType.cs
Normal file
7
0Harmony/HarmonyLib/HarmonyReversePatchType.cs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
namespace HarmonyLib;
|
||||
|
||||
public enum HarmonyReversePatchType
|
||||
{
|
||||
Original,
|
||||
Snapshot
|
||||
}
|
||||
126
0Harmony/HarmonyLib/HarmonySharedState.cs
Normal file
126
0Harmony/HarmonyLib/HarmonySharedState.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
8
0Harmony/HarmonyLib/HarmonyTargetMethod.cs
Normal file
8
0Harmony/HarmonyLib/HarmonyTargetMethod.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
using System;
|
||||
|
||||
namespace HarmonyLib;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class HarmonyTargetMethod : Attribute
|
||||
{
|
||||
}
|
||||
8
0Harmony/HarmonyLib/HarmonyTargetMethods.cs
Normal file
8
0Harmony/HarmonyLib/HarmonyTargetMethods.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
using System;
|
||||
|
||||
namespace HarmonyLib;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class HarmonyTargetMethods : Attribute
|
||||
{
|
||||
}
|
||||
8
0Harmony/HarmonyLib/HarmonyTranspiler.cs
Normal file
8
0Harmony/HarmonyLib/HarmonyTranspiler.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
using System;
|
||||
|
||||
namespace HarmonyLib;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class HarmonyTranspiler : Attribute
|
||||
{
|
||||
}
|
||||
119
0Harmony/HarmonyLib/ILInstruction.cs
Normal file
119
0Harmony/HarmonyLib/ILInstruction.cs
Normal 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}";
|
||||
}
|
||||
}
|
||||
96
0Harmony/HarmonyLib/InlineSignature.cs
Normal file
96
0Harmony/HarmonyLib/InlineSignature.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
205
0Harmony/HarmonyLib/InlineSignatureParser.cs
Normal file
205
0Harmony/HarmonyLib/InlineSignatureParser.cs
Normal 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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
0Harmony/HarmonyLib/InstantiationHandler.cs
Normal file
3
0Harmony/HarmonyLib/InstantiationHandler.cs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
namespace HarmonyLib;
|
||||
|
||||
public delegate T InstantiationHandler<out T>();
|
||||
9
0Harmony/HarmonyLib/LeaveTry.cs
Normal file
9
0Harmony/HarmonyLib/LeaveTry.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
namespace HarmonyLib;
|
||||
|
||||
internal class LeaveTry
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return "(autogenerated)";
|
||||
}
|
||||
}
|
||||
11
0Harmony/HarmonyLib/MethodBaseExtensions.cs
Normal file
11
0Harmony/HarmonyLib/MethodBaseExtensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
765
0Harmony/HarmonyLib/MethodBodyReader.cs
Normal file
765
0Harmony/HarmonyLib/MethodBodyReader.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
74
0Harmony/HarmonyLib/MethodCopier.cs
Normal file
74
0Harmony/HarmonyLib/MethodCopier.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
7
0Harmony/HarmonyLib/MethodDispatchType.cs
Normal file
7
0Harmony/HarmonyLib/MethodDispatchType.cs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
namespace HarmonyLib;
|
||||
|
||||
public enum MethodDispatchType
|
||||
{
|
||||
VirtualCall,
|
||||
Call
|
||||
}
|
||||
169
0Harmony/HarmonyLib/MethodInvoker.cs
Normal file
169
0Harmony/HarmonyLib/MethodInvoker.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
1183
0Harmony/HarmonyLib/MethodPatcher.cs
Normal file
1183
0Harmony/HarmonyLib/MethodPatcher.cs
Normal file
File diff suppressed because it is too large
Load diff
12
0Harmony/HarmonyLib/MethodType.cs
Normal file
12
0Harmony/HarmonyLib/MethodType.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
namespace HarmonyLib;
|
||||
|
||||
public enum MethodType
|
||||
{
|
||||
Normal,
|
||||
Getter,
|
||||
Setter,
|
||||
Constructor,
|
||||
StaticConstructor,
|
||||
Enumerator,
|
||||
Async
|
||||
}
|
||||
124
0Harmony/HarmonyLib/Patch.cs
Normal file
124
0Harmony/HarmonyLib/Patch.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
123
0Harmony/HarmonyLib/PatchArgumentExtensions.cs
Normal file
123
0Harmony/HarmonyLib/PatchArgumentExtensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
363
0Harmony/HarmonyLib/PatchClassProcessor.cs
Normal file
363
0Harmony/HarmonyLib/PatchClassProcessor.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
88
0Harmony/HarmonyLib/PatchFunctions.cs
Normal file
88
0Harmony/HarmonyLib/PatchFunctions.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
124
0Harmony/HarmonyLib/PatchInfo.cs
Normal file
124
0Harmony/HarmonyLib/PatchInfo.cs
Normal 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>();
|
||||
}
|
||||
}
|
||||
61
0Harmony/HarmonyLib/PatchInfoSerialization.cs
Normal file
61
0Harmony/HarmonyLib/PatchInfoSerialization.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
75
0Harmony/HarmonyLib/PatchJobs.cs
Normal file
75
0Harmony/HarmonyLib/PatchJobs.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
264
0Harmony/HarmonyLib/PatchProcessor.cs
Normal file
264
0Harmony/HarmonyLib/PatchProcessor.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
214
0Harmony/HarmonyLib/PatchSorter.cs
Normal file
214
0Harmony/HarmonyLib/PatchSorter.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
157
0Harmony/HarmonyLib/PatchTools.cs
Normal file
157
0Harmony/HarmonyLib/PatchTools.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
54
0Harmony/HarmonyLib/Patches.cs
Normal file
54
0Harmony/HarmonyLib/Patches.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
22
0Harmony/HarmonyLib/Priority.cs
Normal file
22
0Harmony/HarmonyLib/Priority.cs
Normal 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;
|
||||
}
|
||||
3
0Harmony/HarmonyLib/RefResult.cs
Normal file
3
0Harmony/HarmonyLib/RefResult.cs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
namespace HarmonyLib;
|
||||
|
||||
public delegate ref T RefResult<T>();
|
||||
35
0Harmony/HarmonyLib/ReversePatcher.cs
Normal file
35
0Harmony/HarmonyLib/ReversePatcher.cs
Normal 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 + ">"));
|
||||
}
|
||||
}
|
||||
6
0Harmony/HarmonyLib/SetterHandler.cs
Normal file
6
0Harmony/HarmonyLib/SetterHandler.cs
Normal 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);
|
||||
40
0Harmony/HarmonyLib/SymbolExtensions.cs
Normal file
40
0Harmony/HarmonyLib/SymbolExtensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
154
0Harmony/HarmonyLib/Tools.cs
Normal file
154
0Harmony/HarmonyLib/Tools.cs
Normal 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)");
|
||||
}
|
||||
}
|
||||
}
|
||||
62
0Harmony/HarmonyLib/Transpilers.cs
Normal file
62
0Harmony/HarmonyLib/Transpilers.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
434
0Harmony/HarmonyLib/Traverse.cs
Normal file
434
0Harmony/HarmonyLib/Traverse.cs
Normal 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
BIN
0Harmony/ILRepack.List
Normal file
Binary file not shown.
120
0Harmony/Iced.Intel.BlockEncoderInternal/Block.cs
Normal file
120
0Harmony/Iced.Intel.BlockEncoderInternal/Block.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
28
0Harmony/Iced.Intel.BlockEncoderInternal/BlockData.cs
Normal file
28
0Harmony/Iced.Intel.BlockEncoderInternal/BlockData.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
111
0Harmony/Iced.Intel.BlockEncoderInternal/CallInstr.cs
Normal file
111
0Harmony/Iced.Intel.BlockEncoderInternal/CallInstr.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
23
0Harmony/Iced.Intel.BlockEncoderInternal/CodeWriterImpl.cs
Normal file
23
0Harmony/Iced.Intel.BlockEncoderInternal/CodeWriterImpl.cs
Normal 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
Loading…
Add table
Add a link
Reference in a new issue