DFU Injector

Show off your mod creations or just a work in progress.
User avatar
ShortBeard
Posts: 45
Joined: Tue Aug 06, 2019 1:10 pm

DFU Injector

Post by ShortBeard »

Hi everyone,
I've attempted to create a solution to be able to replace methods at run time in DFU. It's not elegant, but it's a solution.

Known issues:
  • Doesn't work on Windows 7 - Fixed

Preface:

This is very much a work in progress. You should expect things not to work properly. This has the potential to mess everything and anything up. Use this at your own risk. Please see the post below with the header "risks" for more information on that. Please also note, this has only been tested on 64 bit versions of Windows.

What DFU Injector?:
In short, DFU Injector will allow modders to overwrite existing in-game methods. Though reflection can be used in .dfmods to access private members, method-changing (as far as I know) has been somewhat untouchable. Note, that this type of mod creation will create mods in an entirely different way, and will use a modders custom DLL files instead of .dfmod files.

How it works:
When Daggerfall Unity launches, a piece of software called "Doorstop" allows code to run right before the "core" of the DFU code runs. This allows arbitrary code execution, and an ability to replace methods at run time using Harmony. I'm not sure if there is a better method, but this is the solution I have come up with. DFU Injector acts as a middle-man between Doorstop and Harmony-based mods.

Should I use this?:
Probably not. Please use the regular .dfmod where possible, as they extend on existing and working functionality of the game. DFU Injector has the potential to break everything if you don't know what you're doing. You also run the risk that your mod may mess up core-functionality after a DFU update. Use this at your own risk. If your game breaks, it's up to you to fix it. If your save file stops working, that's on you. If a mod that someone else has given you breaks, talk to the person who made the mod. This is only intended to provide a platform for more mod customization, what happens beyond that is out of my control.

Requirements for mod developers:
• Decent knowledge of C#
• You know how to compile a DLL
• You know how to reference and include other DLLs
• A basic understanding of XML

Beyond providing DFU Injector and a very basic tutorial so that you know how to use it, it's up to the modders to figure out how to use Harmony to get the results they want. I can provide some outlining, and I will help where I can, but I can't write your whacky mod for you. I've tried to make what is a somewhat complicated process as simple as I can. I am far from an expert, and I am sure there are a million better ways to do it.

Technical requirements:
For this your mod to function correctly with DFU Injector, there's a few things you will need:
• DFU_Injector.dll + Doorstop & Harmony (Provided in a .rar file attached to this post)
• Naming conventions in your mods code so that DFU Injector knows how to work with it (more on this below in the tutorial)
• An XML file that accompanies your DLL file (Contains some basic data about the mod, author, version, etc.) An example XML file is also attached.

-See next post for basic tutorial-
Attachments
DFU_Harmony_Tutorial.rar
(1 KiB) Downloaded 146 times
DFU_Injector.rar
(276.87 KiB) Downloaded 151 times
TestMod_XML.rar
(228 Bytes) Downloaded 136 times
Last edited by ShortBeard on Sat Oct 10, 2020 2:33 pm, edited 15 times in total.

User avatar
ShortBeard
Posts: 45
Joined: Tue Aug 06, 2019 1:10 pm

Re: DFU Injector

Post by ShortBeard »

Tutorial:

In this tutorial, we will replace the method that would normally play a sound when the player draws his weapon. We will instead replace it with code that teleports the player into the sky. It might be best to have a save game in a town/outdoors to make sure this is working properly for testing purposes. Please note that while the tutorial harmony code works, it won't always be so simple depending on the complexity. This is just intended to get your foot in the door. Check out the Harmony documentation for more complex designs.

