{"id":13663407,"url":"https://github.com/GregRos/Patchwork","last_synced_at":"2025-04-25T17:30:41.626Z","repository":{"id":36020888,"uuid":"40316092","full_name":"GregRos/Patchwork","owner":"GregRos","description":"A library for modifying .NET assemblies.","archived":false,"fork":false,"pushed_at":"2018-11-23T20:06:53.000Z","size":1692,"stargazers_count":67,"open_issues_count":8,"forks_count":10,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-04-08T11:38:15.759Z","etag":null,"topics":["csharp","dotnet","game","modding-tools","unity"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GregRos.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-08-06T16:44:03.000Z","updated_at":"2024-12-27T07:10:33.000Z","dependencies_parsed_at":"2022-08-17T22:55:53.075Z","dependency_job_id":null,"html_url":"https://github.com/GregRos/Patchwork","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GregRos%2FPatchwork","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GregRos%2FPatchwork/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GregRos%2FPatchwork/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GregRos%2FPatchwork/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GregRos","download_url":"https://codeload.github.com/GregRos/Patchwork/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250779138,"owners_count":21485889,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","dotnet","game","modding-tools","unity"],"created_at":"2024-08-02T05:02:26.802Z","updated_at":"2025-04-25T17:30:40.757Z","avatar_url":"https://github.com/GregRos.png","language":"C#","funding_links":[],"categories":["C\\#"],"sub_categories":[],"readme":"# Patchwork\n\n\u003cspan style=\"font-size:18\"\u003e[MIT License](http://opensource.org/licenses/MIT) |  [Latest Version: 0.9.1](https://github.com/GregRos/Patchwork/releases/latest)\u003c/span\u003e\n\n[![Build status](https://ci.appveyor.com/api/projects/status/o8dg9qosrlcddo3l/branch/master?svg=true)](https://ci.appveyor.com/project/GregRos/patchwork/branch/master)\n\n----\n\n## User's Guide\nPatchwork is a program that allows you to mod certain games using mod files made by others.\n\nYou will need to download Patchwork from a mod site or a similar source, because the version that can be downloaded from this site is only suitable for developers.\n\n### Requirements\nPatchwork is a Mono/.NET application and so needs the .NET Framework or Mono to run.\n\n* **Windows:** [.NET Framework 4.5+](https://www.microsoft.com/en-us/download/details.aspx?id=30653) \n* **Linux and Mac:** [Mono 4.2.1.102+](http://www.mono-project.com/download/)\n\n### Instructions\n\nUsing the program is straight-forward:\n\n1. Extract it into an **empty** folder.\n\n2. Launch the program (`PatchworkLauncher.exe`)\n   \n   **Note:** On Linux, you may need to open the program using  `mono` explicitly (see instructions on running Mono applications in your distribution).\n\n3. Specify your game folder in the dialog box or type it in the textbox.\n\n   **Note:** The dialog box will not display hidden files or folders.\n\n4. Go to the *Active Mods* menu and add the mod file(s) (usually ending with `.pw.dll`) to the list of mods, checking those you want enabled.\n\n   **Note:** Mod files so chosen will normally be copied to the `Mods` folder.\n\n5. Use *Launch with Mods* and *Launch without Mods* to start the game.\n\n## Developer's Guide\n\nIf you have any ideas or new direction for this project, please contribute. Right now I've lost interest in it, but I really think it can be improved and I'll probably start working on it again.\n\n---\n\n**Patchwork** is a framework for integrating your own code into existing .NET assemblies (\"patching\" them). It allows you to edit, create, or replace things such as types, properties, and methods in a simple, straight-forward, and declarative way, using attributes.\n\nThe framework lets you basically rewrite entire programs, such as games, according to your whims (as long as they're written in a .NET language of course). Little in the code is beyond your control, and you can write it all using the same tools as the original developers. \n\nYou write code in C# or another language, and that code is injected into the target assembly according to your patching declarations. It is minimally transformed, fixing references to such things as types and methods, so that it remains valid at the point of injection.\n\nThe framework was written with game modding in mind, but can be used for any purpose.\n\nThe framework is mostly documented, including the non-public members.\n\n### Moddable Games\nLike I said above, the library was written with game modding in mind. In general, you can mod two kinds of games with it: i\n#### .NET/XNA\nGames that run on .NET/XNA. You can mod pretty much anything in this case. However, there aren't many popular XNA titles.\n\n#### Unity/.NET\nGames that run on Unity and use .NET for their game logic (mainly C#, but some also use other languages). \n\nLuckily, the majority of popular Unity titles do primarily use .NET.\n\nModding in this case is somewhat more limited, as you can only mod the game logic in the scripts, but from experience, you still have vastly more power than typical official modding tools would give you.\n\n### Components \nThe framework consists of several separate components. \n\n#### PatchworkLauncher (PWL)\nThis is the end-user patching GUI. Users use this program to apply your modifications to their games or applications. It is most convenient to download it from the [Releases](https://github.com/GregRos/Patchwork/releases) section.\n\nYou also use this program to test your patch assembly for yourself. \n\n##### OpenAssemblyCreator (OAC)\nThis is a command-line tool packaged with PWL. It allows you to create an open assembly to reference in your patch assembly. More on this later.\n\n#### Patchwork.Attributes (PWA)\nThe `Patchwork.Attributes` assembly is meant to be referenced by your patch assembly, and is compiled with framework version 2.0 to improve compatibility. It contains the attributes that serve as patching instructions. It has no dependencies.\n\nYou can conveniently get this package from [NuGet: Patchwork.Attributes](https://www.nuget.org/packages/Patchwork.Attributes/) and reference it in your patch assembly.\n\n#### Patchwork.Engine\nThis is the library that actually does the patching. It can be used separately from the GUI. However, you generally wouldn't want to do so unless you wanted to write a new front-end, some kind of script, or... had some other reason.\n\nNaturally, it's packaged with the launcher.\n\n### Getting Started\nFirst, get the latest PWL release from the releases section. \n\nUnless someone else already wrote it, you'll need to write an `AppInfo.dll` file for the game you want to patch. See the section on the launcher for more information.\n\nNext, create a new project (preferably with a name ending with `.pw` for reasons explained later). \n\nReference the `Patchwork.Attributes` assembly from that project.\n\nSince you probably don't want to copy files around manually, you should take advantage of the `DontCopyFiles` option in the `Preferences` section of the `preferences.pw.xml` file. It lets you add files to your mod list without copying them to a mods folder, so you can just add the result of your build directly.\n\nThat's... it. You're ready to go. Good luck out there. \n\nBy the way, patching using the launcher creates a dependency on `Patchwork.Attributes` in the target (patched) assembly.\n\n### Overview\nThere are a few stages to writing a patch.\n\n#### Finding What to Patch\nBefore you start writing your patch assembly, you need to find what you want to patch. This involves decompiling the target assembly. See *Recommended Decompilers* below for more information. \n\nAlso, take note of the target framework version of the assembly, as for the most reliable results you'd want your patch to be built against the same framework version.\n\n#### Creating an Open Assembly\nYou also need to have an \"open\" version of the assembly you want to patch. \"Open\" here means that all of its members are public and non-sealed. \n\nTo see why this is required, imagine you have a class like:\n\n\tclass Player {\n\t\tprivate int _hitpoints;\n\t\t\n\t\tpublic void GetHit() {\n\t\t\t_hitpoints--;\n\t\t}\n\t}\n\nAnd you want to overwrite the `GetHit` method to perform `_hitpoints++` instead. \n\nThe problem is that `_hitpoints` can only be accessed from inside the class `Player`. But you have to write the code `_hitpoints++` in your own assembly. The framework will inject it into the correct spot, but your C# compiler doesn't know that and won't let you access the member.\n\nChanging all members to public allows you to access the member. However, it can cause problems too. You might make a mistake and illegally refer to a non-public member, for example.\n\nOn the brighter side, the `PEVerify` tool (which executed automatically) will catch any accessibility issues and warn you about them.\n\nYou create an open assembly using the command-line tool `OpenAssemblyCreator.exe`, also called OAC. Using it is straight-forward. \n#### Creating the Patch Assembly\nYou patch an existing target assembly by writing a *patch assembly* (probably with the target assembly referenced), and load it as input to patch a \"target\" assembly. This assembly contains attributes that are used as patching instructions. \n\nYou need to specify the `PatchAssembly` attribute on any such patch assembly.\n\n##### Writing It\nPatch assemblies consist of patch types, which are new types or just sets of modifications to an existing type. Here is a simple example:\n\n\t[ModifiesType] //means the class modifies another class\n\tclass AttackMod : Attack {\n\t\t//note that by default it modifies its parent class, if any, \n\t\t//so this class modifies Attack.\n\t\t\n\t\t[NewMember] //this means a new method will be injected into the type\n\t\tpublic Hit() {\n\t\t\t//whatever you want to do\n\t\t}\n\t\t\n\t\t[ModifiesMember(\"ExistingMethod\")]\n\t\tpublic void ExistingMethodRevised() {\n\t\t\t//your instructions will replace those of ExistingMethod,\n\t\t\t//as defined in the modified type\n\t\t}\n\t}\n\t\n\t[NewType]\n\tpublic class MyNewType {\n\t\t//No need to specify attributes here.\n\t\tprivate readonly string _myField;\n\t\t\n\t\tpublic int MyMethod(int x) { ... }\n\t}\nYou can add any members to the type, and attach the right attributes to them, depending on whether you want them to modify existing members or create new ones. (If you create a new type, there are no existing members to modify, of course).\n\nCompiler-generated members are imported as new members by default, even if they don't have a patching attribute.\n\n##### Format and Additional Info\nThe recommended extension for patch assemblies is `pw.dll`.\n\nIn order for a patch assembly to work with the Patchwork launcher, it must define a class with the following requirements.\n\n1. It implements `Patchwork.Attributes.IPatchInfo`\n2. It is decorated with `Patchwork.Attributes.PatchInfoAttribute`.\n3. It has a default constructor.\n\nThere must be only one such class in the assembly.\n\nThe class should not contain any references to any types not found in the GAC or in `Patchwork.Attributes`.\n\nIt is instantiated in a separate AppDomain from the rest of the application, and this AppDomain is unloaded if the user removes it from the patch list.\n\nThe `PatchInfo` is needed to tell the launcher what file to patch. The class can find the file based on the operating system and other information.\n\n#### Patching\nThe patch is mostly viewed as data by the patchwork launcher. To patch another assembly with your patch assembly, you must load the `pw.dll` file into the launcher and then start the game.\n\nThat's it.\n\n\n### Patchwork Launcher\nEach launcher executable is meant to work with one game. To work with a game, someone must write an `AppInfo.dll` assembly (as described below) and put it in the launcher directory. The launcher loads it on startup and uses it to get information about the game. The launcher should be distributed with this file.\n\nThe launcher allows users to manage patches for the chosen game, as well as change the order in which they are applied. It keeps the original game files on disk and switches them with modded files when the user launches the game. It only patches the files when necessary. It stays in the background, and once the game is exited, the launcher switches to the original files once more. \n\nOnce it starts up, the launcher also checks the state of the files and fixes them if necessary, in case it was terminated unexpectedly.\n\nThe launcher works on Mono and is written in Windows Forms for that purpose.\n\n#### AppInfo.dll\nThis file is required for the launcher to work correctly with a game. It's an assembly (the name doesn't matter) containing a type that:\n\n1. Inherits from `Patchwork.Attributes.AppInfoFactory`.\n2. Is decorated with `Patchwork.Attributes.AppInfoFactoryAttribute`.\n3. Has a default constructor.\n\nThis type has a `CreateInfo` method that returns an `Pathcwork.Attributes.AppInfo` object which provides information about the game.\n\nThere must be only one such class in the assembly.\n\nThis class is instantiated during runtime, in the same AppDomain as the original application. The launcher can't start if the file is invalid or not found.\n\nIf this file is not found, an error message is generated and the launcher doesn't work properly.\n\n### Available Attributes\nThese attributes are located in the `Patchwork.Attributes` namespace. Note that this isn't necessarily a full list.\n\n#### Note about attribute constructors\nAttributes that require types as parameters invariably have an `object` parameter instead of a `Type` parameter.\n\nThis is a necessary workaround. You still use `typeof(T)` to specify the type.\n\n#### PatchingAssembly\nYou *must* add this attribute to your assembly (using `[assembly: PatchingAssembly]`) for it to be recognized as an assembly that contains patching types.\n\n#### ModifiesType(name)\nSays that your type modifies another type in the game. Allows you to use `ModifiesMember` within that type.\n\nYou can specify the full name of the type you want to modify, or let PW infer it.\n\n#### ReplacesType(name)\nAlternative version of the above attribute. Removes all the members of the type, overwriting it with your own members. Currently implemented only on enums. `ModifiesMember` attributes are invalid, since they have no meaning.\n\n#### ModifiesMember(name,scope)\nModifies the member, such as its accessibility, body, and maybe other things. scope controls the scope of the modification.\n\n#### ModifiesAccessibility(name)\nRestricted form of the last attribute. Modifies just the accessibility to be identical to your member. \n\nProvided for convenience.\n\n#### NewMember(altName)\nIntroduce this as a new member to the patched type. If you specify `altName`, the member will be introduced under this name instead.\n\nIf the member collides with an existing member, its name will be suffixed with `_$pw$_RANDOM`, e.g. `_$pw$_dfRff`.  A warning will be emitted.\n\n#### DuplicatesBody(methodName, declaringType)\nPut this on a method marked with NewMember or ModifiesMember to insert the body of another method into it. Optionally, you can provide the type that declares the method; otherwise, it defaults to the type being modified.\nYou can use it to call original members in the modified type, as it takes the body from the original assembly.\n\n#### NewType(altName, altNamespace)\nPut this attribute on a type to denote it is a new type that will be introduced into the assembly.\n\nThe name of the type will normally be the same as it is in your assembly, including namespaces and so forth. However, `altName` and `altNamespace` allow you to specify an alternative name/namespace.\n\nIn case of a collision, the type name will be suffixed with `_$pw$_RANDOM`, e.g. `_$pw$_dffERr`. A warning will be emitted.\n\nYou can create any kind of type you like, whether interface, struct, or class. You can have inheritance, generic type parameters, put constraints on those parameters, etc. Anything goes.\n\nYou don't need to use creation attributes on any of your type's members, except for other types. They will be considered to have the `NewMember` attribute. \n\nYou can put `ModifiesType` on a nested type inside a `NewType`, but not `ModifiesMember`. \n\n\n#### RemoveThisMember\n\nRemoves a member of the same name from the modified type. Added for the sake of completeness.\n\nAfter using it, it's wise to mark the member using the `[Obsolete]` attribute so you don't invoke it by accident.\n\nPW will not check if this action causes an error, but errors may still come up in the patching process later on.\n\nIt is not possible to remove types.\n\n#### DisablePatching\nDisables the patching of this element and all child elements, including nested types. \n\nModifications will not be performed, and new types will not be created.\n\n#### MemberAlias(memberName, declaringType, aliasCallMode)\nThis attribute lets you create an alias for another member. When Patchwork encounters a reference to the alias member in your code, it will replace that reference with the aliased member.\n\nIt is useful for making explicit calls to things such as base class constructors. If `aliasCallMode == AliasCallMode.NonVirtual`, a call to the member is translated to a non-virtual call, bypassing any overrides. This will allow you to inject `base.OverriddenMethod()` sorts of calls into the methods you modify.\n\n#### PatchworkDebugRegister(memberName, declaringType)\nThis is a special attribute for debugging purposes.  You can specify a static string member that will be used as a debug register for the current method. It will be updated with information about which line number is going to be executed next. It lets you find the line number at which an exception was thrown (or something else happened), when the exception does not contain this information. \n\nFor example, the register can contain the following after an exception is thrown and is caught in the same method:\n\n\t10 ⇒ 11 ⇒ 45 ⇒ 46 ⇒ 47 ⇒ 251 ⇒ 252\n\nIf the catch clause was at line 251, then line 47 is the one that threw the exception.\n\nThis is a hack, but it can be quite useful.\n\n#### ToggleFieldAttributes(fieldAttributes)\nThis custom attribute toggles (XORs) the intrinsic deceleration attributes of the patched field with the input attributes. It must be used with an action attribute, such as `ModifiesMember`.\n\nThis attribute allows you to change accessibility, as well as more arcane things. Using it incautiously can cause runtime errors.\n\n#### ToggleMethodAttributes(methodAttributes)\nThis custom attribute toggles (XORs) the intrinsic deceleration attributes of the patched method with the input attributes. It must be used with an action attribute, such as `ModifiesMember`.\n\nThis attribute allows you to change accessibility, add/remove the `sealed` qualifier, and perform other, more arcane tasks. Using it incautiously can cause runtime errors.\n\n### Naming Conventions\nIt is best practice to follow certain naming conventions when writing your patch assembly.\n\nYou should prefix each code element according to the action the framework is expected to perform on it. That way, you will be able to tell what sort of member it is just by glancing at the name, and your code will be more readable to others.\n\n| Function                                                   | Form            | Related Attribute            |\n|---------------------------------------------------------   |-----------------|------------------------------|\n| Modification of `Name` type/member from the original assembly | `mod_Name`        | `ModifiesType`, `ModifiesMember` |\n| Duplicate of `Name` in the modified type                  | `orig_Name`       | `DuplicatesBody`               |\n| Duplicate of `Name` in type `Type` in the original assembly | `orig_Type_Name` | `DuplicatesBody`               |\n| Alias of `Name` in type `Type`                              | `alias_Type_Name` | `MemberAlias`                  |\n| New type or member                                      | (none)          | `NewMember`, `NewType`           \n\nFor instance constructors, use the name `ctor` and for static ones use `cctor`. \n\n(none) means that you should not prefix the name with anything.\n\n\n\n### Specific Issues\n\n#### About Overloading\nWhen you put an attribute on a code element, the framework will usually use that element's name (or an alternative name you supply) and, in the case of methods and properties, their parameters, to find what to modify.\n\nTo modify one of several overloaded methods, you just need to duplicate that method's parameter types exactly.\n\nNote that return types of existing methods cannot be modified.\n\n#### Modifying/Creating Properties\nNote that to modify a property's `get` and `set` accessors, you need to put `ModifiesMember` *on the accessor you want to modify*, not on the property deceleration. The accessors are actually methods, and it's those the framework modifies.\n\nHowever, you can choose to put `NewMember` on the property, in which case the accessors will be created automatically.\n\nThis also applies to the property's accessibility. In the IL, only get/set methods have accessibility, so if you want to modify it you have to put the attribute on the accessor, possibly on both.\n\nYou might need to use the explicit name of the property accessor to modify it (if your property is named differently). Accessor names are normally `get_{Property}` and `set_{Property}`.\n\nPretty much *the only* time you'd want to use `ModifiesMember` attribute on a property itself is when you want to create a brand new accessor for the property. In this case, the property data must be modified. You'll still put the `NewMember` attribute on the new accessor.  \n\n#### Modifying Constructors\nYou can't create constructors for existing types, but you can modify existing ones. Constructors are just methods called `.ctor`. You just need to duplicate their signature in a normal method, and change the modified member name in the attribute. Every object has a default `.ctor`.\n\nStatic constructors are called `.cctor`. Not all types have static constructors.\n\nNote that constructors also contain the type's initializers, so you may need to copy those or the class might not work correctly. \n\nInstance constructors normally contain explicit calls to a base class constructor (e.g. `base::.ctor()`). It is best practice to add this call. This can be achieved by using the `MemberAlias` attribute. For example:\n\n\t[MemberAlias(\".ctor\", typeof(object))]\n\tprivate void object_ctor() {\n\t\t//this is an alias for object::.ctor()\n\t}\n\t\n\t[ModifiesMember(\".ctor\")]\n\tpublic void CtorNew() {\n\t\tobject_ctor();\n\t\tIEModOptions.LoadFromPrefs();\n\t}\n\nStatic constructors do not contain such a call.\n\n#### Nested Types\n\nYou can have your nested types modify other types, or you can modify other nested types, without regard to the nesting level. The location of your nested type doesn't matter, and using `ModifiesType` behaves the same way. \n\nTo modify a type by name (rather than having PW infer it), you have to give the *full name* of the type, without regard to where the attribute appears.\n\nIn the IL and in Mono.Cecil, as well as in this framework, you use `/` to indicate nesting. E.g. `Namespace.ContainerClass/Nested/NestedNested`.\n\nYou can also have a new nested type inside a modification to an existing type. In this case, the nested type will be moved to the modified type.\n\n#### Modifying Explicitly Implemented Methods\nThese are regular methods with different actual names. The names are `[INTERFACE_FULL_NAME].Method`. For example, if `IEnumerable\u003cT\u003e.GetEnumerator()` were explicitly implemented, you'd set the member name to:\n\n\t\tSystem.Collections.Generic.IEnumerable\u003cT\u003e.GetEnumerator\n\nThe dots are actually part of the name of the method, just like the dot in `.ctor` is. IL doesn't follow C# naming rules.\n\n### Patching History\nThe launcher embeds various attributes in the target assembly that let you see what Patchwork did to it. These are called history attributes. The following are embedded.\n\nAlso, the patching attributes you use also get embedded, except for `PatchAssembly`.\n\n#### PatchingHistoryAttribute\nAbstract parent of all history attributes.\n\n#### PatchedByAssemblyAttribute\nContains information about the patch assembly, the original assembly (before patching was performed), and the Patchwork assembly that performed patching.\n\n#### PatchedByMemberAttribute\nIndicates the member in the patch assembly that contained the patching instruction to patch this member.\n\n#### PatchedByTypeAttribute\nIndicates the type in the patch assembly that contained the patching instruction to patch this type.\n\n### Limitations\nIn this section I'll list the limitations of the library, in terms of the code that it can deal with at this stage, and what it *can't* allow you to do. This section will be updated as more features get worked in.\n\n#### Assemblies\n1. Multi-module assemblies won't work properly (either as patches or patch targets). Note that few IDEs (if any) can naturally produce such assemblies, though they can be the result of tools such as ILMerge.\n2. Inter-dependencies between multiple patch assemblies haven't been tested.\n\n#### Members\n2. You can't add new constructors or finalizers to existing types.\n3. Existing declarations can only be modified in limited ways. For example, you can't un-seal a sealed class, change type parameters and their constraints, etc. New members can still be sealed or unsealed, etc, as you prefer. \n3. Field initializers don't work in modifying types, except for const fields. This is unlikely to be fixed anytime soon, as it requires pretty tricky IL injection.\n\n#### Language Features\n1. `unsafe` context features, like pointers and pinned variables, probably won't work.\n2. Various exotic and undocumented (in C#) constructs cannot be used, such as `__arglist`.\n3. Exotic features such as optional and required signature modifiers (OptionalModifier, aka `modopt`; RequiredModifier, aka `modreq`), isn't supported\n\n#### Other .NET Languages\nThis library is for transforming IL, not transforming source code, so it doesn't actually care what language you write in. As long as you put attributes on things that are recognizable in the IL as properties, methods, and classes, it will probably work correctly.\n\nThat said, you could experience more problems if you write in languages other than C#, simply because they can be compiled to very different IL, and the different input could reveal flaws I never encountered during testing. \n\nHowever, don't take this as me discouraging you from using other languages. \n\n\n\n### Recommended Decompilers\nI've tried a number of decompilers. \n\n1. **[Telerik JustDecompile](http://www.telerik.com/download/justdecompile)**: Probably the best overall decompiler I've tested. It has great search functions, can produce IL as well as C#, good decompilation ability, and has a great interface. Decompilation isn't perfect, as it can't decompile such things as iterators. Tends to handle errors fairly decently.\n2. [**ILSpy:**](http://ilspy.net/) This one generates the best source code *by far* from the decompilers I've tested. It can decompile iterators, lambdas, you name it. Unfortunately, it has no search function that deserves the name and handles errors very badly, even when set to IL. The interface is also inconvenient.\n3. [**dotPeek**](https://www.jetbrains.com/decompiler): It has a good interface and decent search, with the very helpful ability of finding related (e.g. derived) types, but isn't very good at decompiling compiler-generated code. It can't even handle things like auto-properties.\n\n### Dependencies\n\n1. [Mono.Cecil](http://www.mono-project.com/docs/tools+libraries/libraries/Mono.Cecil/), without which none of this would have been possible.\n2. [Serilog](http://serilog.net/), used for logging.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGregRos%2FPatchwork","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FGregRos%2FPatchwork","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGregRos%2FPatchwork/lists"}