1- using COTL_API . CustomInventory ;
1+ using System . Reflection ;
22using HarmonyLib ;
3- using Sirenix . Utilities ;
3+ using UnityEngine . UIElements . Collections ;
44
55namespace COTL_API . HarmonyUtils ;
66
77public class Patcher
88{
9- private List < PatchProcessor > xyz = new ( ) ;
9+ private readonly HashSet < MethodBase > _patchedMethods = new ( ) ;
1010
11- public void PatchAll ( )
11+ public void PatchAll ( Type targetType )
1212 {
13- CreatePatchClass ( typeof ( InventoryItem ) , typeof ( CustomInventoryItem ) ) ;
13+ if ( targetType == null )
14+ throw new ArgumentNullException ( nameof ( targetType ) ) ;
15+
16+ var candidateMethods = targetType
17+ . GetMethods ( BindingFlags . Public | BindingFlags . Instance | BindingFlags . Static | BindingFlags . NonPublic )
18+ . Where ( IsEnumMethod )
19+ . Where ( m => ! _patchedMethods . Contains ( m ) ) ;
20+
21+ foreach ( var method in candidateMethods )
22+ try
23+ {
24+ HarmonyMethod patchMethod ;
25+ if ( method . ReturnType == typeof ( void ) )
26+ patchMethod =
27+ new HarmonyMethod ( typeof ( GenericEnumPatch ) . GetMethod ( nameof ( GenericEnumPatch . PrefixVoid ) ) ) ;
28+ else
29+ patchMethod =
30+ new HarmonyMethod (
31+ typeof ( GenericEnumPatch ) . GetMethod ( nameof ( GenericEnumPatch . PrefixWithReturn ) ) ) ;
32+
33+ Plugin . Instance ! . _harmony . Patch ( method , patchMethod ) ;
34+ _patchedMethods . Add ( method ) ;
35+
36+ LogDebug ( $ "Successfully patched method: { method . DeclaringType ? . Name } .{ method . Name } " +
37+ $ "(Enum: { method . GetParameters ( ) [ 0 ] . ParameterType . Name } )") ;
38+ }
39+ catch ( Exception ex )
40+ {
41+ LogError ( $ "Failed to patch method { method . Name } : { ex . Message } ") ;
42+ }
43+ }
44+
45+ public void UnpatchAll ( Type targetType )
46+ {
47+ if ( targetType == null )
48+ throw new ArgumentNullException ( nameof ( targetType ) ) ;
49+
50+ var candidateMethods = targetType
51+ . GetMethods ( BindingFlags . Public | BindingFlags . Instance | BindingFlags . Static | BindingFlags . NonPublic )
52+ . Where ( IsEnumMethod )
53+ . Where ( m => _patchedMethods . Contains ( m ) ) ;
54+
55+ foreach ( var method in candidateMethods )
56+ {
57+ HarmonyMethod patchMethod ;
58+ if ( method . ReturnType == typeof ( void ) )
59+ patchMethod =
60+ new HarmonyMethod ( typeof ( GenericEnumPatch ) . GetMethod ( nameof ( GenericEnumPatch . PrefixVoid ) ) ) ;
61+ else
62+ patchMethod =
63+ new HarmonyMethod (
64+ typeof ( GenericEnumPatch ) . GetMethod ( nameof ( GenericEnumPatch . PrefixWithReturn ) )
65+ ) ;
66+
67+ Plugin . Instance ! . _harmony . Unpatch ( method , patchMethod . method ) ;
68+ }
1469 }
15-
16- public void CreatePatchClass ( Type orig , Type dest )
70+
71+ private static bool IsEnumMethod ( MethodInfo method )
1772 {
18- var destMethods = dest . GetMethods ( ) ;
19-
20- foreach ( var x in orig . GetMethods ( ) )
73+ var parameters = method . GetParameters ( ) ;
74+ return parameters . Length > 0 && parameters [ 0 ] . ParameterType . IsEnum ;
75+ }
76+ }
77+
78+ public static class GenericEnumPatch
79+ {
80+ [ HarmonyPriority ( Priority . High ) ]
81+ public static bool PrefixVoid (
82+ MethodBase __originalMethod ,
83+ object __instance ,
84+ object [ ] __args )
85+ {
86+ try
2187 {
22- LogDebug ( "Trying to patch " + x . Name + "AAAAA" ) ;
23-
24- var method = destMethods . FirstOrDefault ( info =>
88+ if ( __args == null || __args . Length == 0 || ! ( __args [ 0 ] is Enum enumValue ) )
89+ return true ;
90+
91+ var registry = RegistryManager . GetRegistry ( enumValue . GetType ( ) ) ;
92+
93+ var customClass = registry . Get ( enumValue ) ;
94+ if ( customClass == null )
95+ return true ;
96+
97+ var customType = customClass . GetType ( ) ;
98+ var customMethods = customType . GetMethods (
99+ BindingFlags . Public | BindingFlags . Instance | BindingFlags . Static | BindingFlags . NonPublic ) ;
100+
101+ var originalParams = __originalMethod . GetParameters ( ) . Skip ( 1 ) . ToArray ( ) ;
102+
103+ var customMethod = customMethods . FirstOrDefault ( m =>
104+ m . Name == __originalMethod . Name &&
105+ IsParameterCompatible ( originalParams , m . GetParameters ( ) ) ) ;
106+
107+ if ( customMethod == null )
25108 {
26- if ( info . Name != x . Name ) return false ;
27- if ( info . ReturnType != dest . GetReturnType ( ) ) return false ;
28- return info . GetParameters ( ) . Length == x . GetParameters ( ) . Length &&
29- x . GetParameters ( ) . All ( parameter => parameter . GetType ( ) == info . GetParameters ( ) [ parameter . Position ] . ParameterType ) ;
30- } ) ;
31-
32- if ( method is null )
33- return ;
34-
35- var processor = new PatchProcessor ( Plugin . Instance ! . _harmony , x ) ;
36- processor . AddPrefix ( new HarmonyMethod
109+ LogDebug ( $ "No matching custom method found for { __originalMethod . Name } in { customType . Name } ") ;
110+ return true ;
111+ }
112+
113+ LogDebug ( $ "Invoking custom method: { customMethod . Name } ") ;
114+
115+ var customArgs = __args . Skip ( 1 ) . ToArray ( ) ;
116+ customMethod . Invoke ( customClass , customArgs ) ;
117+
118+ return false ;
119+ }
120+ catch ( Exception ex )
121+ {
122+ LogError ( $ "Error in PrefixVoid: { ex . Message } ") ;
123+ return true ;
124+ }
125+ }
126+
127+ [ HarmonyPriority ( Priority . High ) ]
128+ public static bool PrefixWithReturn (
129+ MethodBase __originalMethod ,
130+ object __instance ,
131+ ref object ? __result ,
132+ object [ ] __args )
133+ {
134+ try
135+ {
136+ if ( __args == null || __args . Length == 0 || __args [ 0 ] is not Enum enumValue )
137+ return true ;
138+
139+ var registry = RegistryManager . GetRegistry ( enumValue . GetType ( ) ) ;
140+
141+ var customClass = registry . Get ( enumValue ) ;
142+ if ( customClass == null )
143+ return true ;
144+
145+ var customType = customClass . GetType ( ) ;
146+ var customMethods = customType . GetMethods (
147+ BindingFlags . Public | BindingFlags . Instance | BindingFlags . Static | BindingFlags . NonPublic ) ;
148+
149+ var originalParams = __originalMethod . GetParameters ( ) . Skip ( 1 ) . ToArray ( ) ;
150+
151+ var customMethod = customMethods . FirstOrDefault ( m =>
152+ m . Name == __originalMethod . Name &&
153+ IsParameterCompatible ( originalParams , m . GetParameters ( ) ) ) ;
154+
155+ if ( customMethod == null )
37156 {
38- method = method ,
39- methodName = method . Name ,
40- argumentTypes = method . GetParameters ( ) . Select ( p => p . ParameterType ) . ToArray ( ) ,
41-
42- } ) ;
43- processor . Patch ( ) ;
44-
45- xyz . Add ( processor ) ;
157+ LogDebug ( $ "No matching custom method found for { __originalMethod . Name } in { customType . Name } ") ;
158+ return true ;
159+ }
160+
161+ LogDebug ( $ "Invoking custom method: { customMethod . Name } ") ;
162+
163+ var customArgs = __args . Skip ( 1 ) . ToArray ( ) ;
164+ __result = customMethod . Invoke ( customClass , customArgs ) ;
165+
166+ return false ;
167+ }
168+ catch ( Exception ex )
169+ {
170+ LogError ( $ "Error in PrefixWithReturn: { ex . Message } ") ;
171+ return true ;
46172 }
47173 }
174+
175+ private static bool IsCompatible ( ParameterInfo info1 , ParameterInfo info2 )
176+ {
177+ if ( info1 == null || info2 == null )
178+ return false ;
179+
180+ return info1 . ParameterType . IsAssignableFrom ( info2 . ParameterType ) ||
181+ info2 . ParameterType . IsAssignableFrom ( info1 . ParameterType ) ;
182+ }
183+
184+ private static bool IsParameterCompatible ( ParameterInfo [ ] originalParams , ParameterInfo [ ] customParams )
185+ {
186+ if ( originalParams == null || customParams == null )
187+ return false ;
188+
189+ if ( originalParams . Length != customParams . Length )
190+ return false ;
191+
192+ return ! originalParams . Where ( ( t , i ) => ! IsCompatible ( t , customParams [ i ] ) ) . Any ( ) ;
193+ }
48194}
0 commit comments