• Unzip the contents of "DFU_Injector.rar" into the same location as your DaggerfallUnity.exe
• Make a new folder in your existing Mods folder and name it "injector_mods" without quotes.
• Make a new folder in injector_mods, and give it a name that is suitable for your mod. "TestMod" is fine for this tutorial. The name doesn't matter, it's just used by DFU_Injector for organizational purposes.
• Start a new project in your preferred IDE (I'm using Visual Studio 2019) in preparation to create a DLL file.
• Paste in the example code found inside DFU_Harmony_Tutorial.rar attached to the original post. Please note that the Namespace, Class and DFUReplace_Start() method naming conventions must not be altered. "MyReplacementMethod" can be named anything you like, just don't change the names of the rest.
• Add a reference to 0Harmony.dll to your solution. Also add a reference to Assembly-Csharp.dll (this can be found in the DFU directory DaggerfallUnity_Data\Managed)
• If you intend to use any Unity functionality such as transforming positions, etc. - you will also need to add a reference to UnityEngine.CoreModule.dll as I have done in this tutorial.
• Build the DLL. For more specific information about what is going on, read the comments in the tutorial code.
• Place the newly built DLL in the "TestMod" directory created earlier. Also place the XML file in this directory. Make sure that the XML node "FileName" contains the exact name of your DLL including the extension (for example, <FileName>TestMod.dll</FileName>)
• Run DaggerfallUnity.exe as normal, load your saved game, and if everything worked then the player should be teleported into the sky when drawing your weapon instead of it playing a weapon drawing sound.

Issues/Errors:
If it's not working as expected, or if DaggerfallUnity doesn't launch, check the injector_mods folder for an error log file. If no error log is generated here, it's potentially an issue with doorstop running. Your version of winhttp.dll depends on your operating system. You can try re-building winhttp.dll yourself from source (see DoorStop github's page)
Last edited by ShortBeard on Thu Oct 15, 2020 2:14 am, edited 5 times in total.

User avatar
ShortBeard
Posts: 45
Joined: Tue Aug 06, 2019 1:10 pm

Re: DFU Injector

Post by ShortBeard »

Other notes:
The XML file can contain multiple <FileName> nodes, and will attempt to load all of the DLLs you list in there, if your mod uses multiple. However, please also not that this does not currently contain any kind of priority organization. If two mods attempt to overwrite the same method, it's luck of the draw.


Risks:

As previously mentioned, one of the most obvious risks is you replace a method that DFU itself changes later (such as an update or bug fix) and can mess up some intended functionality. The other big risk is that a DLL provided by a modder can run arbitrary code on your system and leaves you vulnerable to potential malware, etc. If you don't trust a mod, don't use it. If you want to investigate a mod, you can always use ILSpy to inspect the assembly (see references below for link)

Conclusion:
If you have any questions, concerns, requests or if you think this can be done in a better way, please post it in this thread! Perhaps one day the DLL's can just be placed in regular .dfmods and loaded that way instead of needing to be in a separate director. I think the ideal circumstance would be to be able to have .CS source files sitting in there which are compiled by DFU_Injector at run time, to make adjustments more accessible.


References:
Doorstop - https://github.com/NeighTools/UnityDoorstop/releases
Harmony - https://github.com/pardeike/Harmony
DFU Injector Source - https://github.com/ShortBeard/DFU_Injector
ILSpy - https://github.com/icsharpcode/ILSpy
Last edited by ShortBeard on Sat Oct 10, 2020 6:38 am, edited 4 times in total.

User avatar
haloterm
Posts: 391
Joined: Sat Feb 16, 2019 5:21 am

Re: DFU Injector

Post by haloterm »

ShortBeard wrote: Fri Oct 09, 2020 12:16 pm What DFU Injector?:
In short, DFU Injector will allow modders to overwrite existing in-game methods. Though reflection can be used in .dfmods to access private members, method-changing (as far as I know) has been somewhat untouchable. Note, that this type of mod creation will create mods in an entirely different way, and will use a modders custom DLL files instead of .dfmod files.
Very interesting. Do I understand it correctly that it somehow replaces DFU's own code in runtime with code written by a modder and provided in the .dll? Are there safety measures in place to avoid malware injection?

From a player's point of view, what types of mods could modders do with your method that could not be done with a .dfmod, which already allows C# programming?

Also, this is Windows-only, correct?

User avatar
ShortBeard
Posts: 45
Joined: Tue Aug 06, 2019 1:10 pm

