diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c1982ea0..fbdedbba 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,6 +42,8 @@ jobs: copy libs/BepInEx.cfg Release/BepInEx/config/BepInEx.cfg copy PathfinderPatcher/bin/Release/PathfinderPatcher.exe Release/PathfinderPatcher.exe copy libs/Mono.Cecil.dll Release/Mono.Cecil.dll + copy Linux/intercept.so Release/intercept.so + copy Linux/StartPathfinder.sh Release/StartPathfinder.sh - name: Create Release ZIP uses: TheDoctor0/zip-release@0.6.0 diff --git a/BepInEx.Hacknet/BepInEx.Hacknet.csproj b/BepInEx.Hacknet/BepInEx.Hacknet.csproj index 4843374b..650e1da6 100644 --- a/BepInEx.Hacknet/BepInEx.Hacknet.csproj +++ b/BepInEx.Hacknet/BepInEx.Hacknet.csproj @@ -9,7 +9,7 @@ Properties BepInEx.Hacknet BepInEx.Hacknet - v4.0 + v4.5 512 true @@ -35,18 +35,17 @@ false - + ..\libs\0Harmony.dll - + ..\libs\BepInEx.Core.dll - - False + ..\libs\FNA.dll False - + ..\libs\HacknetPathfinder.exe False @@ -62,20 +61,19 @@ ..\libs\Mono.Cecil.Rocks.dll - + ..\libs\MonoMod.RuntimeDetour.dll - + ..\libs\MonoMod.Utils.dll - - ..\libs\SemVer.dll + + ..\libs\SemanticVersioning.dll - diff --git a/BepInEx.Hacknet/HacknetChainloader.cs b/BepInEx.Hacknet/HacknetChainloader.cs index 7ebf33d0..5b20cfe4 100644 --- a/BepInEx.Hacknet/HacknetChainloader.cs +++ b/BepInEx.Hacknet/HacknetChainloader.cs @@ -11,6 +11,7 @@ using HarmonyLib; using Microsoft.Xna.Framework; using Mono.Cecil; +using Mono.Cecil.Cil; using MonoMod.Cil; using HN = global::Hacknet; @@ -138,13 +139,27 @@ internal static bool LoadTempPluginsPrefix(ExtensionInfo info) [HarmonyPatch(typeof(HN.OS), nameof(HN.OS.quitGame))] internal static void UnloadOnOSQuitPostfix() => HacknetChainloader.Instance.UnloadTemps(); - // I would hook Hacknet.Screens.DrawExtensionInfoDetail instead, but for some reason that method is cursed, so I look here instead - [HarmonyPostfix] - [HarmonyPatch(typeof(Button), nameof(Button.doButton), new Type[] { typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(string), typeof(Color?) })] - internal static void OnBackButtonPressPostfix(int myID, bool __result) + [HarmonyILManipulator] + [HarmonyPatch(typeof(ExtensionsMenuScreen), nameof(ExtensionsMenuScreen.DrawExtensionInfoDetail))] + internal static void OnBackButtonPressPostfix(ILContext il) + { + ILCursor c = new ILCursor(il); + + c.GotoNext(MoveType.Before, + x => x.MatchLdnull(), + x => x.MatchStfld(AccessTools.Field(typeof(ExtensionsMenuScreen), nameof(ExtensionsMenuScreen.ExtensionInfoToShow))) + ); + + c.Emit(OpCodes.Ldsfld, AccessTools.Field(typeof(HacknetChainloader), nameof(HacknetChainloader.Instance))); + c.Emit(OpCodes.Callvirt, AccessTools.Method(typeof(HacknetChainloader), nameof(HacknetChainloader.UnloadTemps))); + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(AppDomain), "get_BaseDirectory")] + private static bool ReturnCorrectDirectoryPrefix(out string __result) { - if (myID == 7900040 && __result) - HacknetChainloader.Instance.UnloadTemps(); + __result = Paths.GameRootPath; + return false; } } diff --git a/ExampleMod/ExampleMod.csproj b/ExampleMod/ExampleMod.csproj index 1196b7d3..88ffc401 100644 --- a/ExampleMod/ExampleMod.csproj +++ b/ExampleMod/ExampleMod.csproj @@ -9,7 +9,7 @@ Properties ExampleMod ExampleMod - v4.0 + v4.5 512 true @@ -33,30 +33,22 @@ 4 - + ..\libs\0Harmony.dll - False + False - + ..\libs\BepInEx.Core.dll - False + False - + ..\libs\FNA.dll False - + ..\libs\HacknetPathfinder.exe False - - ..\libs\Mono.Cecil.dll - False - - - ..\libs\MonoMod.Utils.dll - False - diff --git a/Linux/StartPathfinder.sh b/Linux/StartPathfinder.sh new file mode 100644 index 00000000..3bad6c53 --- /dev/null +++ b/Linux/StartPathfinder.sh @@ -0,0 +1 @@ +TERM=xterm LD_PRELOAD="$(pwd)/lib64/libcef.so $(pwd)/intercept.so /usr/lib/libmono-2.0.so.1" MONO_DEBUG=explicit-null-checks ./HacknetPathfinder.bin.x86_64 diff --git a/Linux/intercept.c b/Linux/intercept.c new file mode 100644 index 00000000..3ea0b95e --- /dev/null +++ b/Linux/intercept.c @@ -0,0 +1,11 @@ +#define _GNU_SOURCE + +#include +#include + +void mono_set_dirs(char *assembly_dir,char *config_dir) { + setenv("MONO_PATH", "/usr/lib/mono/4.5", 1); + + void (*set_dir_ptr)(char*,char*) = dlsym(RTLD_NEXT, "mono_set_dirs"); + (*set_dir_ptr)("/usr/lib/mono/4.5", config_dir); +} diff --git a/Linux/intercept.so b/Linux/intercept.so new file mode 100755 index 00000000..79511f25 Binary files /dev/null and b/Linux/intercept.so differ diff --git a/PathfinderAPI/PathfinderAPI.csproj b/PathfinderAPI/PathfinderAPI.csproj index c397635d..eb101cf6 100644 --- a/PathfinderAPI/PathfinderAPI.csproj +++ b/PathfinderAPI/PathfinderAPI.csproj @@ -9,7 +9,7 @@ Properties Pathfinder PathfinderAPI - v4.0 + v4.5 512 true 8 @@ -32,29 +32,29 @@ 4 - + ..\libs\0Harmony.dll - False + False - + ..\libs\BepInEx.Core.dll - False + False - + ..\libs\FNA.dll False - + ..\libs\HacknetPathfinder.exe False ..\libs\Mono.Cecil.dll - False + False - + ..\libs\MonoMod.Utils.dll - False + False diff --git a/PathfinderInstaller/PathfinderInstaller.py b/PathfinderInstaller/PathfinderInstaller.py index 1e6fee2f..7124de2f 100644 --- a/PathfinderInstaller/PathfinderInstaller.py +++ b/PathfinderInstaller/PathfinderInstaller.py @@ -41,9 +41,12 @@ def install_pathfinder(gen_event_callback, hacknet_directory): try: os.remove(patcher_exe) os.remove(os.path.join(hacknet_directory, 'Mono.Cecil.dll')) - hacknet_exe = os.path.join(hacknet_directory, 'Hacknet.exe') - os.rename(hacknet_exe, os.path.join(hacknet_directory, 'HacknetOld.exe')) - os.rename(os.path.join(hacknet_directory, 'HacknetPathfinder.exe'), hacknet_exe) + if platform.system() == 'Windows': + hacknet_exe = os.path.join(hacknet_directory, 'Hacknet.exe') + os.rename(hacknet_exe, os.path.join(hacknet_directory, 'HacknetOld.exe')) + os.rename(os.path.join(hacknet_directory, 'HacknetPathfinder.exe'), hacknet_exe) + else: + shutil.copy(os.path.join(hacknet_directory, 'Hacknet.bin.x86_64'), os.path.join(hacknet_directory, 'HacknetPathfinder.bin.x86_64')) except OSError: gen_event_callback('<>') return diff --git a/PathfinderPatcher/Program.cs b/PathfinderPatcher/Program.cs index 7617cddb..3c35a6f3 100644 --- a/PathfinderPatcher/Program.cs +++ b/PathfinderPatcher/Program.cs @@ -1,7 +1,7 @@ using System; using System.Linq; using System.Security; -using System.Reflection; +using SR = System.Reflection; using Mono.Cecil; using Mono.Cecil.Cil; @@ -66,17 +66,17 @@ void MakePublic(TypeDefinition type, bool nested = false) processor.Emit(OpCodes.Ldstr, "./BepInEx/core/BepInEx.Hacknet.dll"); processor.Emit(OpCodes.Call, hn.MainModule.ImportReference(typeof(System.IO.Path).GetMethod("GetFullPath", new Type[] { typeof(string) }))); // Load BepInEx.Hacknet.dll - processor.Emit(OpCodes.Call, hn.MainModule.ImportReference(typeof(Assembly).GetMethod("LoadFile", new Type[] { typeof(string) }))); + processor.Emit(OpCodes.Call, hn.MainModule.ImportReference(typeof(SR.Assembly).GetMethod("LoadFile", new Type[] { typeof(string) }))); // Get Entrypoint type processor.Emit(OpCodes.Ldstr, "BepInEx.Hacknet.Entrypoint"); - processor.Emit(OpCodes.Call, hn.MainModule.ImportReference(typeof(Assembly).GetMethod("GetType", new Type[] { typeof(string) }))); + processor.Emit(OpCodes.Call, hn.MainModule.ImportReference(typeof(SR.Assembly).GetMethod("GetType", new Type[] { typeof(string) }))); // Get bootstrap method processor.Emit(OpCodes.Ldstr, "Bootstrap"); processor.Emit(OpCodes.Call, hn.MainModule.ImportReference(typeof(Type).GetMethod("GetMethod", new Type[] { typeof(string) }))); // Call bootstrap method processor.Emit(OpCodes.Ldnull); processor.Emit(OpCodes.Ldnull); - processor.Emit(OpCodes.Callvirt, hn.MainModule.ImportReference(typeof(MethodBase).GetMethod("Invoke", new Type[] { typeof(object), typeof(object[]) }))); + processor.Emit(OpCodes.Callvirt, hn.MainModule.ImportReference(typeof(SR.MethodBase).GetMethod("Invoke", new Type[] { typeof(object), typeof(object[]) }))); processor.Emit(OpCodes.Pop); // Return processor.Emit(OpCodes.Ret); @@ -88,6 +88,17 @@ void MakePublic(TypeDefinition type, bool nested = false) var unverifiableCtor = hn.MainModule.ImportReference(unverifiableType.Methods.First(x => x.IsConstructor && x.Parameters.Count == 0)); hn.MainModule.CustomAttributes.Add(new CustomAttribute(unverifiableCtor)); + var corlibRef = hn.MainModule.AssemblyReferences.FirstOrDefault(x => x.Name == "mscorlib"); + corlibRef.PublicKey = null; + corlibRef.PublicKeyToken = null; + corlibRef.HasPublicKey = false; + + var targetRuntime = hn.CustomAttributes.FirstOrDefault(x => x.AttributeType.Name == "TargetFrameworkAttribute"); + targetRuntime.ConstructorArguments.Clear(); + targetRuntime.ConstructorArguments.Add(new CustomAttributeArgument(hn.MainModule.TypeSystem.String, ".NETFramework,Version=v4.5")); + targetRuntime.Properties.Clear(); + targetRuntime.Properties.Add(new CustomAttributeNamedArgument("FrameworkDisplayName", new CustomAttributeArgument(hn.MainModule.TypeSystem.String, ".NET Framework 4.5"))); + // Write modified assembly to disk hn.Write("HacknetPathfinder.exe"); diff --git a/README.md b/README.md index 5a26ab17..16832e4d 100644 --- a/README.md +++ b/README.md @@ -8,20 +8,32 @@ There are several options available to choose to install Pathfinder, the install ### Installer -If you're on Windows, it's recommended that you use the installer .exe from [here](https://github.com/Arkhist/Hacknet-Pathfinder/releases). Just run the installer and it should automatically find your Hacknet folder, then just hit install. Launching Hacknet from Steam will launch Pathfinder! +If you're on Windows, it's recommended that you use the installer .exe from [here](https://github.com/Arkhist/Hacknet-Pathfinder/releases). Just run the installer and it should automatically find your Hacknet folder, then just hit install. Launching Hacknet from Steam will launch Pathfinder (on Windows)! If you decide to use the .py installer (or you're just on Linux and have to use it) keep in mind it requires python3 and tk to be installed before you run it. +If you're on Linux, once the installer is complete, make sure to +x StartPathfinder.sh yourself. + To uninstall, just reopen the installer and click uninstall. This will clear out all of the changes the installer made, and will also delete all of your mods with it. -### Manually +### Manually (Windows) Get the latest ZIP from the releases page [here](https://github.com/Arkhist/Hacknet-Pathfinder/releases) and extract it to your Hacknet folder. -Run PathfinderPatcher.exe and it will create HacknetPathfinder.exe, if you want this to be launched when you launch from Steam rename Hacknet.exe and replace it with HacknetPathfinder.exe (this also applies to Linux). +Run PathfinderPatcher.exe and it will create HacknetPathfinder.exe, if you want this to be launched when you launch from Steam rename Hacknet.exe and replace it with HacknetPathfinder.exe. To uninstall, just delete HacknetPathfinder.exe (or whatever you renamed it to) and move back the original Hacknet.exe if you renamed it. If you also want to remove all your mods and configs, delete the BepInEx directory. +### Manually (Linux) + +Get the latest ZIP from the releases page [here](https://github.com/Arkhist/Hacknet-Pathfinder/releases) and extract it to your Hacknet folder. + +Run PathfinderPatcher.exe and it will create HacknetPathfinder.exe. + +Copy `Hacknet.bin.x86_64` to `HacknetPathfinder.bin.x86_64` + +Make StartPathfinder.sh executable and run it. + ## Troubleshooting ### The game crashes before it even loads! (Windows only) diff --git a/libs/0Harmony.dll b/libs/0Harmony.dll index 1065ad10..2f2e6ef9 100644 Binary files a/libs/0Harmony.dll and b/libs/0Harmony.dll differ diff --git a/libs/BepInEx.Core.dll b/libs/BepInEx.Core.dll index af11f59e..f90a869e 100644 Binary files a/libs/BepInEx.Core.dll and b/libs/BepInEx.Core.dll differ diff --git a/libs/BepInEx.Core.xml b/libs/BepInEx.Core.xml index 46f787d1..d42d0727 100644 --- a/libs/BepInEx.Core.xml +++ b/libs/BepInEx.Core.xml @@ -810,7 +810,7 @@ - The version range of the referenced plugin. + The version range of the referenced plugin. @@ -887,6 +887,14 @@ The plugin instance. The attributes of the instance, if existing. + + + Gets the specified attributes of a reflection metadata type, if they exist. + + The attribute type to retrieve. + The reflection metadata instance. + The attributes of the instance, if existing. + Retrieves the dependencies of the specified plugin type. diff --git a/libs/MonoMod.RuntimeDetour.dll b/libs/MonoMod.RuntimeDetour.dll index 35087890..ede4adbe 100644 Binary files a/libs/MonoMod.RuntimeDetour.dll and b/libs/MonoMod.RuntimeDetour.dll differ diff --git a/libs/MonoMod.Utils.dll b/libs/MonoMod.Utils.dll index a8fecaf8..4eadfbe7 100644 Binary files a/libs/MonoMod.Utils.dll and b/libs/MonoMod.Utils.dll differ diff --git a/libs/SemVer.dll b/libs/SemVer.dll deleted file mode 100644 index daba2a2e..00000000 Binary files a/libs/SemVer.dll and /dev/null differ diff --git a/libs/SemanticVersioning.dll b/libs/SemanticVersioning.dll new file mode 100644 index 00000000..9e47be91 Binary files /dev/null and b/libs/SemanticVersioning.dll differ