gregros / patchwork Goto Github PK
View Code? Open in Web Editor NEWA library for modifying .NET assemblies.
License: MIT License
A library for modifying .NET assemblies.
License: MIT License
The following pattern doesn't always work as expected:
[NewMember]
[DuplicatesBody("foo")]
public void ori_foo() { }
[ModifiesMember("foo")]
public void mod_foo()
{
ori_foo();
}
This seems to be caused by AssemblyPatcher::UpdateMethods: the order in which it iterates over lists of NewMember & ModifiesMember is undefined. If it processes NewMember attributes first, everything works correctly. Otherwise it would duplicate mod_foo()'s body into ori_foo() (and cause infinite recursion).
Now, this is a bit strange - the comment in GetBodySource seems to imply that it should always get the unmodified body, regardless of the patching order. At least for me, it doesn't work like that - and looking at the code, I don't really understand why should it work differently.
A simple fix would be to always process NewMembers before ModifiesMembers by writing two loops.
How do you modify named constructors with and without parameters? For example:
public class StatsDistribution {
public StatsDistribution() {
this.Points = 0;
// snip
}
}
Or:
public class LevelUpState {
public LevelUpState(UnitDescriptor unit, bool isPregen) {
this.m_Unit = unit;
// snip
}
}
The objective is to be able to use this
in new and modified members.
In LaunchManager.cs
, Command_ChangeFolder exits the application if the user presses the OK button.
In Win11, PatchworkLauncher stops working and frequently displays various errors.
I checked several times to see if .NET Framework 4.5 was installed and used the .NET repair tool to be on the safe side.
I created a new Patchwork project. The only "mod" consists of declaring a new type and nothing else.
In TryConstructAttribute
, customAttrData.Constructor
is {System.Void System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(System.String)}
when the null is created.
In CustomAttributeHelper.cs
:
private static object TryConstructAttribute(this CustomAttribute customAttrData) {
var constructor = customAttrData.Constructor.Resolve();
// customAttrData.Constructor == {System.Void System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(System.String)}
if (constructor == null)
{
throw new NullReferenceException();
}
var constructorInfo = (ConstructorInfo) constructor.LoadMethod();
var args = customAttrData.ConstructorArguments.Select(UnpackArgument).ToArray();
if (constructorInfo.GetParameters().Any(p => p.ParameterType == typeof (Type))) {
//we cannot invoke this constructor.
return null;
}
var ret = constructorInfo.Invoke(args);
return ret;
}
System.NullReferenceException
HResult=0x80004003
Message=Object reference not set to an instance of an object.
Source=Patchwork.Engine
StackTrace:
at Patchwork.Engine.Utility.CecilLoader.LoadMethod(MethodDefinition methodDef) in E:\repos\DeadfireMods.pw Launcher\Patchwork.Engine\Utility\Reflections and Cecil\CecilLoader.cs:line 111
at Patchwork.Engine.Utility.CustomAttributeHelper.TryConstructAttribute(CustomAttribute customAttrData) in E:\repos\DeadfireMods.pw Launcher\Patchwork.Engine\Utility\Reflections and Cecil\CustomAttributeHelper.cs:line 137
at Patchwork.Engine.Utility.CustomAttributeHelper.<>c.<GetAllCustomAttributes>b__3_1(CustomAttribute attr) in E:\repos\DeadfireMods.pw Launcher\Patchwork.Engine\Utility\Reflections and Cecil\CustomAttributeHelper.cs:line 43
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at Patchwork.Engine.Utility.CustomAttributeHelper.GetAllCustomAttributes(ICustomAttributeProvider provider) in E:\repos\DeadfireMods.pw Launcher\Patchwork.Engine\Utility\Reflections and Cecil\CustomAttributeHelper.cs:line 44
at Patchwork.Engine.Utility.CustomAttributeHelper.GetCustomAttributes[T](ICustomAttributeProvider provider) in E:\repos\DeadfireMods.pw Launcher\Patchwork.Engine\Utility\Reflections and Cecil\CustomAttributeHelper.cs:line 59
at Patchwork.Engine.Utility.CustomAttributeHelper.HasCustomAttribute[T](ICustomAttributeProvider memberDef) in E:\repos\DeadfireMods.pw Launcher\Patchwork.Engine\Utility\Reflections and Cecil\CustomAttributeHelper.cs:line 24
at Patchwork.Engine.Utility.CustomAttributeHelper.IsPatchingAssembly(AssemblyDefinition assembly) in E:\repos\DeadfireMods.pw Launcher\Patchwork.Engine\Utility\Reflections and Cecil\CustomAttributeHelper.cs:line 33
at Patchwork.Engine.AssemblyPatcher.<>c.<CopyCustomAttributes>b__33_8(<>f__AnonymousType12`2 <>h__TransparentIdentifier3) in E:\repos\DeadfireMods.pw Launcher\Patchwork.Engine\AssemblyPatcher\ModifyExisting.cs:line 61
at System.Linq.Enumerable.<>c__DisplayClass6_0`1.<CombinePredicates>b__0(TSource x)
at System.Linq.Enumerable.<>c__DisplayClass6_0`1.<CombinePredicates>b__0(TSource x)
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at Patchwork.Engine.AssemblyPatcher.CopyCustomAttributes(ICustomAttributeProvider targetMember, ICustomAttributeProvider yourMember, Func`2 filter) in E:\repos\DeadfireMods.pw Launcher\Patchwork.Engine\AssemblyPatcher\ModifyExisting.cs:line 66
at Patchwork.Engine.AssemblyPatcher.ModifyTypeDecleration(TypeDefinition yourType) in E:\repos\DeadfireMods.pw Launcher\Patchwork.Engine\AssemblyPatcher\ModifyExisting.cs:line 231
at Patchwork.Engine.AssemblyPatcher.UpdateTypes(SimpleTypeLookup`1 typeActions) in E:\repos\DeadfireMods.pw Launcher\Patchwork.Engine\AssemblyPatcher\AssemblyPatcher.cs:line 239
at Patchwork.Engine.AssemblyPatcher.PatchManifest(PatchingManifest manifest, IProgressMonitor o) in E:\repos\DeadfireMods.pw Launcher\Patchwork.Engine\AssemblyPatcher\AssemblyPatcher.cs:line 422
at PatchworkLauncher.LaunchManager.ApplyInstructions(IEnumerable`1 patchGroups, ProgressObject po) in E:\repos\DeadfireMods.pw Launcher\PatchworkLauncher\GUI\LaunchManager.cs:line 709
at PatchworkLauncher.LaunchManager.<>c__DisplayClass46_1.<Command_Patch>b__0() in E:\repos\DeadfireMods.pw Launcher\PatchworkLauncher\GUI\LaunchManager.cs:line 335
at System.Threading.Tasks.Task.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
Some of the latest unity games come with separate executables for 32\64 bit platforms, and I figured that currently patchwork isn't able to let user choose which executable to use.
I made a fork to fix this for myself. My coding skills are pretty basic so I'm not sure I should commit to a project like this, but if you want I can make a pull request to save you some time.
Describe the bug
Unexpected patching failure of Tyranny's Assembly-CSharp.dll.
I don't know why this happens; it's just a public class that I want to patch: public class UIConversationManager : UIHudWindow
The issue persists even if I remove all the member modifications:
using Patchwork.Attributes;
namespace TyrannyHighDPI.pw
{
[ModifiesType]
public class ModUIConversationManager : UIConversationManager {}
}
2023-01-07 22:55:08.146 +01:00 [Information] Created patcher for assembly: Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
2023-01-07 22:55:08.196 +01:00 [Information] =====Creating new fields=====
2023-01-07 22:55:08.285 +01:00 [Error] An error has occurred,
While trying to: Patch the game
Error type: A system error or some sort of bug. (KeyNotFoundException)
Internal message: The given key was not present in the dictionary.
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at Patchwork.AssemblyPatcher.FixTypeReference(TypeReference yourTypeRef)
at Patchwork.AssemblyPatcher.FixMethodReference(MethodReference yourMethodRef, Boolean isntFixTypeCall)
at Patchwork.AssemblyPatcher.TransferMethodBody(MethodDefinition targetMethod, MethodDefinition yourMethod)
at Patchwork.AssemblyPatcher.ModifyMethod(TypeDefinition targetType, MethodDefinition yourMethod, MemberActionAttribute memberAction, MethodDefinition targetMethod)
at Patchwork.AssemblyPatcher.UpdateMethods(SimpleTypeLookup`1 methodActions)
at Patchwork.AssemblyPatcher.PatchManifest(PatchingManifest manifest, ProgressObject o)
at PatchworkLauncher.LaunchManager.ApplyInstructions(IEnumerable`1 patchGroups, ProgressObject po)
at PatchworkLauncher.LaunchManager.<>c__DisplayClass46_0.<Command_Patch>b__0()
at System.Threading.Tasks.Task.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at PatchworkLauncher.LaunchManager.<Command_Patch>d__46.MoveNext()
Patch and Target Binaries
TyrannyHighDPI.pw.zip
I don't wanna get in trouble for sharing Obsidian's original file publicly. Can provide it on a person-to-person basis.
Example (from IEMod):
[ModifiesMember(nameof(LoadPrefab))]
public static T mod_LoadPrefab(string assetName, bool instantiate)
where T : Object
Source object has:
public static T LoadPrefab(string assetName, bool instantiate)
where T : Object
and
public static Object LoadPrefab(string prefabPath, bool instantiate)
Result: InvalidOperation Exception, because GetMethodLike in CecilHelper is expecting to get exactly one result from the source object, but both methods from the source object are matching and it can't pick between them. I'd expect it instead to realize that since the mod method is templatized it should prefer the templatized source method.
I recently implemented a way for Patchwork to launch a game through Steam or Galaxy, but because Steam or Galaxy does not exit with the game, the launcher stays resident. Instead of minimizing Patchwork to the tray with no possibility of restoring the window, why not keep the window open?
You can just try to restore patched files when you try to launch/do a test run again if the state is not idle, or when you close the launcher.
I'm partially thinking aloud, but I don't see why the launcher needs to disappear.
If I remove one of the two DLLs, patching completes successfully.
System.ArgumentOutOfRangeException: Value of '4' is not valid for 'Value'. 'Value' should be between 'minimum' and 'maximum'.
Parameter name: Value
at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
at System.Windows.Forms.Control.Invoke(Delegate method)
at PatchworkLauncher.GuiBindings.<>c__DisplayClass0_0`2.<Bind>b__0(Action act)
at Patchwork.Utility.Binding.DispatchingBindable`1.set_Value(T value)
at Patchwork.Utility.Binding.Binding`1.OnChanged(IBindable`1 changedBinding)
at System.Action`1.Invoke(T obj)
at Patchwork.Utility.Binding.BindableBase`1.NotifyChange()
at Patchwork.Utility.Binding.VariableBindable`1.set_Value(T value)
at PatchworkLauncher.LaunchManager.ApplyInstructions(IEnumerable`1 patchGroups, ProgressObject po)
at PatchworkLauncher.LaunchManager.<>c__DisplayClass46_1.<Command_Patch>b__0()
at System.Threading.Tasks.Task.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at PatchworkLauncher.LaunchManager.<Command_Patch>d__46.MoveNext()
For example:
private static readonly float NearEnemyPenaltyPerSecond = 1f;
The readonly keyword was added in C# 7.2. I was thinking of readonly struct
and ref readonly
.
https://gist.github.com/fireundubh/330f35c7f9a0657a94eadcbd21c3a74f
using Kingmaker.Localization;
using Patchwork;
namespace KingmakerMods.Mods.Settings
{
[ModifiesType]
public class LocalizedStringNew : LocalizedString
{
[ModifiesMember("m_Key", ModificationScope.Nothing)]
private string mod_m_Key;
[ModifiesMember("Key")]
public string mod_Key
{
[ModifiesMember("get_Key", ModificationScope.Nothing)]
get { return this.mod_m_Key; }
[NewMember("set_Key")]
set { this.mod_m_Key = value; }
}
}
}
2018-10-24 00:19:59.660 -07:00 [FTL] Could not resolve a method reference, most likely because it wasn't imported. Details: System.Void KingmakerMods.Mods.Settings.LocalizedStringNew::.ctor()
2018-10-24 00:19:59.671 -07:00 [ERR] An error has occurred,
While trying to: Patch the game
Error type: A system error or some sort of bug. (KeyNotFoundException)
Internal message: The given key was not present in the dictionary.
The given key was not present in the dictionary.
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at Patchwork.Engine.Utility.ReflectHelper.GetEnumValueText[T](T value) in E:\projects\PatchworkPathfinder\Patchwork.Engine\Utility\Reflections and Cecil\ReflectHelper.cs:line 75
at PatchworkLauncher.LaunchManager.Command_Display_Patching_Error(PatchingProcessException ex) in E:\projects\PatchworkPathfinder\PatchworkLauncher\GUI\LaunchManager.cs:line 528
at PatchworkLauncher.LaunchManager.<Command_Patch>d__46.MoveNext() in E:\projects\PatchworkPathfinder\PatchworkLauncher\GUI\LaunchManager.cs:line 340
I don't understand the first log entry. I tried adding a .ctor, but there was no change. I'm guessing the real problem is the implicit operator method.
PatchworkLauncher.PatchingProcessException
HResult=0x80131500
Message=Exception of type 'PatchworkLauncher.PatchingProcessException' was thrown.
Source=PatchworkLauncher
StackTrace:
at PatchworkLauncher.LaunchManager.ApplyInstructions(IEnumerable`1 patchGroups, ProgressObject po) in E:\projects\PatchworkPathfinder\PatchworkLauncher\GUI\LaunchManager.cs:line 715
at PatchworkLauncher.LaunchManager.<>c__DisplayClass46_1.<Command_Patch>b__0() in E:\projects\PatchworkPathfinder\PatchworkLauncher\GUI\LaunchManager.cs:line 335
at System.Threading.Tasks.Task.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
Inner Exception 1:
PatchDeclerationException: The attribute on the method called 'System.String KingmakerMods.Mods.Settings.LocalizedStringNew::op_Implicit(KingmakerMods.Mods.Settings.LocalizedStringNew)' refers to '.ctor', but that member doesn't exist in this context (possibly overload resolution failure).
Yeah, that's what the PatchDeclerationException message seems to be saying.
Sometimes one wants to perform a small patch on a large method body (e.g. remove a code block, replace conditional branch with unconditional, etc.).
Currently it's not very convenient - you either have to duplicate method and write a replacement, which calls original at some point (which is sometimes not possible, if undoing things original method did is tricky), or just duplicate whole body manually using decompiled sources and adjust parts (which is ugly, especially if original method is large).
What would be great is having a syntax like:
[ModifiesMember("foo")]
[PatchesBody]
static void patch_foo(MethodDefinition def) {
// modify 'def' as needed
}
So it would be quite similar to [DuplicatesBody], only instead of discarding annotated function's body it will be executed for patching.
I'm not sure how to distinguish between overloads - it seems that currently it is done via function signature comparison. One option would be to pass a collection of method definitions (all overloads) and let the patch function itself find the correct one.
System.NullReferenceException: Object reference not set to an instance of an object.
at Patchwork.Engine.Utility.CecilOverloadResolver.GetField(TypeDefinition typeDef, String name) in E:\projects\PatchworkPathfinder\Patchwork.Engine\Utility\Reflections and Cecil\CecilOverloadResolver.cs:line 20
at Patchwork.Engine.PatchingManifest.GetPatchedMember[T](TypeDefinition targetType, T yourMemberDef, MemberActionAttribute actionAttribute) in E:\projects\PatchworkPathfinder\Patchwork.Engine\PatchingManifest\PatchingManifest.cs:line 107
at Patchwork.Engine.PatchingManifest.SpecializeMembers[T](SimpleTypeLookup`1 lookup, AssemblyDefinition toTargetAssembly) in E:\projects\PatchworkPathfinder\Patchwork.Engine\PatchingManifest\PatchingManifest.cs:line 135
at Patchwork.Engine.PatchingManifest.Specialize(AssemblyDefinition assemblyDef) in E:\projects\PatchworkPathfinder\Patchwork.Engine\PatchingManifest\PatchingManifest.cs:line 166
at Patchwork.Engine.AssemblyPatcher.PatchManifest(PatchingManifest manifest, IProgressMonitor o) in E:\projects\PatchworkPathfinder\Patchwork.Engine\AssemblyPatcher\AssemblyPatcher.cs:line 300
at PatchworkLauncher.LaunchManager.ApplyInstructions(IEnumerable`1 patchGroups, ProgressObject po) in E:\projects\PatchworkPathfinder\PatchworkLauncher\GUI\LaunchManager.cs:line 747
at PatchworkLauncher.LaunchManager.<>c__DisplayClass46_1.<Command_Patch>b__0() in E:\projects\PatchworkPathfinder\PatchworkLauncher\GUI\LaunchManager.cs:line 369
at System.Threading.Tasks.Task.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at PatchworkLauncher.LaunchManager.<Command_Patch>d__46.MoveNext() in E:\projects\PatchworkPathfinder\PatchworkLauncher\GUI\LaunchManager.cs:line 369
namespace KingmakerMods.Mods.Game.Configurables.GoldPoints
{
[ModifiesType]
public class BuildingItemNew : Kingmaker.UI.Settlement.BuildingItem
{
[ModifiesType("Kingmaker.UI.Settlement.BuildingItem.RequiredStaff")]
private class RequiredStaffNew
{
[ModifiesMember("Slots")]
public Image source_Slots;
}
}
}
[FTL] Encountered a feature that isn't supported. Details: MetadataType not supported: RequiredModifier
What does this mean?
This page says ELEMENT_TYPE_CMOD_REQD
is a C language required modifier, but I don't see anything like that in the source method I'm trying to duplicate and modify.
I have the following new type to be injected via Patchwork:
[NewType]
public class MyData
{
[XmlArrayItem(ElementName = "DataName", Type = typeof(string))]
public List DataElements { get; set; }
}
However, when serializing or deserializing, the XmlArrayItem appears to be missing and the elements just get serialized as "string" instead of "DataName". The only reason I can think this might be so is that the attribute isn't ending up set on the injected method.
Currently, when you try to patch multiple assemblies (with multiple PatchInfos), you get an error like this:
error CS0579: Duplicate 'PatchAssembly' attribute
Seems like a better approach would be to have GetTargetFile return a FileInfo collection.
When a ModifyMethod targets a method that isn't present in the target assembly (possibly because its signature is slightly different) it throws a not very helpful Null Reference Exception that doesn't tell you what method is causing the problem.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.