Re: DFU Injector

Post by ShortBeard »

haloterm wrote: Fri Oct 09, 2020 12:24 pm
ShortBeard wrote: Fri Oct 09, 2020 12:16 pm What DFU Injector?:
In short, DFU Injector will allow modders to overwrite existing in-game methods. Though reflection can be used in .dfmods to access private members, method-changing (as far as I know) has been somewhat untouchable. Note, that this type of mod creation will create mods in an entirely different way, and will use a modders custom DLL files instead of .dfmod files.
Very interesting. Do I understand it correctly that it somehow replaces DFU's own code in runtime with code written by a modder and provided in the .dll? Are there safety measures in place to avoid malware injection?

From a player's point of view, what types of mods could modders do with your method that could not be done with a .dfmod, which already allows C# programming?

Also, this is Windows-only, correct?
Thanks for the interest :)

And yes, you understand that correctly. In a nut shell, it will swap out the targeted methods before the core functionality of DFU actually launches, so that when the game starts up it's none the wiser. There are no safety measures in place to prevent malware injection, this is purely a "use at your own risk" situation. I've just finished adding a disclaimer to my last post, and I would highly recommend people use ILSpy to inspect a DLL assembly if they want to be certain that it's safe.

As for "what can be done?", that's really up to the modding community. Ralzar mentioned that there has been a hold-back on creating a mod that would help create human mobs that don't scale to player level. Maybe he can use this to help with that.

And I have only tested this on windows (I will update this in the original post), so for now the answer to that is yes. I know that OSX uses a different directory path that DFU_Injector currently doesn't support. I can't say for Linux.

User avatar
Magicono43
Posts: 1141
Joined: Tue Nov 06, 2018 7:06 am

Re: DFU Injector

Post by Magicono43 »

