Giter Site home page Giter Site logo

patchwork's Issues

DuplicatesBody sometimes picks up patched method body

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?

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.

Patchwork Launcher Win11 doesnt Work

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.

TryConstructAttribute throws NullReferenceException while updating types

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.

Traced to

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;
		}

Exception details

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()

Support for multiple game executables.

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.

Here's what I ended up with:
patchwork

Patch fails with "Internal message: The given key was not present in the dictionary."

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.

Patchwork problem with Generic/non-Generic method pair in source object

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.

Feature Request: Keep the launcher open after patching

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.

ArgumentOutOfRangeException while patching with multiple mods targeting different assemblies

  1. I created a second patch DLL that patches a different second assembly.
  2. I added that DLL in the GUI for a total of two patch DLLs.
  3. When I execute a Test Run, patching completes successfully for the first DLL, but when patching tries to start for the second, the following exception occurs.

If I remove one of the two DLLs, patching completes successfully.

Exception details

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()

Screenshot

2018-11-28 17_54_08

How do you modify readonly fields?

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.

Could not resolve a method reference, most likely because it wasn't imported

Source

https://gist.github.com/fireundubh/330f35c7f9a0657a94eadcbd21c3a74f

Patch

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; }
		}
	}
}

Exception Details

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.

Add support for arbitrary IL patching

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.

Improve error message when nested type isn't matched

Exception Details

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

Sample Code

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;
		}
	}
}

Patchwork possibly losing method attributes that are non-patchwork related?

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.

Feature Request: Support patching multiple assemblies

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.