From 56c094a2d760195c589c16be9fbfd085e2aa0bd4 Mon Sep 17 00:00:00 2001 From: "Melvin \"Dumbass\" Shwuaner" <83349478+MelvinShwuaner@users.noreply.github.com> Date: Mon, 9 Nov 2026 14:57:06 +0300 Subject: [PATCH 01/14] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e3c854c..02d647e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ The logo is generated by Image Creator on 2023/10/13. Inform the remedy if there is infringement.

-A new mod loader for [Worldbox](http://www.superworldbox.com/) after [NCMS](https://denq04.github.io/ncms/), with an Android version, called NML +A new mod loader for [Worldbox](http://www.superworldbox.com/) after [NCMS](https://denq04.github.io/ncms/), with Complete Platform Support, called NML for short. Detailed introduction look [Gitbook](https://markamibox.github.io/worldbox-modding-box/#/index).

Overview

@@ -37,7 +37,7 @@ It is recommended to subscribe to the NML item on the Steam Workshop to receive ### Android Installation -1. Use a suitable Mono mod loader for worldbox, like FusionCore +1. Use a suitable Mono mod loader for worldbox 2. Move NeoModLoader.dll to `/mods` 3. enable experimental mode and start the game From 8b33768fff52cbd57166bf7d05bf8685934e6c6c Mon Sep 17 00:00:00 2001 From: "Melvin \"Dumbass\" Shwuaner" <83349478+MelvinShwuaner@users.noreply.github.com> Date: Mon, 9 Nov 2026 15:47:09 +0300 Subject: [PATCH 02/14] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 02d647e..ce31587 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,8 @@ for short. Detailed introduction look [Gitbook](https://markamibox.github.io/wor 2. Delete NCMS_memload.dll (They can be installed together but not suggested) 3. Start the game with experimental mode enabled -It is recommended to subscribe to the NML item on the Steam Workshop to receive automatic updates - +It is recommended to subscribe to the NML item on the Steam Workshop to receive automatic updates +note: standard mobile apps are IL2CPP and cant be modded normally. so you need a Mono version. NeoModLoader is not affiliated with these. these may also be outdated for the latest version. ### Android Installation 1. Use a suitable Mono mod loader for worldbox From 1829154bd010564f2dbe5b97c3219fd0f7ae7b75 Mon Sep 17 00:00:00 2001 From: "Melvin \"Dumbass\" Shwuaner" <83349478+MelvinShwuaner@users.noreply.github.com> Date: Mon, 9 Nov 2026 15:47:17 +0300 Subject: [PATCH 03/14] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ce31587..72331bb 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ for short. Detailed introduction look [Gitbook](https://markamibox.github.io/wor 3. Start the game with experimental mode enabled It is recommended to subscribe to the NML item on the Steam Workshop to receive automatic updates -note: standard mobile apps are IL2CPP and cant be modded normally. so you need a Mono version. NeoModLoader is not affiliated with these. these may also be outdated for the latest version. +note: standard mobile versions are IL2CPP and cant be modded normally. so you need a Mono version. NeoModLoader is not affiliated with these. these may also be outdated for the latest version. ### Android Installation 1. Use a suitable Mono mod loader for worldbox From 9b322b0973d133844c7bf8c70f89e359bcdfd4b5 Mon Sep 17 00:00:00 2001 From: "Melvin \"Dumbass\" Shwuaner" <83349478+MelvinShwuaner@users.noreply.github.com> Date: Mon, 9 Nov 2026 15:47:35 +0300 Subject: [PATCH 04/14] Update README with mobile modding information Added note about mobile versions and Mono mod loader. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 72331bb..3a82919 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ for short. Detailed introduction look [Gitbook](https://markamibox.github.io/wor 3. Start the game with experimental mode enabled It is recommended to subscribe to the NML item on the Steam Workshop to receive automatic updates + note: standard mobile versions are IL2CPP and cant be modded normally. so you need a Mono version. NeoModLoader is not affiliated with these. these may also be outdated for the latest version. ### Android Installation From 56b6f8ec7bc0b482b6ca91ab6d0f8541bb0c0e0d Mon Sep 17 00:00:00 2001 From: "Melvin \"Dumbass\" Shwuaner" <83349478+MelvinShwuaner@users.noreply.github.com> Date: Mon, 9 Nov 2026 17:48:08 +0300 Subject: [PATCH 05/14] native library --- WorldBoxMod.cs | 2 + constants/Paths.cs | 9 ++- services/NativeLibraryManager.cs | 134 +++++++++++++++++++++++++++++++ utils/ResourcesPatch.cs | 16 +++- 4 files changed, 156 insertions(+), 5 deletions(-) create mode 100644 services/NativeLibraryManager.cs diff --git a/WorldBoxMod.cs b/WorldBoxMod.cs index b0cb6ec..9b18986 100644 --- a/WorldBoxMod.cs +++ b/WorldBoxMod.cs @@ -145,6 +145,8 @@ private void Update() Paths.NCMSAdditionModResourceFolderName), Linker); ResourcesPatch.LoadAssetBundlesFromFolder(Path.Combine(mod.mod_decl.FolderPath, Paths.ModAssetBundleFolderName)); + ResourcesPatch.LoadNativeLibrariesFromFolder(Path.Combine(mod.mod_decl.FolderPath, + Paths.NativeLibrariesFolder)); } }, "Load Resources From Mod " + mod.mod_decl.Name); } diff --git a/constants/Paths.cs b/constants/Paths.cs index e6db87d..de19c4b 100644 --- a/constants/Paths.cs +++ b/constants/Paths.cs @@ -17,7 +17,10 @@ public static class Paths /// Path to persistent data /// public static readonly string PersistentDataPath = Combine(Application.persistentDataPath); - + /// + /// path to special folder. on PC its just the Users home directory. on Mobile its app-specific directories. + /// + public static string SpecialFolderPath => System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); /// /// Path to folder StreamingAssets /// @@ -109,6 +112,10 @@ public static class Paths /// public static readonly string ModDefaultConfigFileName = "default_config.json"; + /// + /// Folder name of a mod's resource folder which will be patched to + /// + public static readonly string NativeLibrariesFolder = "Native"; /// /// Folder name of a mod's resource folder which will be patched to /// diff --git a/services/NativeLibraryManager.cs b/services/NativeLibraryManager.cs new file mode 100644 index 0000000..d7d17ea --- /dev/null +++ b/services/NativeLibraryManager.cs @@ -0,0 +1,134 @@ +using System.Collections.Concurrent; +using System.Reflection; +using System.Runtime.InteropServices; +using NeoModLoader.constants; +using UnityEngine; + +namespace NeoModLoader.services; + +public static class NativeLibraryManager +{ + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi, EntryPoint = "LoadLibrary")] + private static extern IntPtr WinLoadLibrary(string lpFileName); + [DllImport("kernel32.dll", CharSet = CharSet.Ansi, EntryPoint = "GetProcAddress", SetLastError = true)] + private static extern IntPtr WinGetAddress(IntPtr hModule, string procName); + + private const int RTLD_NOW = 2; + [DllImport("libdl.so.2", EntryPoint = "dlopen", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr UnixDlOpen(string filename, int flags); + [DllImport("libdl", EntryPoint = "dlsym", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr UnixDlSym(IntPtr handle, string symbol); + + class UnixHandler(string FolderName, string SpecialPath) : PlatformNativeHandler + { + public void LoadNativeLibraries(string path) + { + string Folder = Path.Combine(path, FolderName); + if (!Directory.Exists(Folder)) + { + LogService.LogWarning($"Warning: {path} doesn't contain any native libraries for the current platform!"); + return; + } + foreach (var file in Directory.GetFiles(Folder)) + { + IntPtr handle; + if (SpecialPath != null) //android requires libraries in data/user + { + string newfile = Path.Combine(SpecialPath, Path.GetFileName(file)); + File.Copy(file, newfile, true); + handle = UnixDlOpen(newfile, RTLD_NOW); + } + else + { + handle = UnixDlOpen(file, RTLD_NOW); + } + if (handle == IntPtr.Zero) + { + throw new BadImageFormatException($"Failed to load native library {file}"); + } + LibraryHandles.TryAdd(Path.GetFileNameWithoutExtension(file), handle); + } + } + + public IntPtr GetFunction(IntPtr Handle, string Name) + { + return UnixDlSym(Handle, Name); + } + } + + class NotSupportedHandler : PlatformNativeHandler + { + public void LoadNativeLibraries(string Folder) + { + throw new PlatformNotSupportedException("Native libraries are not supported on this platform!"); + } + + public IntPtr GetFunction(IntPtr Handle, string Name) + { + throw new PlatformNotSupportedException("Native libraries are not supported on this platform!"); + } + } + class WinHandler(string FolderName) : PlatformNativeHandler + { + public void LoadNativeLibraries(string path) + { + string Folder = Path.Combine(path, FolderName); + if (!Directory.Exists(Folder)) + { + LogService.LogWarning($"Warning: {path} doesn't contain any native libraries for the current platform!"); + return; + } + foreach (var file in Directory.GetFiles(Folder)) + { + IntPtr handle = WinLoadLibrary(file); + if (handle == IntPtr.Zero) + { + throw new BadImageFormatException($"Failed to load native library {file}"); + } + LibraryHandles.TryAdd(Path.GetFileNameWithoutExtension(file), handle); + } + } + public IntPtr GetFunction(IntPtr Handle, string Name) + { + return WinGetAddress(Handle, Name); + } + } + interface PlatformNativeHandler + { + void LoadNativeLibraries(string Folder); + IntPtr GetFunction(IntPtr Handle, string Name); + } + + private static ConcurrentDictionary LibraryHandles = new ConcurrentDictionary(); + private static PlatformNativeHandler Handler; + + static NativeLibraryManager() + { + Handler = Application.platform switch + { + RuntimePlatform.OSXEditor => new UnixHandler("osx", null), + RuntimePlatform.OSXPlayer => new NotSupportedHandler(), + RuntimePlatform.WindowsPlayer => new WinHandler("win"), + RuntimePlatform.WindowsEditor => new WinHandler("win"), + RuntimePlatform.IPhonePlayer => new NotSupportedHandler(), + RuntimePlatform.Android => new UnixHandler("android", Path.Combine(Paths.SpecialFolderPath, "NML")), + RuntimePlatform.LinuxPlayer or RuntimePlatform.LinuxEditor => new UnixHandler("linux", null), + _ => new NotSupportedHandler() + }; + } + + public static void LoadLibraries(string Folder) + { + Handler.LoadNativeLibraries(Folder); + } + + public static T GetNativeFunction(string Name, string Symbol) where T : Delegate + { + if (!LibraryHandles.TryGetValue(Name, out IntPtr Handle)) + { + return null; + } + IntPtr Function = Handler.GetFunction(Handle, Name); + return Marshal.GetDelegateForFunctionPointer(Function, typeof(T)) as T; + } +} \ No newline at end of file diff --git a/utils/ResourcesPatch.cs b/utils/ResourcesPatch.cs index 06cf140..1b8fe47 100644 --- a/utils/ResourcesPatch.cs +++ b/utils/ResourcesPatch.cs @@ -111,7 +111,11 @@ private static TextAsset LoadTextAsset(string path) textAsset.name = Path.GetFileNameWithoutExtension(path); return textAsset; } - + internal static void LoadNativeLibrariesFromFolder(string pFolder) + { + if (!Directory.Exists(pFolder)) return; + NativeLibraryManager.LoadLibraries(pFolder); + } internal static void LoadResourceFromFolder(string pFolder, AssetLinker Linker) { if (!Directory.Exists(pFolder)) return; @@ -131,16 +135,20 @@ internal static void LoadAssetBundlesFromFolder(string pFolder) { RuntimePlatform.WindowsPlayer => "win", RuntimePlatform.WindowsEditor => "win", - RuntimePlatform.Android => "and", + RuntimePlatform.Android => "android", RuntimePlatform.IPhonePlayer => "ios", RuntimePlatform.OSXPlayer => "osx", RuntimePlatform.OSXEditor => "osx", RuntimePlatform.LinuxPlayer => "linux", RuntimePlatform.LinuxEditor => "linux", - _ => "win" + _ => throw new PlatformNotSupportedException() }; string platform_folder = Path.Combine(pFolder, platform_subfolder_name); - if (!Directory.Exists(platform_folder)) return; + if (!Directory.Exists(platform_folder)) + { + LogService.LogWarning($"Warning: {pFolder} doesn't contain any asset bundles for the current platform!"); + return; + } AssetBundleUtils.LoadFromFolder(platform_folder); } From f809b8f287c4cba95f974621d92b062b8ce78c66 Mon Sep 17 00:00:00 2001 From: "Melvin \"Dumbass\" Shwuaner" <83349478+MelvinShwuaner@users.noreply.github.com> Date: Mon, 9 Nov 2026 17:53:00 +0300 Subject: [PATCH 06/14] Update NativeLibraryManager.cs --- services/NativeLibraryManager.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/services/NativeLibraryManager.cs b/services/NativeLibraryManager.cs index d7d17ea..cd20fca 100644 --- a/services/NativeLibraryManager.cs +++ b/services/NativeLibraryManager.cs @@ -14,6 +14,7 @@ public static class NativeLibraryManager private static extern IntPtr WinGetAddress(IntPtr hModule, string procName); private const int RTLD_NOW = 2; + private const int RTLD_GLOBAL = 8; [DllImport("libdl.so.2", EntryPoint = "dlopen", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr UnixDlOpen(string filename, int flags); [DllImport("libdl", EntryPoint = "dlsym", CallingConvention = CallingConvention.Cdecl)] @@ -23,6 +24,7 @@ class UnixHandler(string FolderName, string SpecialPath) : PlatformNativeHandler { public void LoadNativeLibraries(string path) { + string Folder = Path.Combine(path, FolderName); if (!Directory.Exists(Folder)) { @@ -36,11 +38,11 @@ public void LoadNativeLibraries(string path) { string newfile = Path.Combine(SpecialPath, Path.GetFileName(file)); File.Copy(file, newfile, true); - handle = UnixDlOpen(newfile, RTLD_NOW); + handle = UnixDlOpen(newfile, RTLD_NOW | RTLD_GLOBAL); } else { - handle = UnixDlOpen(file, RTLD_NOW); + handle = UnixDlOpen(file, RTLD_NOW | RTLD_GLOBAL); } if (handle == IntPtr.Zero) { @@ -131,4 +133,5 @@ public static T GetNativeFunction(string Name, string Symbol) where T : Deleg IntPtr Function = Handler.GetFunction(Handle, Name); return Marshal.GetDelegateForFunctionPointer(Function, typeof(T)) as T; } + } \ No newline at end of file From 48443abae34350efacbb670f3199fea1735d7a58 Mon Sep 17 00:00:00 2001 From: "Melvin \"Dumbass\" Shwuaner" <83349478+MelvinShwuaner@users.noreply.github.com> Date: Mon, 9 Nov 2026 21:18:50 +0300 Subject: [PATCH 07/14] Update NativeLibraryManager.cs --- services/NativeLibraryManager.cs | 289 +++++++++++++++++++++---------- 1 file changed, 196 insertions(+), 93 deletions(-) diff --git a/services/NativeLibraryManager.cs b/services/NativeLibraryManager.cs index cd20fca..5fb0457 100644 --- a/services/NativeLibraryManager.cs +++ b/services/NativeLibraryManager.cs @@ -1,137 +1,240 @@ using System.Collections.Concurrent; +using System.Data; using System.Reflection; using System.Runtime.InteropServices; using NeoModLoader.constants; +using Newtonsoft.Json; using UnityEngine; namespace NeoModLoader.services; +using System; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Json; +using System.Text; +using UnityEngine; public static class NativeLibraryManager { - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi, EntryPoint = "LoadLibrary")] - private static extern IntPtr WinLoadLibrary(string lpFileName); - [DllImport("kernel32.dll", CharSet = CharSet.Ansi, EntryPoint = "GetProcAddress", SetLastError = true)] - private static extern IntPtr WinGetAddress(IntPtr hModule, string procName); - - private const int RTLD_NOW = 2; - private const int RTLD_GLOBAL = 8; - [DllImport("libdl.so.2", EntryPoint = "dlopen", CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr UnixDlOpen(string filename, int flags); - [DllImport("libdl", EntryPoint = "dlsym", CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr UnixDlSym(IntPtr handle, string symbol); - class UnixHandler(string FolderName, string SpecialPath) : PlatformNativeHandler + // Windows + [DllImport("kernel32.dll", EntryPoint = "LoadLibraryW", + SetLastError = true, CharSet = CharSet.Unicode)] + private static extern IntPtr WinLoadLibrary(string fileName); + + [DllImport("kernel32.dll", EntryPoint = "GetProcAddress", + SetLastError = true, CharSet = CharSet.Ansi)] + private static extern IntPtr WinGetAddress(IntPtr handle, string symbol); + + // Linux + [DllImport("libdl.so.2", EntryPoint = "dlopen", + CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr LinuxDlOpen(string filename, int flags); + + [DllImport("libdl.so.2", EntryPoint = "dlsym", + CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr LinuxDlSym(IntPtr handle, string symbol); + + [DllImport("libdl.so.2", EntryPoint = "dlerror", + CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr LinuxDlError(); + + // Android + [DllImport("libdl.so", EntryPoint = "dlopen", + CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr AndroidDlOpen(string filename, int flags); + + [DllImport("libdl.so", EntryPoint = "dlsym", + CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr AndroidDlSym(IntPtr handle, string symbol); + + [DllImport("libdl.so", EntryPoint = "dlerror", + CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr AndroidDlError(); + + private const int OPEN_FLAGS = 2 | 0x100; + + delegate IntPtr loadlib(string Path, out string Error); + delegate IntPtr getaddress(IntPtr Handle, string symbol); + + static getaddress GetAddress; + static loadlib Loadlib; + struct NativeLibrary(IntPtr Handle, string Path) + { + public IntPtr Handle = Handle; + public string Path = Path; + } + private static readonly ConcurrentDictionary + Libraries = new(); + + private static string PlatformFolder; + private static string SpecialPath; + + [Serializable] + private class LoadOrder { - public void LoadNativeLibraries(string path) + public List Order { get; set; } + } + + static NativeLibraryManager() + { + switch (Application.platform) { - - string Folder = Path.Combine(path, FolderName); - if (!Directory.Exists(Folder)) - { - LogService.LogWarning($"Warning: {path} doesn't contain any native libraries for the current platform!"); - return; - } - foreach (var file in Directory.GetFiles(Folder)) - { - IntPtr handle; - if (SpecialPath != null) //android requires libraries in data/user + case RuntimePlatform.WindowsEditor: + case RuntimePlatform.WindowsPlayer: + GetAddress = WinGetAddress; + Loadlib = (path, out error) => { - string newfile = Path.Combine(SpecialPath, Path.GetFileName(file)); - File.Copy(file, newfile, true); - handle = UnixDlOpen(newfile, RTLD_NOW | RTLD_GLOBAL); - } - else - { - handle = UnixDlOpen(file, RTLD_NOW | RTLD_GLOBAL); - } - if (handle == IntPtr.Zero) + error = null; + IntPtr handle = WinLoadLibrary(path); + if (handle == IntPtr.Zero) + { + error = new System.ComponentModel.Win32Exception( + Marshal.GetLastWin32Error()).Message; + } + + return handle; + }; + PlatformFolder = "win"; + break; + + case RuntimePlatform.LinuxEditor: + case RuntimePlatform.LinuxPlayer: + GetAddress = LinuxDlSym; + Loadlib = (path, out error) => { - throw new BadImageFormatException($"Failed to load native library {file}"); - } - LibraryHandles.TryAdd(Path.GetFileNameWithoutExtension(file), handle); - } - } + error = null; + IntPtr handle = LinuxDlOpen(path, OPEN_FLAGS); + if (handle == IntPtr.Zero) + { + error = Marshal.PtrToStringAnsi(LinuxDlError()); + } + return handle; + }; + PlatformFolder = "linux"; + break; - public IntPtr GetFunction(IntPtr Handle, string Name) - { - return UnixDlSym(Handle, Name); + case RuntimePlatform.Android: + PlatformFolder = "android"; + SpecialPath = Path.Combine( + Paths.SpecialFolderPath, "NML"); + GetAddress = AndroidDlSym; + Loadlib = (path, out error) => + { + error = null; + IntPtr handle = AndroidDlOpen(path, OPEN_FLAGS); + if (handle == IntPtr.Zero) + { + error = Marshal.PtrToStringAnsi(AndroidDlError()); + } + return handle; + }; + break; + + default: + PlatformFolder = null; + break; } } - class NotSupportedHandler : PlatformNativeHandler + public static void LoadLibraries(string Folder) { - public void LoadNativeLibraries(string Folder) + if (PlatformFolder == null) { throw new PlatformNotSupportedException("Native libraries are not supported on this platform!"); } + string nativeFolder = Path.Combine(Folder, PlatformFolder); + if (!Directory.Exists(nativeFolder)) + { + LogService.LogWarning( + $"Warning: {Folder} doesn't contain any native libraries for the current platform!"); + return; + } + string[] files = Directory.GetFiles(nativeFolder); + + Dictionary available = + new Dictionary(StringComparer.Ordinal); - public IntPtr GetFunction(IntPtr Handle, string Name) + foreach (string file in files) { - throw new PlatformNotSupportedException("Native libraries are not supported on this platform!"); + string name = Path.GetFileNameWithoutExtension(file); + + available.Add(name, file); } - } - class WinHandler(string FolderName) : PlatformNativeHandler - { - public void LoadNativeLibraries(string path) + + List loadOrder = GetOrder( + Folder) ?? [..available.Keys]; + + foreach (string name in loadOrder) { - string Folder = Path.Combine(path, FolderName); - if (!Directory.Exists(Folder)) + string file = available[name]; + + if (Libraries.TryGetValue(name, out var lib)) { - LogService.LogWarning($"Warning: {path} doesn't contain any native libraries for the current platform!"); - return; + if (string.Equals(lib.Path, file, + StringComparison.Ordinal)) + continue; + + throw new InvalidOperationException( + $"Native library '{name}' has already been loaded from '{lib.Path}'. " + + $"Cannot load it again from '{file}'."); } - foreach (var file in Directory.GetFiles(Folder)) + + string loadPath = file; + if (SpecialPath != null) { - IntPtr handle = WinLoadLibrary(file); - if (handle == IntPtr.Zero) - { - throw new BadImageFormatException($"Failed to load native library {file}"); - } - LibraryHandles.TryAdd(Path.GetFileNameWithoutExtension(file), handle); + Directory.CreateDirectory(SpecialPath); + loadPath = Path.Combine(SpecialPath, Path.GetFileName(file)); + File.Copy(file, loadPath, true); } + IntPtr handle = Loadlib(loadPath, out string error); + if (handle == IntPtr.Zero) + { + throw new BadImageFormatException( + $"Failed to load native library '{file}': {error}"); + } + Libraries[name] = new NativeLibrary(handle, file); } - public IntPtr GetFunction(IntPtr Handle, string Name) - { - return WinGetAddress(Handle, Name); - } - } - interface PlatformNativeHandler - { - void LoadNativeLibraries(string Folder); - IntPtr GetFunction(IntPtr Handle, string Name); } - private static ConcurrentDictionary LibraryHandles = new ConcurrentDictionary(); - private static PlatformNativeHandler Handler; - - static NativeLibraryManager() + public static T GetNativeFunction( + string Name, string Symbol) where T : Delegate { - Handler = Application.platform switch + if (!Libraries.TryGetValue(Name, out var lib)) + return null; + IntPtr function = GetAddress(lib.Handle, Symbol); + if (function == IntPtr.Zero) { - RuntimePlatform.OSXEditor => new UnixHandler("osx", null), - RuntimePlatform.OSXPlayer => new NotSupportedHandler(), - RuntimePlatform.WindowsPlayer => new WinHandler("win"), - RuntimePlatform.WindowsEditor => new WinHandler("win"), - RuntimePlatform.IPhonePlayer => new NotSupportedHandler(), - RuntimePlatform.Android => new UnixHandler("android", Path.Combine(Paths.SpecialFolderPath, "NML")), - RuntimePlatform.LinuxPlayer or RuntimePlatform.LinuxEditor => new UnixHandler("linux", null), - _ => new NotSupportedHandler() - }; + throw new EntryPointNotFoundException( + $"Could not find symbol '{Symbol}' in native library '{Name}'."); + } + return Marshal.GetDelegateForFunctionPointer( + function, typeof(T)) as T; } - public static void LoadLibraries(string Folder) - { - Handler.LoadNativeLibraries(Folder); - } + // Dependency handling - public static T GetNativeFunction(string Name, string Symbol) where T : Delegate + private static List GetOrder( + string root) { - if (!LibraryHandles.TryGetValue(Name, out IntPtr Handle)) - { + string manifestPath = Path.Combine( + root, "order.json"); + if (!File.Exists(manifestPath)) return null; + try + { + string Json = File.ReadAllText(manifestPath); + LoadOrder order = JsonConvert.DeserializeObject(Json); + return order?.Order; + } + catch (Exception ex) + { + throw new InvalidDataException( + "Failed to read order.json.", ex); } - IntPtr Function = Handler.GetFunction(Handle, Name); - return Marshal.GetDelegateForFunctionPointer(Function, typeof(T)) as T; } - } \ No newline at end of file From 919ad111295239c1e55bae042df1b500623f1385 Mon Sep 17 00:00:00 2001 From: "Melvin \"Dumbass\" Shwuaner" <83349478+MelvinShwuaner@users.noreply.github.com> Date: Mon, 9 Nov 2026 22:58:49 +0300 Subject: [PATCH 08/14] stuff2069 --- .../UnityEngine.AndroidJNIModule.dll | Bin 0 -> 84480 bytes constants/Paths.cs | 4 -- resources/commit | 2 +- services/NativeLibraryManager.cs | 37 ++++++++++++------ utils/MobileStuff.cs | 9 +++++ 5 files changed, 34 insertions(+), 18 deletions(-) create mode 100755 assembly-dependencies/UnityEngine.AndroidJNIModule.dll create mode 100644 utils/MobileStuff.cs diff --git a/assembly-dependencies/UnityEngine.AndroidJNIModule.dll b/assembly-dependencies/UnityEngine.AndroidJNIModule.dll new file mode 100755 index 0000000000000000000000000000000000000000..4a0c558e1129d3acfc7efd3271303044f8819543 GIT binary patch literal 84480 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 literal 0 HcmV?d00001 diff --git a/constants/Paths.cs b/constants/Paths.cs index de19c4b..bc27edf 100644 --- a/constants/Paths.cs +++ b/constants/Paths.cs @@ -18,10 +18,6 @@ public static class Paths /// public static readonly string PersistentDataPath = Combine(Application.persistentDataPath); /// - /// path to special folder. on PC its just the Users home directory. on Mobile its app-specific directories. - /// - public static string SpecialFolderPath => System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); - /// /// Path to folder StreamingAssets /// public static readonly string StreamingAssetsPath = Combine(Application.streamingAssetsPath); diff --git a/resources/commit b/resources/commit index 83ef0fe..0adda88 100644 --- a/resources/commit +++ b/resources/commit @@ -1 +1 @@ -17c7aa59e67d7b8eecf451070fe3efc09a0a0532 +48443abae34350efacbb670f3199fea1735d7a58 diff --git a/services/NativeLibraryManager.cs b/services/NativeLibraryManager.cs index 5fb0457..573745d 100644 --- a/services/NativeLibraryManager.cs +++ b/services/NativeLibraryManager.cs @@ -1,26 +1,16 @@ -using System.Collections.Concurrent; -using System.Data; using System.Reflection; -using System.Runtime.InteropServices; using NeoModLoader.constants; using Newtonsoft.Json; -using UnityEngine; - namespace NeoModLoader.services; using System; using System.Collections.Generic; using System.Collections.Concurrent; using System.IO; -using System.Linq; using System.Runtime.InteropServices; -using System.Runtime.Serialization; -using System.Runtime.Serialization.Json; -using System.Text; using UnityEngine; public static class NativeLibraryManager { - // Windows [DllImport("kernel32.dll", EntryPoint = "LoadLibraryW", SetLastError = true, CharSet = CharSet.Unicode)] @@ -120,8 +110,7 @@ static NativeLibraryManager() case RuntimePlatform.Android: PlatformFolder = "android"; - SpecialPath = Path.Combine( - Paths.SpecialFolderPath, "NML"); + SpecialPath = Path.Combine(IL2CPP2MONO.App.InternalPath, "NML"); GetAddress = AndroidDlSym; Loadlib = (path, out error) => { @@ -205,7 +194,7 @@ public static T GetNativeFunction( string Name, string Symbol) where T : Delegate { if (!Libraries.TryGetValue(Name, out var lib)) - return null; + throw new DllNotFoundException($"native library {Name} was not found!"); IntPtr function = GetAddress(lib.Handle, Symbol); if (function == IntPtr.Zero) { @@ -237,4 +226,26 @@ private static List GetOrder( "Failed to read order.json.", ex); } } + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate bool NativeModEntry (IntPtr NMLAPI); + + static IntPtr NMLAPI(string type, string method) + { + Assembly NML = typeof(WorldBoxMod).Assembly; + Type Type = NML.GetType(type); + if (Type == null) + { + return IntPtr.Zero; + } + MethodInfo Method = Type.GetMethod(method, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + return Method == null ? IntPtr.Zero : Method.MethodHandle.GetFunctionPointer(); + } + /// + /// native libraries which act as mods may use the NML API for assistance. the entry point is always bool ModEntry(IntPtr NMLAPIFUNC) where NMLAPIFUNC is IntPtr (string type, string method). this returns a neomodloader function which may be used + /// + public static bool LoadNativeMod(string Name) + { + NativeModEntry EntryPoint = GetNativeFunction(Name, "ModEntry"); + return EntryPoint(Marshal.GetFunctionPointerForDelegate(NMLAPI)); + } } \ No newline at end of file diff --git a/utils/MobileStuff.cs b/utils/MobileStuff.cs new file mode 100644 index 0000000..cd41a7d --- /dev/null +++ b/utils/MobileStuff.cs @@ -0,0 +1,9 @@ +using System.Runtime.CompilerServices; + +namespace IL2CPP2MONO; + +public class App +{ + public static string InternalPath { [MethodImpl(MethodImplOptions.InternalCall)] get; } +} +//we will add an IOS class used for detours once we make them good \ No newline at end of file From 8454fb0025ac9544ec88c029df703305c804e548 Mon Sep 17 00:00:00 2001 From: "Melvin \"Dumbass\" Shwuaner" <83349478+MelvinShwuaner@users.noreply.github.com> Date: Tue, 10 Nov 2026 18:49:09 +0300 Subject: [PATCH 09/14] fix --- resources/commit | 2 +- services/NativeLibraryManager.cs | 16 ++++------------ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/resources/commit b/resources/commit index 0adda88..0ce6225 100644 --- a/resources/commit +++ b/resources/commit @@ -1 +1 @@ -48443abae34350efacbb670f3199fea1735d7a58 +919ad111295239c1e55bae042df1b500623f1385 diff --git a/services/NativeLibraryManager.cs b/services/NativeLibraryManager.cs index 573745d..fe23f9b 100644 --- a/services/NativeLibraryManager.cs +++ b/services/NativeLibraryManager.cs @@ -34,18 +34,14 @@ public static class NativeLibraryManager private static extern IntPtr LinuxDlError(); // Android - [DllImport("libdl.so", EntryPoint = "dlopen", + [DllImport("libmain.so", EntryPoint = "load_library", CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr AndroidDlOpen(string filename, int flags); + private static extern IntPtr AndroidDlOpen(string filename); [DllImport("libdl.so", EntryPoint = "dlsym", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr AndroidDlSym(IntPtr handle, string symbol); - [DllImport("libdl.so", EntryPoint = "dlerror", - CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr AndroidDlError(); - private const int OPEN_FLAGS = 2 | 0x100; delegate IntPtr loadlib(string Path, out string Error); @@ -114,12 +110,8 @@ static NativeLibraryManager() GetAddress = AndroidDlSym; Loadlib = (path, out error) => { - error = null; - IntPtr handle = AndroidDlOpen(path, OPEN_FLAGS); - if (handle == IntPtr.Zero) - { - error = Marshal.PtrToStringAnsi(AndroidDlError()); - } + error = ""; //it already logs the error inside libmain + IntPtr handle = AndroidDlOpen(path); return handle; }; break; From 2d7a4a3ab8bb95241b1df582a2a479ae80819fbc Mon Sep 17 00:00:00 2001 From: "Melvin \"Dumbass\" Shwuaner" <83349478+MelvinShwuaner@users.noreply.github.com> Date: Tue, 10 Nov 2026 20:05:38 +0300 Subject: [PATCH 10/14] fix nmlapi --- general/RF.cs | 8 ++++++++ resources/commit | 2 +- services/NativeLibraryManager.cs | 34 +++++++++++++++++++++++--------- utils/ReflectionHelper.cs | 6 +++++- 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/general/RF.cs b/general/RF.cs index 051c316..e27c85a 100644 --- a/general/RF.cs +++ b/general/RF.cs @@ -45,11 +45,19 @@ public static Delegate GetMethodDelegate(this Type type, string name, bool is_st return method; } var newMethod1 = ReflectionHelper.GetMethod(type, name, is_static); + if (newMethod1 == null) + { + return null; + } methods.Add(name, newMethod1); return newMethod1; } var newMethod = ReflectionHelper.GetMethod(type, name, is_static); + if (newMethod == null) + { + return null; + } _method_cache.Add(type, new Dictionary { { name, newMethod } }); return newMethod; } diff --git a/resources/commit b/resources/commit index 0ce6225..945849a 100644 --- a/resources/commit +++ b/resources/commit @@ -1 +1 @@ -919ad111295239c1e55bae042df1b500623f1385 +8454fb0025ac9544ec88c029df703305c804e548 diff --git a/services/NativeLibraryManager.cs b/services/NativeLibraryManager.cs index fe23f9b..07a1f50 100644 --- a/services/NativeLibraryManager.cs +++ b/services/NativeLibraryManager.cs @@ -1,5 +1,6 @@ using System.Reflection; using NeoModLoader.constants; +using NeoModLoader.General; using Newtonsoft.Json; namespace NeoModLoader.services; using System; @@ -219,25 +220,40 @@ private static List GetOrder( } } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - delegate bool NativeModEntry (IntPtr NMLAPI); + delegate IntPtr NMLAPIFunc( + [MarshalAs(UnmanagedType.LPUTF8Str)] string signature + ); - static IntPtr NMLAPI(string type, string method) + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + delegate bool NativeModEntry( [MarshalAs(UnmanagedType.FunctionPtr)] NMLAPIFunc NMLAPI); + + static ConcurrentDictionary Delegates = new(); + static IntPtr NMLAPI(string signature) { - Assembly NML = typeof(WorldBoxMod).Assembly; - Type Type = NML.GetType(type); - if (Type == null) + Assembly NML = WorldBoxMod.NeoModLoaderAssembly; + string[] args = signature.Split(':'); + Type Type = NML.GetType(args[0]); + Delegate Method = Type?.GetMethodDelegate(args[1], true); + if (Method == null) { return IntPtr.Zero; } - MethodInfo Method = Type.GetMethod(method, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); - return Method == null ? IntPtr.Zero : Method.MethodHandle.GetFunctionPointer(); + if (Delegates.TryGetValue(Method, out var lib)) + { + return lib; + } + + IntPtr ptr = Marshal.GetFunctionPointerForDelegate(Method); + Delegates.TryAdd(Method, ptr); + return ptr; } + static readonly NMLAPIFunc NMLAPICallback = NMLAPI; /// - /// native libraries which act as mods may use the NML API for assistance. the entry point is always bool ModEntry(IntPtr NMLAPIFUNC) where NMLAPIFUNC is IntPtr (string type, string method). this returns a neomodloader function which may be used + /// native libraries which act as mods may use the NML API for assistance. the entry point is always bool ModEntry(IntPtr NMLAPIFUNC) where NMLAPIFUNC is IntPtr (string type, string method). this returns a neomodloader static function which may be used /// public static bool LoadNativeMod(string Name) { NativeModEntry EntryPoint = GetNativeFunction(Name, "ModEntry"); - return EntryPoint(Marshal.GetFunctionPointerForDelegate(NMLAPI)); + return EntryPoint(NMLAPICallback); } } \ No newline at end of file diff --git a/utils/ReflectionHelper.cs b/utils/ReflectionHelper.cs index ea07b18..dabae19 100644 --- a/utils/ReflectionHelper.cs +++ b/utils/ReflectionHelper.cs @@ -21,7 +21,7 @@ internal static Delegate GetMethod(string method_name, bool is_static = false internal static Delegate GetMethod(Type type, string method_name, bool is_static = false) { return createMethodDelegate(is_static - ? type.GetMethod(method_name, BindingFlags.Static | BindingFlags.NonPublic) + ? type.GetMethod(method_name, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public) : AccessTools.Method(type, method_name)); } @@ -138,6 +138,10 @@ internal static Action CreateFieldSetter(string field_name) private static Delegate createMethodDelegate(MethodInfo method_info) { + if (method_info == null) + { + return null; + } List paramExpressions = method_info.GetParameters() .Select((p, i) => Expression.Parameter(p.ParameterType, p.Name)).ToList(); From 18dc6eb527b5152d631f4d69554b8461aba0495a Mon Sep 17 00:00:00 2001 From: "Melvin \"Dumbass\" Shwuaner" <83349478+MelvinShwuaner@users.noreply.github.com> Date: Tue, 10 Nov 2026 20:33:54 +0300 Subject: [PATCH 11/14] Update NativeLibraryManager.cs --- services/NativeLibraryManager.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/services/NativeLibraryManager.cs b/services/NativeLibraryManager.cs index 07a1f50..d693fe2 100644 --- a/services/NativeLibraryManager.cs +++ b/services/NativeLibraryManager.cs @@ -1,4 +1,5 @@ using System.Reflection; +using NeoModLoader.api; using NeoModLoader.constants; using NeoModLoader.General; using Newtonsoft.Json; @@ -225,7 +226,7 @@ delegate IntPtr NMLAPIFunc( ); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - delegate bool NativeModEntry( [MarshalAs(UnmanagedType.FunctionPtr)] NMLAPIFunc NMLAPI); + delegate bool NativeModEntry( [MarshalAs(UnmanagedType.FunctionPtr)] NMLAPIFunc NMLAPI, [MarshalAs(UnmanagedType.U8)] ModDeclare Declare); static ConcurrentDictionary Delegates = new(); static IntPtr NMLAPI(string signature) @@ -249,11 +250,12 @@ static IntPtr NMLAPI(string signature) } static readonly NMLAPIFunc NMLAPICallback = NMLAPI; /// - /// native libraries which act as mods may use the NML API for assistance. the entry point is always bool ModEntry(IntPtr NMLAPIFUNC) where NMLAPIFUNC is IntPtr (string type, string method). this returns a neomodloader static function which may be used + /// native libraries which act as mods may use the NML API for assistance. the entry point is always bool ModEntry(IntPtr NMLAPIFUNC) /// - public static bool LoadNativeMod(string Name) + /// NMLAPIFUNC is IntPtr (string signature). this returns a neomodloader static function which may be used + public static bool LoadNativeMod(string Name, ModDeclare Mod) { NativeModEntry EntryPoint = GetNativeFunction(Name, "ModEntry"); - return EntryPoint(NMLAPICallback); + return EntryPoint(NMLAPICallback, Mod); } } \ No newline at end of file From 734fb8182c04674efbee244be70a9c6ab1a3c080 Mon Sep 17 00:00:00 2001 From: "Melvin \"Dumbass\" Shwuaner" <83349478+MelvinShwuaner@users.noreply.github.com> Date: Tue, 10 Nov 2026 20:37:21 +0300 Subject: [PATCH 12/14] Update Paths.cs --- constants/Paths.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants/Paths.cs b/constants/Paths.cs index bc27edf..86390db 100644 --- a/constants/Paths.cs +++ b/constants/Paths.cs @@ -109,7 +109,7 @@ public static class Paths public static readonly string ModDefaultConfigFileName = "default_config.json"; /// - /// Folder name of a mod's resource folder which will be patched to + /// Folder name of a mods native libraries /// public static readonly string NativeLibrariesFolder = "Native"; /// From 9902208ed4b6c31c3f1cf0d57235c288afaa7a19 Mon Sep 17 00:00:00 2001 From: "Melvin \"Dumbass\" Shwuaner" <83349478+MelvinShwuaner@users.noreply.github.com> Date: Tue, 10 Nov 2026 20:47:03 +0300 Subject: [PATCH 13/14] Update NativeLibraryManager.cs --- services/NativeLibraryManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/NativeLibraryManager.cs b/services/NativeLibraryManager.cs index d693fe2..ef28551 100644 --- a/services/NativeLibraryManager.cs +++ b/services/NativeLibraryManager.cs @@ -226,7 +226,7 @@ delegate IntPtr NMLAPIFunc( ); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - delegate bool NativeModEntry( [MarshalAs(UnmanagedType.FunctionPtr)] NMLAPIFunc NMLAPI, [MarshalAs(UnmanagedType.U8)] ModDeclare Declare); + delegate bool NativeModEntry( [MarshalAs(UnmanagedType.FunctionPtr)] NMLAPIFunc NMLAPI, [MarshalAs(UnmanagedType.LPUTF8Str)] string DeclareID); static ConcurrentDictionary Delegates = new(); static IntPtr NMLAPI(string signature) @@ -256,6 +256,6 @@ static IntPtr NMLAPI(string signature) public static bool LoadNativeMod(string Name, ModDeclare Mod) { NativeModEntry EntryPoint = GetNativeFunction(Name, "ModEntry"); - return EntryPoint(NMLAPICallback, Mod); + return EntryPoint(NMLAPICallback, Mod.UID); } } \ No newline at end of file From 07036ef598b49dba325559e6b6eba8a23ca3e1df Mon Sep 17 00:00:00 2001 From: "Melvin \"Dumbass\" Shwuaner" <83349478+MelvinShwuaner@users.noreply.github.com> Date: Wed, 11 Nov 2026 21:20:56 +0300 Subject: [PATCH 14/14] actually screw that --- resources/commit | 2 +- services/NativeLibraryManager.cs | 38 -------------------------------- 2 files changed, 1 insertion(+), 39 deletions(-) diff --git a/resources/commit b/resources/commit index 945849a..5420a16 100644 --- a/resources/commit +++ b/resources/commit @@ -1 +1 @@ -8454fb0025ac9544ec88c029df703305c804e548 +9902208ed4b6c31c3f1cf0d57235c288afaa7a19 diff --git a/services/NativeLibraryManager.cs b/services/NativeLibraryManager.cs index ef28551..06a4c10 100644 --- a/services/NativeLibraryManager.cs +++ b/services/NativeLibraryManager.cs @@ -220,42 +220,4 @@ private static List GetOrder( "Failed to read order.json.", ex); } } - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - delegate IntPtr NMLAPIFunc( - [MarshalAs(UnmanagedType.LPUTF8Str)] string signature - ); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - delegate bool NativeModEntry( [MarshalAs(UnmanagedType.FunctionPtr)] NMLAPIFunc NMLAPI, [MarshalAs(UnmanagedType.LPUTF8Str)] string DeclareID); - - static ConcurrentDictionary Delegates = new(); - static IntPtr NMLAPI(string signature) - { - Assembly NML = WorldBoxMod.NeoModLoaderAssembly; - string[] args = signature.Split(':'); - Type Type = NML.GetType(args[0]); - Delegate Method = Type?.GetMethodDelegate(args[1], true); - if (Method == null) - { - return IntPtr.Zero; - } - if (Delegates.TryGetValue(Method, out var lib)) - { - return lib; - } - - IntPtr ptr = Marshal.GetFunctionPointerForDelegate(Method); - Delegates.TryAdd(Method, ptr); - return ptr; - } - static readonly NMLAPIFunc NMLAPICallback = NMLAPI; - /// - /// native libraries which act as mods may use the NML API for assistance. the entry point is always bool ModEntry(IntPtr NMLAPIFUNC) - /// - /// NMLAPIFUNC is IntPtr (string signature). this returns a neomodloader static function which may be used - public static bool LoadNativeMod(string Name, ModDeclare Mod) - { - NativeModEntry EntryPoint = GetNativeFunction(Name, "ModEntry"); - return EntryPoint(NMLAPICallback, Mod.UID); - } } \ No newline at end of file