So, I know that you have mostly put this up here for those to do with as they wish/are able. But after figuring out how to get it to work a few days ago with you at the tutorial level at least. I tried my own method replacement and it appears to be working at the very first stages (IE it's not crashing), but the method replacement itself does not seem to be working this time. However, it is producing a proper error log for this, so after trying to figure it out myself with not much luck, I figured I would ask and see if you can see anything that seems wrong here or something.

At first I thought that maybe it had something to do with the original method having the private access modifier, but when I looked at the Harmony's "Hello World" example, the original method they used was also private, so I figure that would not be the issue most likely. The only thing I can really think of is that this method while void, does have some two arguments, unlike your example and the "Hello World" examples, and I can't seem to find anything in searching about this "problem" specifically, so i'm not really sure what it wants/needs to not throw this "NullReferenceException" when trying to replace the original method.

Here is the related files, error log, and script used included:
Test DLL Inject Mod.rar
(4.85 KiB) Downloaded 143 times
Edit 1: Oh yeah, and if you try and test it, what SHOULD be happening if the method is replaced correctly is when you look at your armor values on the paperdoll and character sheet, they should read something other than a number, in this case "test" and some other text string garbage.

User avatar
ShortBeard
Posts: 45
Joined: Tue Aug 06, 2019 1:10 pm

Re: DFU Injector

Post by ShortBeard »

Magicono43 wrote: Sun Oct 11, 2020 1:42 pm So, I know that you have mostly put this up here for those to do with as they wish/are able. But after figuring out how to get it to work a few days ago with you at the tutorial level at least. I tried my own method replacement and it appears to be working at the very first stages (IE it's not crashing), but the method replacement itself does not seem to be working this time. However, it is producing a proper error log for this, so after trying to figure it out myself with not much luck, I figured I would ask and see if you can see anything that seems wrong here or something.

At first I thought that maybe it had something to do with the original method having the private access modifier, but when I looked at the Harmony's "Hello World" example, the original method they used was also private, so I figure that would not be the issue most likely. The only thing I can really think of is that this method while void, does have some two arguments, unlike your example and the "Hello World" examples, and I can't seem to find anything in searching about this "problem" specifically, so i'm not really sure what it wants/needs to not throw this "NullReferenceException" when trying to replace the original method.

Here is the related files, error log, and script used included:

Test DLL Inject Mod.rar

Edit 1: Oh yeah, and if you try and test it, what SHOULD be happening if the method is replaced correctly is when you look at your armor values on the paperdoll and character sheet, they should read something other than a number, in this case "test" and some other text string garbage.
Hi Magicono43,


Please see the attached source .cs file. It will look quite a bit different from the tutorial, but I seemed to have tracked down the problem. I changed a bit of the Harmony syntax around, and I believe (but I am not 100% sure) that the problem may have been this (beyond the Harmony problems):

Code: Select all

TextLabel[] armourLabels = new TextLabel[DaggerfallEntity.NumberBodyParts];
The PaperDoll class already has a TextLabel[] armourLabels defined so when your method jumps in, it's creating a new one, which the existing class can't use. If the harmony method does replace it successfully, all the armor fields show 0.

So using a bit of different Harmony syntax, we can replace it and reference the armorLabels field directly within PaperDoll

The new method signature should look like this:

Code: Select all

public static bool Prefix(ref TextLabel[] ___armourLabels, PlayerEntity playerEntity, bool suppress = false)
Note the following:
  • The type returns a bool. If your replacement method returns true, it will run both the original and yours. If it returns false, it will skip the original.
  • One of the arguments is the name of the armourLabels field, as a ref and preceded by a double underscore. This is a way to get direct access to fields in the class.
  • The method name is "Prefix". This is a special method name used by Harmony.
You will also note the "Start" method is a lot smaller. It just executes a harmony method to do all the patches, and it will look for methods with a [HarmonyPatch] declarative tag. This makes it so we don't have to manually target methods.
Attachments
armor_mod_fixed.rar
(836 Bytes) Downloaded 130 times

User avatar
Magicono43
Posts: 1141
Joined: Tue Nov 06, 2018 7:06 am

Re: DFU Injector

Post by Magicono43 »

Well indeed, with the code you posted it does seem to work properly now.
Untitled.png
Untitled.png (145.47 KiB) Viewed 3620 times
Even after reading your post, i'm still honestly not sure why it works now besides that the biggest issue from before was that the class that the replaced method was in already had a definition for one of the values inside the replacement method.

But hey, at least it work, I think i'll take your first warning though and only use this when absolutely necessary, because clearly i'm not experienced enough in this sort of coding practice to use it too effectively without breaking something in the most simple examples, lol.

Thanks for taking the time to trouble-shoot this example though, hopefully it will help anyone out there that tries to use this at some point.

l3lessed
Posts: 1403
Joined: Mon Aug 12, 2019 4:32 pm
Contact:

Re: DFU Injector

Post by l3lessed »

This is cool and opens up a whole new real of possibilities.

However, I take it this will not play friendly with other mods, especially any that touch any base objects harmony is replacing?
My Daggerfall Mod Github: l3lessed DFU Mod Github

My Beth Mods: l3lessed Nexus Page

Daggerfall Unity mods: Combat Overhaul Mod

Enjoy the free work I'm doing? Consider lending your support.

User avatar
ShortBeard
Posts: 45
Joined: Tue Aug 06, 2019 1:10 pm

Re: DFU Injector

Post by ShortBeard »

l3lessed wrote: Wed Oct 14, 2020 4:21 pm This is cool and opens up a whole new real of possibilities.

However, I take it this will not play friendly with other mods, especially any that touch any base objects harmony is replacing?
It depends on the mod. If a regular .dfmod is calling from a method or overriding one, it will in fact use the replaced harmony version. But it really depends on what the effects of the swap-out are. If you are just changing something visually (like the text color on the armor stats) but the logic remains the same, the dfmod should still be able to handle it. But as you implied, if you completely change the logic of a method that a dfmod relies on, it could potentially break it.

It may be possible to swap out the method with one declared as "virtual" so it can be overriden in the .dfmod so it can skip changed code. Or you could do some sort of check for exisitng mods and change the code accordingly. A bit complicated though :)

Post Reply