From af1daafb9627e59d721ef32041b1e0a2f8f1f03e Mon Sep 17 00:00:00 2001 From: Chloe Christley <734133+chloelcdev@users.noreply.github.com> Date: Sun, 27 Apr 2025 14:07:11 -0400 Subject: [PATCH 01/16] Add Dependencies for AssetBundle things (MelonLoaderAssembliesPath) \UnityEngine.Il2CppAssetBundleManager.dll (MonoAssembliesPath) \UnityEngine.AssetBundleModule.dll --- S1API/S1API.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/S1API/S1API.csproj b/S1API/S1API.csproj index a28f1c2d..f9460115 100644 --- a/S1API/S1API.csproj +++ b/S1API/S1API.csproj @@ -41,6 +41,7 @@ + From 064053b261a1333ab4ed0d876de2165fc8d0e0df Mon Sep 17 00:00:00 2001 From: Chloe Christley <734133+chloelcdev@users.noreply.github.com> Date: Sun, 27 Apr 2025 14:09:00 -0400 Subject: [PATCH 02/16] Create AssetBundleHelper.cs --- S1API/AssetBundles/AssetBundleHelper.cs | 54 +++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 S1API/AssetBundles/AssetBundleHelper.cs diff --git a/S1API/AssetBundles/AssetBundleHelper.cs b/S1API/AssetBundles/AssetBundleHelper.cs new file mode 100644 index 00000000..9bc95837 --- /dev/null +++ b/S1API/AssetBundles/AssetBundleHelper.cs @@ -0,0 +1,54 @@ +using System; +using System.IO; +using System.Reflection; +using UnityEngine; + + +namespace S1API.AssetBundles +{ + + public static class AssetBundleHelper + { +#if IL2CPP + /// + /// Loads an Il2Cpp AssetBundle from an embedded resource stream by name. + /// + /// The full embedded resource name (including namespace path). + /// The loaded Il2CppAssetBundle, or throws on failure. + public static Il2CppAssetBundle GetAssetBundleFromStream(string resourceName) + { + // Attempt to find the embedded resource in the executing assembly + var assembly = Assembly.GetExecutingAssembly(); + using (Stream stream = assembly.GetManifestResourceStream(resourceName)) + { + if (stream == null) + throw new Exception($"Embedded resource '{resourceName}' not found in {assembly.FullName}."); // hoping these throws will be melon/bepinex-agnostic + + // Read the stream into a byte array + byte[] data = new byte[stream.Length]; + stream.Read(data, 0, data.Length); + + // Load the AssetBundle from memory + var bundle = Il2CppAssetBundleManager.LoadFromMemory(data); + if (bundle == null) + throw new Exception($"Failed to load AssetBundle from memory: {resourceName}"); + + return bundle; + } + } +#else + public static AssetBundle GetAssetBundleFromStream(string resourceName) + { + // Attempt to find the embedded resource in the executing assembly + var assembly = Assembly.GetExecutingAssembly(); + + var stream = assembly.GetManifestResourceStream(resourceName); + + return AssetBundle.LoadFromStream(stream); + } +#endif + + + } + +} From b60479f030b8ac8f5b89ff6984af4e0393274d4d Mon Sep 17 00:00:00 2001 From: Chloe Christley <734133+chloelcdev@users.noreply.github.com> Date: Sun, 27 Apr 2025 12:25:20 -0400 Subject: [PATCH 03/16] Turned AssetBundleHelper into AssetLoader, added WrappedAssetBundle also added WrappedAssetBundleRequest --- S1API/AssetBundles/AssetBundleHelper.cs | 54 ------- S1API/AssetBundles/AssetLoader.cs | 94 +++++++++++ S1API/AssetBundles/WrappedAssetBundle.cs | 192 +++++++++++++++++++++++ 3 files changed, 286 insertions(+), 54 deletions(-) delete mode 100644 S1API/AssetBundles/AssetBundleHelper.cs create mode 100644 S1API/AssetBundles/AssetLoader.cs create mode 100644 S1API/AssetBundles/WrappedAssetBundle.cs diff --git a/S1API/AssetBundles/AssetBundleHelper.cs b/S1API/AssetBundles/AssetBundleHelper.cs deleted file mode 100644 index 9bc95837..00000000 --- a/S1API/AssetBundles/AssetBundleHelper.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.IO; -using System.Reflection; -using UnityEngine; - - -namespace S1API.AssetBundles -{ - - public static class AssetBundleHelper - { -#if IL2CPP - /// - /// Loads an Il2Cpp AssetBundle from an embedded resource stream by name. - /// - /// The full embedded resource name (including namespace path). - /// The loaded Il2CppAssetBundle, or throws on failure. - public static Il2CppAssetBundle GetAssetBundleFromStream(string resourceName) - { - // Attempt to find the embedded resource in the executing assembly - var assembly = Assembly.GetExecutingAssembly(); - using (Stream stream = assembly.GetManifestResourceStream(resourceName)) - { - if (stream == null) - throw new Exception($"Embedded resource '{resourceName}' not found in {assembly.FullName}."); // hoping these throws will be melon/bepinex-agnostic - - // Read the stream into a byte array - byte[] data = new byte[stream.Length]; - stream.Read(data, 0, data.Length); - - // Load the AssetBundle from memory - var bundle = Il2CppAssetBundleManager.LoadFromMemory(data); - if (bundle == null) - throw new Exception($"Failed to load AssetBundle from memory: {resourceName}"); - - return bundle; - } - } -#else - public static AssetBundle GetAssetBundleFromStream(string resourceName) - { - // Attempt to find the embedded resource in the executing assembly - var assembly = Assembly.GetExecutingAssembly(); - - var stream = assembly.GetManifestResourceStream(resourceName); - - return AssetBundle.LoadFromStream(stream); - } -#endif - - - } - -} diff --git a/S1API/AssetBundles/AssetLoader.cs b/S1API/AssetBundles/AssetLoader.cs new file mode 100644 index 00000000..4f0da8da --- /dev/null +++ b/S1API/AssetBundles/AssetLoader.cs @@ -0,0 +1,94 @@ +using System; +using System.IO; +using System.Reflection; +using UnityEngine; + +namespace S1API.AssetBundles +{ + + public static class AssetLoader + { +#if IL2CPP + /// + /// Loads an Il2Cpp AssetBundle from an embedded resource stream by name. + /// + /// The full embedded resource name (including namespace path). + /// The loaded Il2CppAssetBundle, or throws on failure. + public static WrappedAssetBundle GetAssetBundleFromStream(string fullResourceName) + { + try + { + // Attempt to find the embedded resource in the executing assembly + var assembly = Assembly.GetExecutingAssembly(); + using (Stream stream = assembly.GetManifestResourceStream(fullResourceName)) + { + if (stream == null) + throw new Exception($"Embedded resource '{fullResourceName}' not found in {assembly.FullName}."); // hoping these throws will be melon/bepinex-agnostic + + // Read the stream into a byte array + byte[] data = new byte[stream.Length]; + stream.Read(data, 0, data.Length); + + // Load the AssetBundle from memory + Il2CppAssetBundle bundle = Il2CppAssetBundleManager.LoadFromMemory(data); + if (bundle == null) + throw new Exception($"Failed to load AssetBundle from memory: {fullResourceName}"); + + return new(bundle); + } + } + catch (Exception ex) + { + // Handle exceptions as needed + Debug.LogError($"Error loading AssetBundle from stream: {ex.Message}"); + throw; + } + } +#else + public static WrappedAssetBundle GetAssetBundleFromStream(string fullResourceName) + { + // Attempt to find the embedded resource in the executing assembly + var assembly = Assembly.GetExecutingAssembly(); + + var stream = assembly.GetManifestResourceStream(fullResourceName); + + return new(AssetBundle.LoadFromStream(stream)); + } +#endif + + /// + /// + /// No need to type the assembly just the path stuff. + /// + /// Example if carassetbundle is in subfolder /bundles/: + /// + /// GameObject myCar = Instantiate(EasyLoad("bundles.carassetbundle", "MyCarGameObject")); + /// + /// + public static T EasyLoad(string bundle_name, string object_name) where T : UnityEngine.Object + { + return EasyLoad(bundle_name, object_name, Assembly.GetExecutingAssembly(), out _); + } + + public static T EasyLoad(string bundle_name, string object_name, out WrappedAssetBundle bundle) where T : UnityEngine.Object + { + return EasyLoad(bundle_name, object_name, Assembly.GetExecutingAssembly(), out bundle); + } + + public static T EasyLoad(string bundle_name, string object_name, Assembly assemblyOverride) where T : UnityEngine.Object + { + return EasyLoad(bundle_name, object_name, assemblyOverride, out _); + } + + public static T EasyLoad(string bundle_name, string object_name, Assembly assemblyOverride, out WrappedAssetBundle bundle) where T : UnityEngine.Object + { + // Get the asset bundle from the assembly + bundle = GetAssetBundleFromStream($"{assemblyOverride.GetName().Name}.{bundle_name}"); + + // Load the asset from the bundle + return bundle.LoadAsset(object_name); + } + + } + +} diff --git a/S1API/AssetBundles/WrappedAssetBundle.cs b/S1API/AssetBundles/WrappedAssetBundle.cs new file mode 100644 index 00000000..bc5cbcfc --- /dev/null +++ b/S1API/AssetBundles/WrappedAssetBundle.cs @@ -0,0 +1,192 @@ +using UnityEngine; + +#if IL2CPP +using Type = Il2CppSystem.Type; +using AssetBundle = UnityEngine.Il2CppAssetBundle; +using AssetBundleRequest = UnityEngine.Il2CppAssetBundleRequest; +#endif + + +namespace S1API.AssetBundles +{ + + /// + /// Works just like the AssetBundle type, it's just a proxy, but will use Il2CppAssetBundle if it needs to so you don't have to worry about it. + /// + public class WrappedAssetBundle + { + public bool isStreamedAssetBundle => _realBundle.isStreamedSceneAssetBundle; + + public Object mainAsset => _realBundle.mainAsset; + + + public AssetBundle _realBundle; + + public WrappedAssetBundle(AssetBundle realBundle) + { + _realBundle = realBundle; + } + + + public bool Contains(string name) + { + return _realBundle.Contains(name); + } + + public string[] AllAssetNames() + { + return GetAllAssetNames(); + } + + public string[] GetAllAssetNames() + { + return _realBundle.GetAllAssetNames(); + } + + public string[] AllScenePaths() + { + return GetAllScenePaths(); + } + + public string[] GetAllScenePaths() + { + return _realBundle.GetAllScenePaths(); + } + + public Object Load(string name) + { + return LoadAsset(name); + } + + public Object LoadAsset(string name) + { + return this.LoadAsset(name); + } + + public T Load(string name) where T : Object + { + return LoadAsset(name); + } + + public T LoadAsset(string name) where T : Object + { + return _realBundle.LoadAsset(name); + } + + public Object Load(string name, Type type) + { + return LoadAsset(name, type); + } + + public Object LoadAsset(string name, Type type) + { + return _realBundle.LoadAsset(name, type); + } + + public WrappedAssetBundleRequest LoadAssetAsync(string name) + { + return this.LoadAssetAsync(name); + } + + public WrappedAssetBundleRequest LoadAssetAsync(string name) where T : Object + { + return new(_realBundle.LoadAssetAsync(name)); + } + + public WrappedAssetBundleRequest LoadAssetAsync(string name, Type type) + { + return new(_realBundle.LoadAssetAsync(name, type)); + } + + public Object[] LoadAll() + { + return LoadAllAssets(); + } + + public Object[] LoadAllAssets() + { + return this.LoadAllAssets(); + } + + public T[] LoadAllAssets() where T : Object + { + return _realBundle.LoadAllAssets(); + } + + public Object[] LoadAllAssets(Type type) + { + return _realBundle.LoadAllAssets(type); + } + + public Object[] LoadAssetWithSubAssets(string name) + { + return this.LoadAssetWithSubAssets(name); + } + + public T[] LoadAssetWithSubAssets(string name) where T : Object + { + return _realBundle.LoadAssetWithSubAssets(name); + } + + public Object[] LoadAssetWithSubAssets(string name, Type type) + { + return _realBundle.LoadAssetWithSubAssets(name, type); + } + + public void Unload(bool unloadAllLoadedObjects) + { + _realBundle.Unload(unloadAllLoadedObjects); + } + } + + + /// + /// Works just like the AssetBundleRequest type, it's just a proxy, but will use Il2CppAssetBundleRequest if it needs to so you don't have to worry about it. + /// + public class WrappedAssetBundleRequest + { + public AssetBundleRequest _realRequest; + + public WrappedAssetBundleRequest(AssetBundleRequest realRequest) + { + _realRequest = realRequest; + } + + public Object asset => _realRequest.asset; + + public Object[] allAssets => _realRequest.allAssets; + } + +} + +/* Might need this if the above fails + + +public static class Il2CppStringArrayExtensions +{ + public static string[] ToManagedArray(this Il2CppStringArray il2cppStrings) + { + string[] managedStrings = new string[il2cppStrings.Length]; + for (int i = 0; i < il2cppStrings.Length; i++) + { + managedStrings[i] = il2cppStrings[i]; + } + return managedStrings; + } +} + +public static class Il2CppObjectArrayExtensions +{ + public static T[] ToManagedArray(this Il2CppReferenceArray il2cppObjects) where T : Object + { + T[] managedObjects = new T[il2cppObjects.Length]; + for (int i = 0; i < il2cppObjects.Length; i++) + { + managedObjects[i] = il2cppObjects[i]; + } + return managedObjects; + } +} +*/ + + From c9564cc20eba7f64fd45892e44f4c4733e447202 Mon Sep 17 00:00:00 2001 From: Chloe Christley <734133+chloelcdev@users.noreply.github.com> Date: Sun, 27 Apr 2025 12:33:09 -0400 Subject: [PATCH 04/16] Explicitly specify type for mono --- S1API/AssetBundles/WrappedAssetBundle.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/S1API/AssetBundles/WrappedAssetBundle.cs b/S1API/AssetBundles/WrappedAssetBundle.cs index bc5cbcfc..45368a27 100644 --- a/S1API/AssetBundles/WrappedAssetBundle.cs +++ b/S1API/AssetBundles/WrappedAssetBundle.cs @@ -4,6 +4,8 @@ using Type = Il2CppSystem.Type; using AssetBundle = UnityEngine.Il2CppAssetBundle; using AssetBundleRequest = UnityEngine.Il2CppAssetBundleRequest; +#else +using Type = System.Type; #endif From a29dec2d19411fbf8047f7432fd8ec2cec4d1a9f Mon Sep 17 00:00:00 2001 From: Chloe Christley <734133+chloelcdev@users.noreply.github.com> Date: Sun, 27 Apr 2025 12:33:29 -0400 Subject: [PATCH 05/16] remove deprecated "mainAsset" from WrappedAssetBundle --- S1API/AssetBundles/WrappedAssetBundle.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/S1API/AssetBundles/WrappedAssetBundle.cs b/S1API/AssetBundles/WrappedAssetBundle.cs index 45368a27..cd7b8b8e 100644 --- a/S1API/AssetBundles/WrappedAssetBundle.cs +++ b/S1API/AssetBundles/WrappedAssetBundle.cs @@ -19,9 +19,6 @@ public class WrappedAssetBundle { public bool isStreamedAssetBundle => _realBundle.isStreamedSceneAssetBundle; - public Object mainAsset => _realBundle.mainAsset; - - public AssetBundle _realBundle; public WrappedAssetBundle(AssetBundle realBundle) From 6725b0162d75128540f441667ab72fab7e3a6bcc Mon Sep 17 00:00:00 2001 From: Chloe Christley <734133+chloelcdev@users.noreply.github.com> Date: Sun, 27 Apr 2025 12:55:58 -0400 Subject: [PATCH 06/16] Add back line I guess I deleted? always use UnityEngine.Object in WrappedAssetBundle --- S1API/AssetBundles/WrappedAssetBundle.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/S1API/AssetBundles/WrappedAssetBundle.cs b/S1API/AssetBundles/WrappedAssetBundle.cs index cd7b8b8e..0ffe4e2c 100644 --- a/S1API/AssetBundles/WrappedAssetBundle.cs +++ b/S1API/AssetBundles/WrappedAssetBundle.cs @@ -8,6 +8,8 @@ using Type = System.Type; #endif +using Object = UnityEngine.Object; + namespace S1API.AssetBundles { From ebf36571682fe98d3c44002731dc47457248ac1a Mon Sep 17 00:00:00 2001 From: Chloe Christley <734133+chloelcdev@users.noreply.github.com> Date: Sun, 27 Apr 2025 13:06:48 -0400 Subject: [PATCH 07/16] take away try/catch --- S1API/AssetBundles/AssetLoader.cs | 38 ++++++++++-------------- S1API/AssetBundles/WrappedAssetBundle.cs | 16 ++++++++++ 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/S1API/AssetBundles/AssetLoader.cs b/S1API/AssetBundles/AssetLoader.cs index 4f0da8da..449cfae3 100644 --- a/S1API/AssetBundles/AssetLoader.cs +++ b/S1API/AssetBundles/AssetLoader.cs @@ -16,32 +16,26 @@ public static class AssetLoader /// The loaded Il2CppAssetBundle, or throws on failure. public static WrappedAssetBundle GetAssetBundleFromStream(string fullResourceName) { - try + // Attempt to find the embedded resource in the executing assembly + var assembly = Assembly.GetExecutingAssembly(); + using (Stream stream = assembly.GetManifestResourceStream(fullResourceName)) { - // Attempt to find the embedded resource in the executing assembly - var assembly = Assembly.GetExecutingAssembly(); - using (Stream stream = assembly.GetManifestResourceStream(fullResourceName)) - { - if (stream == null) - throw new Exception($"Embedded resource '{fullResourceName}' not found in {assembly.FullName}."); // hoping these throws will be melon/bepinex-agnostic + if (stream == null) + throw new Exception($"Embedded resource '{fullResourceName}' not found in {assembly.FullName}."); // hoping these throws will be melon/bepinex-agnostic - // Read the stream into a byte array - byte[] data = new byte[stream.Length]; - stream.Read(data, 0, data.Length); + // Read the stream into a byte array + byte[] data = new byte[stream.Length]; + stream.Read(data, 0, data.Length); - // Load the AssetBundle from memory - Il2CppAssetBundle bundle = Il2CppAssetBundleManager.LoadFromMemory(data); - if (bundle == null) - throw new Exception($"Failed to load AssetBundle from memory: {fullResourceName}"); + // Load the AssetBundle from memory + Il2CppAssetBundle bundle = Il2CppAssetBundleManager.LoadFromMemory(data); - return new(bundle); - } - } - catch (Exception ex) - { - // Handle exceptions as needed - Debug.LogError($"Error loading AssetBundle from stream: {ex.Message}"); - throw; + if (bundle == null){ + MelonLoader.Logger.Error($"Failed to load AssetBundle from memory: {fullResourceName}"); + throw new Exception($"Failed to load AssetBundle from memory: {fullResourceName}"); + } + + return new(bundle); } } #else diff --git a/S1API/AssetBundles/WrappedAssetBundle.cs b/S1API/AssetBundles/WrappedAssetBundle.cs index 0ffe4e2c..fe5989fb 100644 --- a/S1API/AssetBundles/WrappedAssetBundle.cs +++ b/S1API/AssetBundles/WrappedAssetBundle.cs @@ -191,3 +191,19 @@ public static T[] ToManagedArray(this Il2CppReferenceArray il2cppObjects) */ +/* + * + * [12:57:44.463] [ManorMod] Unhandled exception in coroutine. It will not continue executing. +System.MissingMethodException: Method not found: 'UnityEngine.AssetBundle UnityEngine.AssetBundle.LoadFromStream(System.IO.Stream)'. + at S1API.AssetBundles.AssetLoader.GetAssetBundleFromStream(String fullResourceName) + at S1API.AssetBundles.AssetLoader.EasyLoad[T](String bundle_name, String object_name, Assembly assemblyOverride, WrappedAssetBundle& bundle) + at S1API.AssetBundles.AssetLoader.EasyLoad[T](String bundle_name, String object_name) + at ManorMod.Core.LoadAssetBundle()+MoveNext() + at MelonLoader.Support.MonoEnumeratorWrapper.MoveNext() in D:\a\MelonLoader\MelonLoader\Dependencies\SupportModules\Il2Cpp\MonoEnumeratorWrapper.cs:line 39 +[12:57:48.792] [ManorMod] System.MissingMethodException: Method not found: 'Void UnityEngine.Events.UnityAction..ctor(System.Object, IntPtr)'. + at ManorMod.Core.OnLateInitializeMelon() + at MelonLoader.MelonEvent.<>c.b__1_0(LemonAction x) in D:\a\MelonLoader\MelonLoader\MelonLoader\Melons\Events\MelonEvent.cs:line 174 + at MelonLoader.MelonEventBase`1.Invoke(Action`1 delegateInvoker) in D:\a\MelonLoader\MelonLoader\MelonLoader\Melons\Events\MelonEvent.cs:line 143 + + +*/ \ No newline at end of file From bfe572d802f3b89cd70924e21435dfc09ef2425a Mon Sep 17 00:00:00 2001 From: Chloe Christley <734133+chloelcdev@users.noreply.github.com> Date: Sun, 27 Apr 2025 14:21:40 -0400 Subject: [PATCH 08/16] Fix quotation marks in csproj --- S1API/S1API.csproj | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/S1API/S1API.csproj b/S1API/S1API.csproj index f9460115..8329d9f2 100644 --- a/S1API/S1API.csproj +++ b/S1API/S1API.csproj @@ -37,16 +37,16 @@ - - - - - + + + + + - - - - + + + + From 80fa2070f8fb6a9575a8da3ad2c2cabb7a30fa62 Mon Sep 17 00:00:00 2001 From: Chloe Christley <734133+chloelcdev@users.noreply.github.com> Date: Wed, 30 Apr 2025 10:06:24 -0400 Subject: [PATCH 09/16] Update AssetLoader.cs --- S1API/AssetBundles/AssetLoader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/S1API/AssetBundles/AssetLoader.cs b/S1API/AssetBundles/AssetLoader.cs index 449cfae3..1ceb1e05 100644 --- a/S1API/AssetBundles/AssetLoader.cs +++ b/S1API/AssetBundles/AssetLoader.cs @@ -8,7 +8,7 @@ namespace S1API.AssetBundles public static class AssetLoader { -#if IL2CPP +#if IL2CPPMELON /// /// Loads an Il2Cpp AssetBundle from an embedded resource stream by name. /// @@ -38,7 +38,7 @@ public static WrappedAssetBundle GetAssetBundleFromStream(string fullResourceNa return new(bundle); } } -#else +#elif MONOMELON public static WrappedAssetBundle GetAssetBundleFromStream(string fullResourceName) { // Attempt to find the embedded resource in the executing assembly From 4cd97fdb100662ff2ee177b04388ec88729e2c2a Mon Sep 17 00:00:00 2001 From: Chloe Christley <734133+chloelcdev@users.noreply.github.com> Date: Thu, 1 May 2025 03:59:24 -0400 Subject: [PATCH 10/16] Create UpdateBepInExBundleManager.bat --- .../UpdateBepInExBundleManager.bat | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 S1API/UnityEngine.BE.Il2CppAssetBundleManager/UpdateBepInExBundleManager.bat diff --git a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/UpdateBepInExBundleManager.bat b/S1API/UnityEngine.BE.Il2CppAssetBundleManager/UpdateBepInExBundleManager.bat new file mode 100644 index 00000000..e8399009 --- /dev/null +++ b/S1API/UnityEngine.BE.Il2CppAssetBundleManager/UpdateBepInExBundleManager.bat @@ -0,0 +1,23 @@ +@echo off +chcp 65001 +setlocal + +REM Base URL for raw files on GitHub (master branch) +set "BASE=https://raw.githubusercontent.com/xmusjackson/UnityEngine.BE.Il2CppAssetBundleManager/master" + +REM Space-separated list of files to download +set "FILES=Il2CppAssetBundle.cs Il2CppAssetBundleManager.cs Il2CppAssetBundleRequest.cs InteropUtils.cs README.md LICENSE.txt NOTICE.md" + +for %%F in (%FILES%) do ( + echo ── Downloading %%F + curl -sSL "%BASE%/%%F" -o "%%F" + if errorlevel 1 ( + echo ⚠️ Failed to download %%F + ) else ( + echo ✅ Saved %%F + ) +) + +echo. +echo All done! +pause From 464a8b64437613297b0a279aa16975add1ca60c6 Mon Sep 17 00:00:00 2001 From: Chloe Christley <734133+chloelcdev@users.noreply.github.com> Date: Thu, 1 May 2025 04:13:13 -0400 Subject: [PATCH 11/16] Have batch file wrap #if IL2CPPBEPINEX tags --- .../UpdateBepInExBundleManager.bat | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/UpdateBepInExBundleManager.bat b/S1API/UnityEngine.BE.Il2CppAssetBundleManager/UpdateBepInExBundleManager.bat index e8399009..8129d5b9 100644 --- a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/UpdateBepInExBundleManager.bat +++ b/S1API/UnityEngine.BE.Il2CppAssetBundleManager/UpdateBepInExBundleManager.bat @@ -1,23 +1,42 @@ @echo off -chcp 65001 -setlocal +setlocal enabledelayedexpansion REM Base URL for raw files on GitHub (master branch) set "BASE=https://raw.githubusercontent.com/xmusjackson/UnityEngine.BE.Il2CppAssetBundleManager/master" -REM Space-separated list of files to download +REM List of files to download set "FILES=Il2CppAssetBundle.cs Il2CppAssetBundleManager.cs Il2CppAssetBundleRequest.cs InteropUtils.cs README.md LICENSE.txt NOTICE.md" for %%F in (%FILES%) do ( - echo ── Downloading %%F + echo -------------------------------------------------- + echo Downloading %%F... curl -sSL "%BASE%/%%F" -o "%%F" + if errorlevel 1 ( - echo ⚠️ Failed to download %%F + echo [FAIL] %%F could not be downloaded. ) else ( - echo ✅ Saved %%F + echo [ OK ] %%F downloaded. + + REM Only wrap .cs files + if /I "%%~xF"==".cs" ( + echo ▶ Wrapping %%F with #if…#endif… + + REM Move original to a temp file + move /Y "%%F" "%%F.tmp" >nul + + REM Recreate with wrapper + ( + echo #if IL2CPPBEPINEX + type "%%F.tmp" + echo #endif + ) > "%%F" + + del "%%F.tmp" + echo ✔ Wrapped %%F + ) ) ) echo. -echo All done! +echo All files processed. pause From 721012f68be1e43f03e9769c1359c54a9e0488df Mon Sep 17 00:00:00 2001 From: Chloe Christley <734133+chloelcdev@users.noreply.github.com> Date: Thu, 1 May 2025 04:14:35 -0400 Subject: [PATCH 12/16] Add downloaded BE.Il2CppAssetBundleManager --- .../Il2CppAssetBundle.cs | 286 ++++++++++++++++++ .../Il2CppAssetBundleManager.cs | 141 +++++++++ .../Il2CppAssetBundleRequest.cs | 75 +++++ .../InteropUtils.cs | 30 ++ .../LICENSE.txt | 1 + .../NOTICE.md | 1 + .../README.md | 15 + 7 files changed, 549 insertions(+) create mode 100644 S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundle.cs create mode 100644 S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleManager.cs create mode 100644 S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleRequest.cs create mode 100644 S1API/UnityEngine.BE.Il2CppAssetBundleManager/InteropUtils.cs create mode 100644 S1API/UnityEngine.BE.Il2CppAssetBundleManager/LICENSE.txt create mode 100644 S1API/UnityEngine.BE.Il2CppAssetBundleManager/NOTICE.md create mode 100644 S1API/UnityEngine.BE.Il2CppAssetBundleManager/README.md diff --git a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundle.cs b/S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundle.cs new file mode 100644 index 00000000..be3faae7 --- /dev/null +++ b/S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundle.cs @@ -0,0 +1,286 @@ +#if IL2CPPBEPINEX +using System; +using Il2CppInterop.Runtime; +using Il2CppInterop.Runtime.InteropTypes.Arrays; +using Il2CppInterop.Runtime.InteropTypes; + +namespace UnityEngine +{ + public class Il2CppAssetBundle + { + private IntPtr bundleptr = IntPtr.Zero; + + public Il2CppAssetBundle(IntPtr ptr) { bundleptr = ptr; } + + static Il2CppAssetBundle() + { + get_isStreamedSceneAssetBundleDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundle::get_isStreamedSceneAssetBundle"); + returnMainAssetDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundle::returnMainAsset"); + ContainsDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundle::Contains"); + GetAllAssetNamesDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundle::GetAllAssetNames"); + GetAllScenePathsDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundle::GetAllScenePaths"); + LoadAsset_InternalDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundle::LoadAsset_Internal(System.String,System.Type)"); + LoadAssetAsync_InternalDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundle::LoadAssetAsync_Internal"); + LoadAssetWithSubAssets_InternalDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundle::LoadAssetWithSubAssets_Internal"); + LoadAssetWithSubAssetsAsync_InternalDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundle::LoadAssetWithSubAssetsAsync_Internal"); + UnloadDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundle::Unload"); + } + + public bool isStreamedSceneAssetBundle + { + get + { + if (bundleptr == IntPtr.Zero) + throw new NullReferenceException("The bundleptr cannot be IntPtr.Zero"); + if (get_isStreamedSceneAssetBundleDelegateField == null) + throw new NullReferenceException("The get_isStreamedSceneAssetBundleDelegateField cannot be null."); + return get_isStreamedSceneAssetBundleDelegateField(bundleptr); + } + } + + public Object mainAsset + { + get + { + if (bundleptr == IntPtr.Zero) + throw new NullReferenceException("The bundleptr cannot be IntPtr.Zero"); + if (returnMainAssetDelegateField == null) + throw new NullReferenceException("The returnMainAssetDelegateField cannot be null."); + IntPtr intPtr = returnMainAssetDelegateField(bundleptr); + return ((intPtr != IntPtr.Zero) ? new Object(intPtr) : null); + } + } + + public bool Contains(string name) + { + if (bundleptr == IntPtr.Zero) + throw new NullReferenceException("The bundleptr cannot be IntPtr.Zero"); + if (string.IsNullOrEmpty(name)) + throw new ArgumentException("The input asset name cannot be null or empty."); + if (ContainsDelegateField == null) + throw new NullReferenceException("The ContainsDelegateField cannot be null."); + return ContainsDelegateField(bundleptr, IL2CPP.ManagedStringToIl2Cpp(name)); + } + + public Il2CppStringArray AllAssetNames() => GetAllAssetNames(); + + public Il2CppStringArray GetAllAssetNames() + { + if (bundleptr == IntPtr.Zero) + throw new NullReferenceException("The bundleptr cannot be IntPtr.Zero"); + if (GetAllAssetNamesDelegateField == null) + throw new NullReferenceException("The GetAllAssetNamesDelegateField cannot be null."); + IntPtr intPtr = GetAllAssetNamesDelegateField(bundleptr); + return ((intPtr != IntPtr.Zero) ? new Il2CppStringArray(intPtr) : null); + } + + public Il2CppStringArray AllScenePaths() => GetAllScenePaths(); + + public Il2CppStringArray GetAllScenePaths() + { + if (bundleptr == IntPtr.Zero) + throw new NullReferenceException("The bundleptr cannot be IntPtr.Zero"); + if (GetAllScenePathsDelegateField == null) + throw new NullReferenceException("The GetAllScenePathsDelegateField cannot be null."); + IntPtr intPtr = GetAllScenePathsDelegateField(bundleptr); + return ((intPtr != IntPtr.Zero) ? new Il2CppStringArray(intPtr) : null); + } + + public Object Load(string name) => LoadAsset(name); + + public Object LoadAsset(string name) => LoadAsset(name); + + public T Load(string name) where T : Object => LoadAsset(name); + + public T LoadAsset(string name) where T : Object + { + if (!InteropUtils.IsGeneratedAssemblyType(typeof(T))) + throw new NullReferenceException("The type must be a Generated Assembly Type."); + IntPtr intptr = LoadAsset(name, Il2CppType.Of().Pointer); + return ((intptr != IntPtr.Zero) ? new Il2CppObjectBase(intptr).Cast() : null); + } + + public Object Load(string name, Il2CppSystem.Type type) => LoadAsset(name, type); + + public Object LoadAsset(string name, Il2CppSystem.Type type) + { + if (type == null) + throw new NullReferenceException("The input type cannot be null."); + IntPtr intptr = LoadAsset(name, type.Pointer); + return ((intptr != IntPtr.Zero) ? new Object(intptr) : null); + } + + public IntPtr Load(string name, IntPtr typeptr) => LoadAsset(name, typeptr); + + public IntPtr LoadAsset(string name, IntPtr typeptr) + { + if (bundleptr == IntPtr.Zero) + throw new NullReferenceException("The bundleptr cannot be IntPtr.Zero"); + if (string.IsNullOrEmpty(name)) + throw new ArgumentException("The input asset name cannot be null or empty."); + if (typeptr == IntPtr.Zero) + throw new NullReferenceException("The input type cannot be IntPtr.Zero"); + if (LoadAsset_InternalDelegateField == null) + throw new NullReferenceException("The LoadAsset_InternalDelegateField cannot be null."); + return LoadAsset_InternalDelegateField(bundleptr, IL2CPP.ManagedStringToIl2Cpp(name), typeptr); + } + + public Il2CppAssetBundleRequest LoadAssetAsync(string name) => LoadAssetAsync(name); + + public Il2CppAssetBundleRequest LoadAssetAsync(string name) where T : Object + { + if (!InteropUtils.IsGeneratedAssemblyType(typeof(T))) + throw new NullReferenceException("The type must be a Generated Assembly Type."); + IntPtr intptr = LoadAssetAsync(name, Il2CppType.Of().Pointer); + return ((intptr != IntPtr.Zero) ? new Il2CppAssetBundleRequest(intptr) : null); + } + + public Il2CppAssetBundleRequest LoadAssetAsync(string name, Il2CppSystem.Type type) + { + if (type == null) + throw new NullReferenceException("The input type cannot be null."); + IntPtr intptr = LoadAssetAsync(name, type.Pointer); + return ((intptr != IntPtr.Zero) ? new Il2CppAssetBundleRequest(intptr) : null); + } + + public IntPtr LoadAssetAsync(string name, IntPtr typeptr) + { + if (bundleptr == IntPtr.Zero) + throw new NullReferenceException("The bundleptr cannot be IntPtr.Zero"); + if (string.IsNullOrEmpty(name)) + throw new ArgumentException("The input asset name cannot be null or empty."); + if (typeptr == IntPtr.Zero) + throw new NullReferenceException("The input type cannot be IntPtr.Zero"); + if (LoadAssetAsync_InternalDelegateField == null) + throw new NullReferenceException("The LoadAssetAsync_InternalDelegateField cannot be null."); + return LoadAssetAsync_InternalDelegateField(bundleptr, IL2CPP.ManagedStringToIl2Cpp(name), typeptr); + } + + public Il2CppReferenceArray LoadAll() => LoadAllAssets(); + + public Il2CppReferenceArray LoadAllAssets() => LoadAllAssets(); + + public Il2CppReferenceArray LoadAll() where T : Object => LoadAllAssets(); + + public Il2CppReferenceArray LoadAllAssets() where T : Object + { + if (!InteropUtils.IsGeneratedAssemblyType(typeof(T))) + throw new NullReferenceException("The type must be a Generated Assembly Type."); + IntPtr intptr = LoadAllAssets(Il2CppType.Of().Pointer); + return ((intptr != IntPtr.Zero) ? new Il2CppReferenceArray(intptr) : null); + } + + public Il2CppReferenceArray LoadAll(Il2CppSystem.Type type) => LoadAllAssets(type); + + public Il2CppReferenceArray LoadAllAssets(Il2CppSystem.Type type) + { + if (type == null) + throw new NullReferenceException("The input type cannot be null."); + IntPtr intptr = LoadAllAssets(type.Pointer); + return ((intptr != IntPtr.Zero) ? new Il2CppReferenceArray(intptr) : null); + } + + public IntPtr LoadAll(IntPtr typeptr) => LoadAllAssets(typeptr); + + public IntPtr LoadAllAssets(IntPtr typeptr) + { + if (typeptr == IntPtr.Zero) + throw new NullReferenceException("The input type cannot be IntPtr.Zero"); + if (LoadAssetWithSubAssets_InternalDelegateField == null) + throw new NullReferenceException("The LoadAssetWithSubAssets_InternalDelegateField cannot be null."); + return LoadAssetWithSubAssets_InternalDelegateField(bundleptr, IL2CPP.ManagedStringToIl2Cpp(string.Empty), typeptr); + } + + public Il2CppReferenceArray LoadAssetWithSubAssets(string name) => LoadAssetWithSubAssets(name); + + public Il2CppReferenceArray LoadAssetWithSubAssets(string name) where T : Object + { + if (!InteropUtils.IsGeneratedAssemblyType(typeof(T))) + throw new NullReferenceException("The type must be a Generated Assembly Type."); + IntPtr intptr = LoadAssetWithSubAssets(name, Il2CppType.Of().Pointer); + return ((intptr != IntPtr.Zero) ? new Il2CppReferenceArray(intptr) : null); + } + + public Il2CppReferenceArray LoadAssetWithSubAssets(string name, Il2CppSystem.Type type) + { + if (type == null) + throw new NullReferenceException("The input type cannot be null."); + IntPtr intptr = LoadAssetWithSubAssets(name, type.Pointer); + return ((intptr != IntPtr.Zero) ? new Il2CppReferenceArray(intptr) : null); + } + + public IntPtr LoadAssetWithSubAssets(string name, IntPtr typeptr) + { + if (bundleptr == IntPtr.Zero) + throw new NullReferenceException("The bundleptr cannot be IntPtr.Zero"); + if (string.IsNullOrEmpty(name)) + throw new ArgumentException("The input asset name cannot be null or empty."); + if (typeptr == IntPtr.Zero) + throw new NullReferenceException("The input type cannot be IntPtr.Zero"); + if (LoadAssetWithSubAssets_InternalDelegateField == null) + throw new NullReferenceException("The LoadAssetWithSubAssets_InternalDelegateField cannot be null."); + return LoadAssetWithSubAssets_InternalDelegateField(bundleptr, IL2CPP.ManagedStringToIl2Cpp(name), typeptr); + } + public Il2CppAssetBundleRequest LoadAssetWithSubAssetsAsync(string name) => LoadAssetWithSubAssetsAsync(name); + + public Il2CppAssetBundleRequest LoadAssetWithSubAssetsAsync(string name) where T : Object + { + if (!InteropUtils.IsGeneratedAssemblyType(typeof(T))) + throw new NullReferenceException("The type must be a Generated Assembly Type."); + IntPtr intptr = LoadAssetWithSubAssetsAsync(name, Il2CppType.Of().Pointer); + return ((intptr != IntPtr.Zero) ? new Il2CppAssetBundleRequest(intptr) : null); + } + + public Il2CppAssetBundleRequest LoadAssetWithSubAssetsAsync(string name, Il2CppSystem.Type type) + { + if (type == null) + throw new NullReferenceException("The input type cannot be null."); + IntPtr intptr = LoadAssetWithSubAssetsAsync(name, type.Pointer); + return ((intptr != IntPtr.Zero) ? new Il2CppAssetBundleRequest(intptr) : null); + } + + public IntPtr LoadAssetWithSubAssetsAsync(string name, IntPtr typeptr) + { + if (bundleptr == IntPtr.Zero) + throw new NullReferenceException("The bundleptr cannot be IntPtr.Zero"); + if (string.IsNullOrEmpty(name)) + throw new ArgumentException("The input asset name cannot be null or empty."); + if (typeptr == IntPtr.Zero) + throw new NullReferenceException("The input type cannot be IntPtr.Zero"); + if (LoadAssetWithSubAssetsAsync_InternalDelegateField == null) + throw new NullReferenceException("The LoadAssetWithSubAssetsAsync_InternalDelegateField cannot be null."); + return LoadAssetWithSubAssetsAsync_InternalDelegateField(bundleptr, IL2CPP.ManagedStringToIl2Cpp(name), typeptr); + } + + public void Unload(bool unloadAllLoadedObjects) + { + if (bundleptr == IntPtr.Zero) + throw new NullReferenceException("The bundleptr cannot be IntPtr.Zero"); + if (UnloadDelegateField == null) + throw new NullReferenceException("The UnloadDelegateField cannot be null."); + UnloadDelegateField(bundleptr, unloadAllLoadedObjects); + } + + private delegate bool get_isStreamedSceneAssetBundleDelegate(IntPtr _this); + private static readonly returnMainAssetDelegate returnMainAssetDelegateField; + private delegate IntPtr returnMainAssetDelegate(IntPtr _this); + private static readonly get_isStreamedSceneAssetBundleDelegate get_isStreamedSceneAssetBundleDelegateField; + private delegate bool ContainsDelegate(IntPtr _this, IntPtr name); + private static readonly ContainsDelegate ContainsDelegateField; + private delegate IntPtr GetAllAssetNamesDelegate(IntPtr _this); + private static readonly GetAllAssetNamesDelegate GetAllAssetNamesDelegateField; + private delegate IntPtr GetAllScenePathsDelegate(IntPtr _this); + private static readonly GetAllScenePathsDelegate GetAllScenePathsDelegateField; + private delegate IntPtr LoadAsset_InternalDelegate(IntPtr _this, IntPtr name, IntPtr type); + private static readonly LoadAsset_InternalDelegate LoadAsset_InternalDelegateField; + private delegate IntPtr LoadAssetAsync_InternalDelegate(IntPtr _this, IntPtr name, IntPtr type); + private static readonly LoadAssetAsync_InternalDelegate LoadAssetAsync_InternalDelegateField; + private delegate IntPtr LoadAssetWithSubAssets_InternalDelegate(IntPtr _this, IntPtr name, IntPtr type); + private static readonly LoadAssetWithSubAssets_InternalDelegate LoadAssetWithSubAssets_InternalDelegateField; + private delegate IntPtr LoadAssetWithSubAssetsAsync_InternalDelegate(IntPtr _this, IntPtr name, IntPtr type); + private static readonly LoadAssetWithSubAssetsAsync_InternalDelegate LoadAssetWithSubAssetsAsync_InternalDelegateField; + private delegate void UnloadDelegate(IntPtr _this, bool unloadAllObjects); + private static readonly UnloadDelegate UnloadDelegateField; + } +} +#endif diff --git a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleManager.cs b/S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleManager.cs new file mode 100644 index 00000000..8e927976 --- /dev/null +++ b/S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleManager.cs @@ -0,0 +1,141 @@ +#if IL2CPPBEPINEX +using Il2CppSystem.IO; +using Il2CppInterop.Runtime; +using Il2CppInterop.Runtime.InteropTypes.Arrays; + +namespace UnityEngine +{ + public class Il2CppAssetBundleManager + { + static Il2CppAssetBundleManager() + { + GetAllLoadedAssetBundles_NativeDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundle::GetAllLoadedAssetBundles_Native"); + LoadFromFile_InternalDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundle::LoadFromFile_Internal(System.String,System.UInt32,System.UInt64)"); + LoadFromFileAsync_InternalDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundle::LoadFromFileAsync_Internal"); + LoadFromMemory_InternalDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundle::LoadFromMemory_Internal"); + LoadFromMemoryAsync_InternalDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundle::LoadFromMemoryAsync_Internal"); + LoadFromStreamInternalDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundle::LoadFromStreamInternal"); + LoadFromStreamAsyncInternalDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundle::LoadFromStreamAsyncInternal"); + UnloadAllAssetBundlesDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundle::UnloadAllAssetBundles"); + } + + public static Il2CppAssetBundle[] GetAllLoadedAssetBundles() + { + if (GetAllLoadedAssetBundles_NativeDelegateField == null) + throw new System.NullReferenceException("The GetAllLoadedAssetBundles_NativeDelegateField cannot be null."); + System.IntPtr intPtr = GetAllLoadedAssetBundles_NativeDelegateField(); + Il2CppReferenceArray refarr = ((intPtr != System.IntPtr.Zero) ? new Il2CppReferenceArray(intPtr) : null); + if (refarr == null) + throw new System.NullReferenceException("The refarr cannot be null."); + System.Collections.Generic.List bundlelist = new System.Collections.Generic.List(); + for (int i = 0; i < refarr.Length; i++) + bundlelist.Add(new Il2CppAssetBundle(IL2CPP.Il2CppObjectBaseToPtrNotNull(refarr[i]))); + return bundlelist.ToArray(); + } + + public static Il2CppAssetBundle LoadFromFile(string path) => LoadFromFile(path, 0u, 0UL); + + public static Il2CppAssetBundle LoadFromFile(string path, uint crc) => LoadFromFile(path, crc, 0UL); + + public static Il2CppAssetBundle LoadFromFile(string path, uint crc, ulong offset) + { + if (string.IsNullOrEmpty(path)) + throw new System.ArgumentException("The input asset bundle path cannot be null or empty."); + if (LoadFromFile_InternalDelegateField == null) + throw new System.NullReferenceException("The LoadFromFile_InternalDelegateField cannot be null."); + System.IntPtr intPtr = LoadFromFile_InternalDelegateField(IL2CPP.ManagedStringToIl2Cpp(path), crc, offset); + return ((intPtr != System.IntPtr.Zero) ? new Il2CppAssetBundle(intPtr) : null); + } + + public static Il2CppAssetBundleCreateRequest LoadFromFileAsync(string path) => LoadFromFileAsync(path, 0u, 0UL); + + public static Il2CppAssetBundleCreateRequest LoadFromFileAsync(string path, uint crc) => LoadFromFileAsync(path, crc, 0UL); + + public static Il2CppAssetBundleCreateRequest LoadFromFileAsync(string path, uint crc, ulong offset) + { + if (string.IsNullOrEmpty(path)) + throw new System.ArgumentException("The input asset bundle path cannot be null or empty."); + if (LoadFromFileAsync_InternalDelegateField == null) + throw new System.NullReferenceException("The LoadFromFileAsync_InternalDelegateField cannot be null."); + System.IntPtr intPtr = LoadFromFileAsync_InternalDelegateField(IL2CPP.ManagedStringToIl2Cpp(path), crc, offset); + return ((intPtr != System.IntPtr.Zero) ? new Il2CppAssetBundleCreateRequest(intPtr) : null); + } + + public static Il2CppAssetBundle LoadFromMemory(Il2CppStructArray binary) => LoadFromMemory(binary, 0u); + + public static Il2CppAssetBundle LoadFromMemory(Il2CppStructArray binary, uint crc) + { + if (binary == null) + throw new System.ArgumentException("The binary cannot be null or empty."); + if (LoadFromMemory_InternalDelegateField == null) + throw new System.NullReferenceException("The LoadFromMemory_InternalDelegateField cannot be null."); + System.IntPtr intPtr = LoadFromMemory_InternalDelegateField(IL2CPP.Il2CppObjectBaseToPtrNotNull(binary), crc); + return ((intPtr != System.IntPtr.Zero) ? new Il2CppAssetBundle(intPtr) : null); + } + + public static Il2CppAssetBundleCreateRequest LoadFromMemoryAsync(Il2CppStructArray binary) => LoadFromMemoryAsync(binary, 0u); + + public static Il2CppAssetBundleCreateRequest LoadFromMemoryAsync(Il2CppStructArray binary, uint crc) + { + if (binary == null) + throw new System.ArgumentException("The binary cannot be null or empty."); + if (LoadFromMemoryAsync_InternalDelegateField == null) + throw new System.NullReferenceException("The LoadFromMemoryAsync_InternalDelegateField cannot be null."); + System.IntPtr intPtr = LoadFromMemoryAsync_InternalDelegateField(IL2CPP.Il2CppObjectBaseToPtrNotNull(binary), crc); + return ((intPtr != System.IntPtr.Zero) ? new Il2CppAssetBundleCreateRequest(intPtr) : null); + } + + public static Il2CppAssetBundle LoadFromStream(Stream stream) => LoadFromStream(stream, 0u, 0u); + + public static Il2CppAssetBundle LoadFromStream(Stream stream, uint crc) => LoadFromStream(stream, crc, 0u); + + public static Il2CppAssetBundle LoadFromStream(Stream stream, uint crc, uint managedReadBufferSize) + { + if (stream == null) + throw new System.ArgumentException("The stream cannot be null or empty."); + if (LoadFromStreamInternalDelegateField == null) + throw new System.NullReferenceException("The LoadFromStreamInternalDelegateField cannot be null."); + System.IntPtr intPtr = LoadFromStreamInternalDelegateField(IL2CPP.Il2CppObjectBaseToPtrNotNull(stream), crc, managedReadBufferSize); + return ((intPtr != System.IntPtr.Zero) ? new Il2CppAssetBundle(intPtr) : null); + } + + public static Il2CppAssetBundleCreateRequest LoadFromStreamAsync(Stream stream) => LoadFromStreamAsync(stream, 0u, 0u); + + public static Il2CppAssetBundleCreateRequest LoadFromStreamAsync(Stream stream, uint crc) => LoadFromStreamAsync(stream, crc, 0u); + + public static Il2CppAssetBundleCreateRequest LoadFromStreamAsync(Stream stream, uint crc, uint managedReadBufferSize) + { + if (stream == null) + throw new System.ArgumentException("The stream cannot be null or empty."); + if (LoadFromStreamAsyncInternalDelegateField == null) + throw new System.NullReferenceException("The LoadFromStreamAsyncInternalDelegateField cannot be null."); + System.IntPtr intPtr = LoadFromStreamAsyncInternalDelegateField(IL2CPP.Il2CppObjectBaseToPtrNotNull(stream), crc, managedReadBufferSize); + return ((intPtr != System.IntPtr.Zero) ? new Il2CppAssetBundleCreateRequest(intPtr) : null); + } + + public static void UnloadAllAssetBundles(bool unloadAllObjects) + { + if (UnloadAllAssetBundlesDelegateField == null) + throw new System.NullReferenceException("The UnloadAllAssetBundlesDelegateField cannot be null."); + UnloadAllAssetBundlesDelegateField(unloadAllObjects); + } + + private delegate System.IntPtr GetAllLoadedAssetBundles_NativeDelegate(); + private static readonly GetAllLoadedAssetBundles_NativeDelegate GetAllLoadedAssetBundles_NativeDelegateField; + private delegate System.IntPtr LoadFromFile_InternalDelegate(System.IntPtr path, uint crc, ulong offset); + private static readonly LoadFromFile_InternalDelegate LoadFromFile_InternalDelegateField; + private delegate System.IntPtr LoadFromFileAsync_InternalDelegate(System.IntPtr path, uint crc, ulong offset); + private static readonly LoadFromFileAsync_InternalDelegate LoadFromFileAsync_InternalDelegateField; + private delegate System.IntPtr LoadFromMemory_InternalDelegate(System.IntPtr binary, uint crc); + private static readonly LoadFromMemory_InternalDelegate LoadFromMemory_InternalDelegateField; + private delegate System.IntPtr LoadFromMemoryAsync_InternalDelegate(System.IntPtr binary, uint crc); + private static readonly LoadFromMemoryAsync_InternalDelegate LoadFromMemoryAsync_InternalDelegateField; + private delegate System.IntPtr LoadFromStreamInternalDelegate(System.IntPtr stream, uint crc, uint managedReadBufferSize); + private static readonly LoadFromStreamInternalDelegate LoadFromStreamInternalDelegateField; + private delegate System.IntPtr LoadFromStreamAsyncInternalDelegate(System.IntPtr stream, uint crc, uint managedReadBufferSize); + private static readonly LoadFromStreamAsyncInternalDelegate LoadFromStreamAsyncInternalDelegateField; + private delegate System.IntPtr UnloadAllAssetBundlesDelegate(bool unloadAllObjects); + private static readonly UnloadAllAssetBundlesDelegate UnloadAllAssetBundlesDelegateField; + } +} +#endif diff --git a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleRequest.cs b/S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleRequest.cs new file mode 100644 index 00000000..d1ff269a --- /dev/null +++ b/S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleRequest.cs @@ -0,0 +1,75 @@ +#if IL2CPPBEPINEX +using System; +using Il2CppInterop.Runtime; +using Il2CppInterop.Runtime.InteropTypes.Arrays; + +namespace UnityEngine +{ + public class Il2CppAssetBundleCreateRequest : AsyncOperation + { + public Il2CppAssetBundleCreateRequest(IntPtr ptr) : base(ptr) { } + + static Il2CppAssetBundleCreateRequest() + { + Il2CppInterop.Runtime.Injection.ClassInjector.RegisterTypeInIl2Cpp(); + + get_assetBundleDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundleCreateRequest::get_assetBundle"); + } + + public Il2CppAssetBundle assetBundle + { + [Il2CppInterop.Runtime.Attributes.HideFromIl2Cpp] + get + { + var ptr = get_assetBundleDelegateField(this.Pointer); + if (ptr == IntPtr.Zero) + return null; + return new Il2CppAssetBundle(ptr); + } + } + + private delegate IntPtr get_assetBundleDelegate(IntPtr _this); + private static get_assetBundleDelegate get_assetBundleDelegateField; + } + + public class Il2CppAssetBundleRequest : AsyncOperation + { + public Il2CppAssetBundleRequest(IntPtr ptr) : base(ptr) { } + + static Il2CppAssetBundleRequest() + { + Il2CppInterop.Runtime.Injection.ClassInjector.RegisterTypeInIl2Cpp(); + + get_assetDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundleRequest::get_asset"); + get_allAssetsDelegateField = IL2CPP.ResolveICall("UnityEngine.AssetBundleRequest::get_allAssets"); + } + + public Object asset + { + get + { + var ptr = get_assetDelegateField(this.Pointer); + if (ptr == IntPtr.Zero) + return null; + return new Object(ptr); + } + } + + public Il2CppReferenceArray allAssets + { + get + { + var ptr = get_allAssetsDelegateField(this.Pointer); + if (ptr == IntPtr.Zero) + return null; + return new Il2CppReferenceArray(ptr); + } + } + + private delegate IntPtr get_assetDelegate(IntPtr _this); + private static get_assetDelegate get_assetDelegateField; + + private delegate IntPtr get_allAssetsDelegate(IntPtr _this); + private static get_allAssetsDelegate get_allAssetsDelegateField; + } +}#endif diff --git a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/InteropUtils.cs b/S1API/UnityEngine.BE.Il2CppAssetBundleManager/InteropUtils.cs new file mode 100644 index 00000000..2944045c --- /dev/null +++ b/S1API/UnityEngine.BE.Il2CppAssetBundleManager/InteropUtils.cs @@ -0,0 +1,30 @@ +#if IL2CPPBEPINEX +using Il2CppInterop.Runtime.InteropTypes; +using Il2CppInterop.Runtime; +using System; + +namespace UnityEngine +{ + internal static class InteropUtils + { + public static bool IsGeneratedAssemblyType(Type type) + => IsInheritedFromIl2CppObjectBase(type) && !IsInjectedType(type); + + public static bool IsInheritedFromIl2CppObjectBase(Type type) + => (type != null) && type.IsSubclassOf(typeof(Il2CppObjectBase)); + + public static bool IsInjectedType(Type type) + { + IntPtr ptr = GetClassPointerForType(type); + return ptr != IntPtr.Zero && RuntimeSpecificsStore.IsInjected(ptr); + } + + public static IntPtr GetClassPointerForType(Type type) + { + if (type == typeof(void)) return Il2CppClassPointerStore.NativeClassPtr; + return (IntPtr)typeof(Il2CppClassPointerStore<>).MakeGenericType(type) + .GetField(nameof(Il2CppClassPointerStore.NativeClassPtr)).GetValue(null); + } + } +} +#endif diff --git a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/LICENSE.txt b/S1API/UnityEngine.BE.Il2CppAssetBundleManager/LICENSE.txt new file mode 100644 index 00000000..1becba2b --- /dev/null +++ b/S1API/UnityEngine.BE.Il2CppAssetBundleManager/LICENSE.txt @@ -0,0 +1 @@ +404: Not Found \ No newline at end of file diff --git a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/NOTICE.md b/S1API/UnityEngine.BE.Il2CppAssetBundleManager/NOTICE.md new file mode 100644 index 00000000..1becba2b --- /dev/null +++ b/S1API/UnityEngine.BE.Il2CppAssetBundleManager/NOTICE.md @@ -0,0 +1 @@ +404: Not Found \ No newline at end of file diff --git a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/README.md b/S1API/UnityEngine.BE.Il2CppAssetBundleManager/README.md new file mode 100644 index 00000000..d36caea7 --- /dev/null +++ b/S1API/UnityEngine.BE.Il2CppAssetBundleManager/README.md @@ -0,0 +1,15 @@ +### GENERAL INFORMATION: + +- Il2Cpp Asset Bundle Manager for [Il2CppInterop](https://github.com/Bepinex/Il2CppInterop) and [BepInEx](https://github.com/BepInEx/BepInEx). +- Originally created by LavaGang for [MelonLoader](https://github.com/LavaGang/MelonLoader). + +--- + +### LICENSING & CREDITS: + +Modified by XmusJackson to support BepInEx. Tested with Schedule I 0.3.4f8. + +UnityEngine.Il2CppAssetBundleManager is licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/LavaGang/UnityEngine.Il2CppAssetBundleManager/blob/master/LICENSE.md) for the full License. + +UnityEngine.Il2CppAssetBundleManager is not sponsored by, affiliated with or endorsed by Unity Technologies or its affiliates. +"Unity" is a trademark or a registered trademark of Unity Technologies or its affiliates in the U.S. and elsewhere. From 451726dd1bafe20b066330400e71ee2ee90b1e78 Mon Sep 17 00:00:00 2001 From: MaxtorCoder Date: Fri, 2 May 2025 18:09:39 +0200 Subject: [PATCH 13/16] fix: Some code style changes TODO: - Track all loaded asset bundles (?) - Test BepInEx once it get's merged properly --- .../{AssetLoader.cs => AssetBundleManager.cs} | 61 ++++---- .../Il2CppAssetBundle.cs | 2 +- .../Il2CppAssetBundleManager.cs | 22 +-- .../Il2CppAssetBundleRequest.cs | 5 +- .../InteropUtils.cs | 2 +- .../LICENSE.txt | 0 .../NOTICE.md | 0 .../README.md | 0 .../UpdateBepInExBundleManager.bat | 0 S1API/AssetBundles/WrappedAssetBundle.cs | 148 ++++-------------- .../AssetBundles/WrappedAssetBundleRequest.cs | 31 ++++ S1API/S1API.csproj | 3 +- 12 files changed, 111 insertions(+), 163 deletions(-) rename S1API/AssetBundles/{AssetLoader.cs => AssetBundleManager.cs} (56%) rename S1API/{ => AssetBundles}/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundle.cs (99%) rename S1API/{ => AssetBundles}/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleManager.cs (98%) rename S1API/{ => AssetBundles}/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleRequest.cs (99%) rename S1API/{ => AssetBundles}/UnityEngine.BE.Il2CppAssetBundleManager/InteropUtils.cs (95%) rename S1API/{ => AssetBundles}/UnityEngine.BE.Il2CppAssetBundleManager/LICENSE.txt (100%) rename S1API/{ => AssetBundles}/UnityEngine.BE.Il2CppAssetBundleManager/NOTICE.md (100%) rename S1API/{ => AssetBundles}/UnityEngine.BE.Il2CppAssetBundleManager/README.md (100%) rename S1API/{ => AssetBundles}/UnityEngine.BE.Il2CppAssetBundleManager/UpdateBepInExBundleManager.bat (100%) create mode 100644 S1API/AssetBundles/WrappedAssetBundleRequest.cs diff --git a/S1API/AssetBundles/AssetLoader.cs b/S1API/AssetBundles/AssetBundleManager.cs similarity index 56% rename from S1API/AssetBundles/AssetLoader.cs rename to S1API/AssetBundles/AssetBundleManager.cs index 1ceb1e05..33a2edfd 100644 --- a/S1API/AssetBundles/AssetLoader.cs +++ b/S1API/AssetBundles/AssetBundleManager.cs @@ -1,14 +1,15 @@ -using System; -using System.IO; -using System.Reflection; +using System.Reflection; + using UnityEngine; namespace S1API.AssetBundles { - - public static class AssetLoader + /// + /// The asset bundle manager + /// + public static class AssetBundleManager { -#if IL2CPPMELON +#if IL2CPPMELON || IL2CPPBEPINEX /// /// Loads an Il2Cpp AssetBundle from an embedded resource stream by name. /// @@ -30,57 +31,61 @@ public static WrappedAssetBundle GetAssetBundleFromStream(string fullResourceNa // Load the AssetBundle from memory Il2CppAssetBundle bundle = Il2CppAssetBundleManager.LoadFromMemory(data); - if (bundle == null){ - MelonLoader.Logger.Error($"Failed to load AssetBundle from memory: {fullResourceName}"); + if (bundle == null) + { + MelonLoader.Logger.Error($"Failed to load AssetBundle from memory: {fullResourceName}"); throw new Exception($"Failed to load AssetBundle from memory: {fullResourceName}"); - } + } return new(bundle); } } -#elif MONOMELON +#elif MONOMELON || MONOBEPINEX + /// + /// Load a instance by resource name. + /// + /// The full embedded resource name (including namespace path); + /// The loaded AssetBundle instance public static WrappedAssetBundle GetAssetBundleFromStream(string fullResourceName) { // Attempt to find the embedded resource in the executing assembly var assembly = Assembly.GetExecutingAssembly(); - var stream = assembly.GetManifestResourceStream(fullResourceName); - - return new(AssetBundle.LoadFromStream(stream)); + return new WrappedAssetBundle(AssetBundle.LoadFromStream(stream)); } #endif /// - /// - /// No need to type the assembly just the path stuff. - /// - /// Example if carassetbundle is in subfolder /bundles/: - /// + /// + /// No need to type the assembly just the path stuff. + /// + /// Example if carassetbundle is in subfolder /bundles/: + /// /// GameObject myCar = Instantiate(EasyLoad("bundles.carassetbundle", "MyCarGameObject")); - /// + /// /// - public static T EasyLoad(string bundle_name, string object_name) where T : UnityEngine.Object + public static T EasyLoad(string bundleName, string objectName) where T : Object { - return EasyLoad(bundle_name, object_name, Assembly.GetExecutingAssembly(), out _); + return EasyLoad(bundleName, objectName, Assembly.GetExecutingAssembly(), out _); } - public static T EasyLoad(string bundle_name, string object_name, out WrappedAssetBundle bundle) where T : UnityEngine.Object + public static T EasyLoad(string bundleName, string objectName, out WrappedAssetBundle bundle) where T : Object { - return EasyLoad(bundle_name, object_name, Assembly.GetExecutingAssembly(), out bundle); + return EasyLoad(bundleName, objectName, Assembly.GetExecutingAssembly(), out bundle); } - public static T EasyLoad(string bundle_name, string object_name, Assembly assemblyOverride) where T : UnityEngine.Object + public static T EasyLoad(string bundleName, string objectName, Assembly assemblyOverride) where T : Object { - return EasyLoad(bundle_name, object_name, assemblyOverride, out _); + return EasyLoad(bundleName, objectName, assemblyOverride, out _); } - public static T EasyLoad(string bundle_name, string object_name, Assembly assemblyOverride, out WrappedAssetBundle bundle) where T : UnityEngine.Object + public static T EasyLoad(string bundleName, string objectName, Assembly assemblyOverride, out WrappedAssetBundle bundle) where T : Object { // Get the asset bundle from the assembly - bundle = GetAssetBundleFromStream($"{assemblyOverride.GetName().Name}.{bundle_name}"); + bundle = GetAssetBundleFromStream($"{assemblyOverride.GetName().Name}.{bundleName}"); // Load the asset from the bundle - return bundle.LoadAsset(object_name); + return bundle.LoadAsset(objectName); } } diff --git a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundle.cs b/S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundle.cs similarity index 99% rename from S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundle.cs rename to S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundle.cs index be3faae7..b2acf95a 100644 --- a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundle.cs +++ b/S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundle.cs @@ -1,5 +1,5 @@ #if IL2CPPBEPINEX -using System; +using System; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppInterop.Runtime.InteropTypes; diff --git a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleManager.cs b/S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleManager.cs similarity index 98% rename from S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleManager.cs rename to S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleManager.cs index 8e927976..d3307449 100644 --- a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleManager.cs +++ b/S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleManager.cs @@ -1,5 +1,5 @@ #if IL2CPPBEPINEX -using Il2CppSystem.IO; +using Il2CppSystem.IO; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes.Arrays; @@ -34,9 +34,9 @@ public static Il2CppAssetBundle[] GetAllLoadedAssetBundles() } public static Il2CppAssetBundle LoadFromFile(string path) => LoadFromFile(path, 0u, 0UL); - + public static Il2CppAssetBundle LoadFromFile(string path, uint crc) => LoadFromFile(path, crc, 0UL); - + public static Il2CppAssetBundle LoadFromFile(string path, uint crc, ulong offset) { if (string.IsNullOrEmpty(path)) @@ -48,9 +48,9 @@ public static Il2CppAssetBundle LoadFromFile(string path, uint crc, ulong offset } public static Il2CppAssetBundleCreateRequest LoadFromFileAsync(string path) => LoadFromFileAsync(path, 0u, 0UL); - + public static Il2CppAssetBundleCreateRequest LoadFromFileAsync(string path, uint crc) => LoadFromFileAsync(path, crc, 0UL); - + public static Il2CppAssetBundleCreateRequest LoadFromFileAsync(string path, uint crc, ulong offset) { if (string.IsNullOrEmpty(path)) @@ -62,7 +62,7 @@ public static Il2CppAssetBundleCreateRequest LoadFromFileAsync(string path, uint } public static Il2CppAssetBundle LoadFromMemory(Il2CppStructArray binary) => LoadFromMemory(binary, 0u); - + public static Il2CppAssetBundle LoadFromMemory(Il2CppStructArray binary, uint crc) { if (binary == null) @@ -74,7 +74,7 @@ public static Il2CppAssetBundle LoadFromMemory(Il2CppStructArray binary, u } public static Il2CppAssetBundleCreateRequest LoadFromMemoryAsync(Il2CppStructArray binary) => LoadFromMemoryAsync(binary, 0u); - + public static Il2CppAssetBundleCreateRequest LoadFromMemoryAsync(Il2CppStructArray binary, uint crc) { if (binary == null) @@ -86,9 +86,9 @@ public static Il2CppAssetBundleCreateRequest LoadFromMemoryAsync(Il2CppStructArr } public static Il2CppAssetBundle LoadFromStream(Stream stream) => LoadFromStream(stream, 0u, 0u); - + public static Il2CppAssetBundle LoadFromStream(Stream stream, uint crc) => LoadFromStream(stream, crc, 0u); - + public static Il2CppAssetBundle LoadFromStream(Stream stream, uint crc, uint managedReadBufferSize) { if (stream == null) @@ -100,9 +100,9 @@ public static Il2CppAssetBundle LoadFromStream(Stream stream, uint crc, uint man } public static Il2CppAssetBundleCreateRequest LoadFromStreamAsync(Stream stream) => LoadFromStreamAsync(stream, 0u, 0u); - + public static Il2CppAssetBundleCreateRequest LoadFromStreamAsync(Stream stream, uint crc) => LoadFromStreamAsync(stream, crc, 0u); - + public static Il2CppAssetBundleCreateRequest LoadFromStreamAsync(Stream stream, uint crc, uint managedReadBufferSize) { if (stream == null) diff --git a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleRequest.cs b/S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleRequest.cs similarity index 99% rename from S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleRequest.cs rename to S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleRequest.cs index d1ff269a..41489e45 100644 --- a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleRequest.cs +++ b/S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/Il2CppAssetBundleRequest.cs @@ -1,5 +1,5 @@ #if IL2CPPBEPINEX -using System; +using System; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes.Arrays; @@ -72,4 +72,5 @@ public Il2CppReferenceArray allAssets private delegate IntPtr get_allAssetsDelegate(IntPtr _this); private static get_allAssetsDelegate get_allAssetsDelegateField; } -}#endif +} +#endif diff --git a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/InteropUtils.cs b/S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/InteropUtils.cs similarity index 95% rename from S1API/UnityEngine.BE.Il2CppAssetBundleManager/InteropUtils.cs rename to S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/InteropUtils.cs index 2944045c..546dd8eb 100644 --- a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/InteropUtils.cs +++ b/S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/InteropUtils.cs @@ -1,5 +1,5 @@ #if IL2CPPBEPINEX -using Il2CppInterop.Runtime.InteropTypes; +using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime; using System; diff --git a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/LICENSE.txt b/S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/LICENSE.txt similarity index 100% rename from S1API/UnityEngine.BE.Il2CppAssetBundleManager/LICENSE.txt rename to S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/LICENSE.txt diff --git a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/NOTICE.md b/S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/NOTICE.md similarity index 100% rename from S1API/UnityEngine.BE.Il2CppAssetBundleManager/NOTICE.md rename to S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/NOTICE.md diff --git a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/README.md b/S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/README.md similarity index 100% rename from S1API/UnityEngine.BE.Il2CppAssetBundleManager/README.md rename to S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/README.md diff --git a/S1API/UnityEngine.BE.Il2CppAssetBundleManager/UpdateBepInExBundleManager.bat b/S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/UpdateBepInExBundleManager.bat similarity index 100% rename from S1API/UnityEngine.BE.Il2CppAssetBundleManager/UpdateBepInExBundleManager.bat rename to S1API/AssetBundles/UnityEngine.BE.Il2CppAssetBundleManager/UpdateBepInExBundleManager.bat diff --git a/S1API/AssetBundles/WrappedAssetBundle.cs b/S1API/AssetBundles/WrappedAssetBundle.cs index fe5989fb..303ca3c0 100644 --- a/S1API/AssetBundles/WrappedAssetBundle.cs +++ b/S1API/AssetBundles/WrappedAssetBundle.cs @@ -1,25 +1,23 @@ using UnityEngine; -#if IL2CPP -using Type = Il2CppSystem.Type; +#if IL2CPPMELON +using Il2CppSystem; using AssetBundle = UnityEngine.Il2CppAssetBundle; using AssetBundleRequest = UnityEngine.Il2CppAssetBundleRequest; #else -using Type = System.Type; +using System; #endif using Object = UnityEngine.Object; - namespace S1API.AssetBundles { - /// - /// Works just like the AssetBundle type, it's just a proxy, but will use Il2CppAssetBundle if it needs to so you don't have to worry about it. + /// INTERNAL: Wrapper around instance. /// public class WrappedAssetBundle { - public bool isStreamedAssetBundle => _realBundle.isStreamedSceneAssetBundle; + public bool IsStreamedAssetBundle => _realBundle.isStreamedSceneAssetBundle; public AssetBundle _realBundle; @@ -28,136 +26,48 @@ public WrappedAssetBundle(AssetBundle realBundle) _realBundle = realBundle; } + public bool Contains(string name) => _realBundle.Contains(name); - public bool Contains(string name) - { - return _realBundle.Contains(name); - } + public string[] GetAllAssetNames() => _realBundle.GetAllAssetNames(); - public string[] AllAssetNames() - { - return GetAllAssetNames(); - } + public string[] GetAllScenePaths() => _realBundle.GetAllScenePaths(); - public string[] GetAllAssetNames() - { - return _realBundle.GetAllAssetNames(); - } + public Object Load(string name) => LoadAsset(name); - public string[] AllScenePaths() - { - return GetAllScenePaths(); - } + public Object LoadAsset(string name) => LoadAsset(name); - public string[] GetAllScenePaths() - { - return _realBundle.GetAllScenePaths(); - } + public T Load(string name) where T : Object => LoadAsset(name); - public Object Load(string name) - { - return LoadAsset(name); - } + public T LoadAsset(string name) where T : Object => _realBundle.LoadAsset(name); - public Object LoadAsset(string name) - { - return this.LoadAsset(name); - } + public Object Load(string name, Type type) => LoadAsset(name, type); - public T Load(string name) where T : Object - { - return LoadAsset(name); - } + public Object LoadAsset(string name, Type type) => _realBundle.LoadAsset(name, type); - public T LoadAsset(string name) where T : Object - { - return _realBundle.LoadAsset(name); - } + public WrappedAssetBundleRequest LoadAssetAsync(string name) => LoadAssetAsync(name); - public Object Load(string name, Type type) - { - return LoadAsset(name, type); - } + public WrappedAssetBundleRequest LoadAssetAsync(string name) where T : Object => + new WrappedAssetBundleRequest(_realBundle.LoadAssetAsync(name)); - public Object LoadAsset(string name, Type type) - { - return _realBundle.LoadAsset(name, type); - } + public WrappedAssetBundleRequest LoadAssetAsync(string name, Type type) => + new WrappedAssetBundleRequest(_realBundle.LoadAssetAsync(name, type)); - public WrappedAssetBundleRequest LoadAssetAsync(string name) - { - return this.LoadAssetAsync(name); - } + public Object[] LoadAll() => LoadAllAssets(); - public WrappedAssetBundleRequest LoadAssetAsync(string name) where T : Object - { - return new(_realBundle.LoadAssetAsync(name)); - } + public Object[] LoadAllAssets() => LoadAllAssets(); - public WrappedAssetBundleRequest LoadAssetAsync(string name, Type type) - { - return new(_realBundle.LoadAssetAsync(name, type)); - } + public T[] LoadAllAssets() where T : Object => _realBundle.LoadAllAssets(); - public Object[] LoadAll() - { - return LoadAllAssets(); - } + public Object[] LoadAllAssets(Type type) => _realBundle.LoadAllAssets(type); - public Object[] LoadAllAssets() - { - return this.LoadAllAssets(); - } - - public T[] LoadAllAssets() where T : Object - { - return _realBundle.LoadAllAssets(); - } - - public Object[] LoadAllAssets(Type type) - { - return _realBundle.LoadAllAssets(type); - } - - public Object[] LoadAssetWithSubAssets(string name) - { - return this.LoadAssetWithSubAssets(name); - } - - public T[] LoadAssetWithSubAssets(string name) where T : Object - { - return _realBundle.LoadAssetWithSubAssets(name); - } - - public Object[] LoadAssetWithSubAssets(string name, Type type) - { - return _realBundle.LoadAssetWithSubAssets(name, type); - } + public Object[] LoadAssetWithSubAssets(string name) => LoadAssetWithSubAssets(name); - public void Unload(bool unloadAllLoadedObjects) - { - _realBundle.Unload(unloadAllLoadedObjects); - } - } + public T[] LoadAssetWithSubAssets(string name) where T : Object => _realBundle.LoadAssetWithSubAssets(name); + public Object[] LoadAssetWithSubAssets(string name, Type type) => _realBundle.LoadAssetWithSubAssets(name, type); - /// - /// Works just like the AssetBundleRequest type, it's just a proxy, but will use Il2CppAssetBundleRequest if it needs to so you don't have to worry about it. - /// - public class WrappedAssetBundleRequest - { - public AssetBundleRequest _realRequest; - - public WrappedAssetBundleRequest(AssetBundleRequest realRequest) - { - _realRequest = realRequest; - } - - public Object asset => _realRequest.asset; - - public Object[] allAssets => _realRequest.allAssets; + public void Unload(bool unloadAllLoadedObjects) => _realBundle.Unload(unloadAllLoadedObjects); } - } /* Might need this if the above fails @@ -192,7 +102,7 @@ public static T[] ToManagedArray(this Il2CppReferenceArray il2cppObjects) /* - * + * * [12:57:44.463] [ManorMod] Unhandled exception in coroutine. It will not continue executing. System.MissingMethodException: Method not found: 'UnityEngine.AssetBundle UnityEngine.AssetBundle.LoadFromStream(System.IO.Stream)'. at S1API.AssetBundles.AssetLoader.GetAssetBundleFromStream(String fullResourceName) @@ -206,4 +116,4 @@ at ManorMod.Core.OnLateInitializeMelon() at MelonLoader.MelonEventBase`1.Invoke(Action`1 delegateInvoker) in D:\a\MelonLoader\MelonLoader\MelonLoader\Melons\Events\MelonEvent.cs:line 143 -*/ \ No newline at end of file +*/ diff --git a/S1API/AssetBundles/WrappedAssetBundleRequest.cs b/S1API/AssetBundles/WrappedAssetBundleRequest.cs new file mode 100644 index 00000000..a9dd0b4f --- /dev/null +++ b/S1API/AssetBundles/WrappedAssetBundleRequest.cs @@ -0,0 +1,31 @@ +using UnityEngine; + +namespace S1API.AssetBundles +{ + /// + /// INTERNAL: Wrapper around instance. + /// + public class WrappedAssetBundleRequest + { + private readonly AssetBundleRequest _realRequest; + + /// + /// INTERNAL: Default constructor for + /// + /// + internal WrappedAssetBundleRequest(AssetBundleRequest realRequest) + { + _realRequest = realRequest; + } + + /// + /// The requested asset instance. + /// + public Object Asset => _realRequest.asset; + + /// + /// All Assets in the . + /// + public Object[] AllAssets => _realRequest.allAssets; + } +} diff --git a/S1API/S1API.csproj b/S1API/S1API.csproj index 26de5041..c9eaf523 100644 --- a/S1API/S1API.csproj +++ b/S1API/S1API.csproj @@ -70,6 +70,7 @@ + @@ -91,7 +92,7 @@ - + From 16dac4e101dcea0885f38c8ce7c274023c2d8a1d Mon Sep 17 00:00:00 2001 From: MaxtorCoder Date: Fri, 2 May 2025 18:46:36 +0200 Subject: [PATCH 14/16] Rename back to AssetLoader --- S1API/AssetBundles/{AssetBundleManager.cs => AssetLoader.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename S1API/AssetBundles/{AssetBundleManager.cs => AssetLoader.cs} (98%) diff --git a/S1API/AssetBundles/AssetBundleManager.cs b/S1API/AssetBundles/AssetLoader.cs similarity index 98% rename from S1API/AssetBundles/AssetBundleManager.cs rename to S1API/AssetBundles/AssetLoader.cs index 33a2edfd..f6a7f867 100644 --- a/S1API/AssetBundles/AssetBundleManager.cs +++ b/S1API/AssetBundles/AssetLoader.cs @@ -7,7 +7,7 @@ namespace S1API.AssetBundles /// /// The asset bundle manager /// - public static class AssetBundleManager + public static class AssetLoader { #if IL2CPPMELON || IL2CPPBEPINEX /// From 86dc4031d8e108f3fd65c183742b7983c7133a60 Mon Sep 17 00:00:00 2001 From: MaxtorCoder Date: Sun, 4 May 2025 12:24:39 +0200 Subject: [PATCH 15/16] fix: Fix for BepInEx building --- S1API/AssetBundles/AssetLoader.cs | 82 ++++++++++++------- S1API/AssetBundles/WrappedAssetBundle.cs | 2 +- .../AssetBundles/WrappedAssetBundleRequest.cs | 4 + 3 files changed, 59 insertions(+), 29 deletions(-) diff --git a/S1API/AssetBundles/AssetLoader.cs b/S1API/AssetBundles/AssetLoader.cs index f6a7f867..9e27fe9e 100644 --- a/S1API/AssetBundles/AssetLoader.cs +++ b/S1API/AssetBundles/AssetLoader.cs @@ -1,6 +1,14 @@ -using System.Reflection; +using System; +using System.Reflection; + +#if IL2CPPBEPINEX || IL2CPPMELON +using System.IO; +#endif using UnityEngine; +using Object = UnityEngine.Object; + +using S1API.Logging; namespace S1API.AssetBundles { @@ -9,36 +17,33 @@ namespace S1API.AssetBundles /// public static class AssetLoader { + private static readonly Log _logger = new Log("AssetLoader"); + #if IL2CPPMELON || IL2CPPBEPINEX /// /// Loads an Il2Cpp AssetBundle from an embedded resource stream by name. /// /// The full embedded resource name (including namespace path). /// The loaded Il2CppAssetBundle, or throws on failure. - public static WrappedAssetBundle GetAssetBundleFromStream(string fullResourceName) + public static WrappedAssetBundle GetAssetBundleFromStream(string fullResourceName) { // Attempt to find the embedded resource in the executing assembly - var assembly = Assembly.GetExecutingAssembly(); - using (Stream stream = assembly.GetManifestResourceStream(fullResourceName)) - { - if (stream == null) - throw new Exception($"Embedded resource '{fullResourceName}' not found in {assembly.FullName}."); // hoping these throws will be melon/bepinex-agnostic + Assembly assembly = Assembly.GetExecutingAssembly(); - // Read the stream into a byte array - byte[] data = new byte[stream.Length]; - stream.Read(data, 0, data.Length); + using Stream? stream = assembly.GetManifestResourceStream(fullResourceName); + if (stream == null) + throw new Exception($"Embedded resource '{fullResourceName}' not found in {assembly.FullName}."); // hoping these throws will be melon/bepinex-agnostic - // Load the AssetBundle from memory - Il2CppAssetBundle bundle = Il2CppAssetBundleManager.LoadFromMemory(data); + // Read the stream into a byte array + byte[] data = new byte[stream.Length]; + _ = stream.Read(data, 0, data.Length); - if (bundle == null) - { - MelonLoader.Logger.Error($"Failed to load AssetBundle from memory: {fullResourceName}"); - throw new Exception($"Failed to load AssetBundle from memory: {fullResourceName}"); - } + // Load the AssetBundle from memory + Il2CppAssetBundle bundle = Il2CppAssetBundleManager.LoadFromMemory(data); + if (bundle == null) + throw new Exception($"Failed to load AssetBundle from memory: {fullResourceName}"); - return new(bundle); - } + return new WrappedAssetBundle(bundle); } #elif MONOMELON || MONOBEPINEX /// @@ -56,29 +61,52 @@ public static WrappedAssetBundle GetAssetBundleFromStream(string fullResourceNam #endif /// - /// - /// No need to type the assembly just the path stuff. - /// - /// Example if carassetbundle is in subfolder /bundles/: - /// - /// GameObject myCar = Instantiate(EasyLoad("bundles.carassetbundle", "MyCarGameObject")); - /// + /// Loads an asset of type from an embedded AssetBundle using the executing assembly. /// + /// The type of asset to load (must derive from UnityEngine.Object). + /// The name of the embedded AssetBundle resource. + /// The name of the asset to load within the AssetBundle. + /// The loaded asset of type . public static T EasyLoad(string bundleName, string objectName) where T : Object { return EasyLoad(bundleName, objectName, Assembly.GetExecutingAssembly(), out _); } + /// + /// Loads an asset of type from an embedded AssetBundle using the executing assembly and outputs the loaded bundle. + /// + /// The type of asset to load (must derive from UnityEngine.Object). + /// The name of the embedded AssetBundle resource. + /// The name of the asset to load within the AssetBundle. + /// The output parameter containing the loaded . + /// The loaded asset of type . public static T EasyLoad(string bundleName, string objectName, out WrappedAssetBundle bundle) where T : Object { return EasyLoad(bundleName, objectName, Assembly.GetExecutingAssembly(), out bundle); } + /// + /// Loads an asset of type from an embedded AssetBundle using a specified assembly. + /// + /// The type of asset to load (must derive from UnityEngine.Object). + /// The name of the embedded AssetBundle resource. + /// The name of the asset to load within the AssetBundle. + /// The assembly from which to load the embedded AssetBundle resource. + /// The loaded asset of type . public static T EasyLoad(string bundleName, string objectName, Assembly assemblyOverride) where T : Object { return EasyLoad(bundleName, objectName, assemblyOverride, out _); } + /// + /// Loads an asset of type from an embedded AssetBundle using a specified assembly and outputs the loaded bundle. + /// + /// The type of asset to load (must derive from UnityEngine.Object). + /// The name of the embedded AssetBundle resource. + /// The name of the asset to load within the AssetBundle. + /// The assembly from which to load the embedded AssetBundle resource. + /// The output parameter containing the loaded . + /// The loaded asset of type . public static T EasyLoad(string bundleName, string objectName, Assembly assemblyOverride, out WrappedAssetBundle bundle) where T : Object { // Get the asset bundle from the assembly @@ -87,7 +115,5 @@ public static T EasyLoad(string bundleName, string objectName, Assembly assem // Load the asset from the bundle return bundle.LoadAsset(objectName); } - } - } diff --git a/S1API/AssetBundles/WrappedAssetBundle.cs b/S1API/AssetBundles/WrappedAssetBundle.cs index 303ca3c0..58719f3b 100644 --- a/S1API/AssetBundles/WrappedAssetBundle.cs +++ b/S1API/AssetBundles/WrappedAssetBundle.cs @@ -1,6 +1,6 @@ using UnityEngine; -#if IL2CPPMELON +#if IL2CPPMELON || IL2CPPBEPINEX using Il2CppSystem; using AssetBundle = UnityEngine.Il2CppAssetBundle; using AssetBundleRequest = UnityEngine.Il2CppAssetBundleRequest; diff --git a/S1API/AssetBundles/WrappedAssetBundleRequest.cs b/S1API/AssetBundles/WrappedAssetBundleRequest.cs index a9dd0b4f..fa9dd8eb 100644 --- a/S1API/AssetBundles/WrappedAssetBundleRequest.cs +++ b/S1API/AssetBundles/WrappedAssetBundleRequest.cs @@ -1,5 +1,9 @@ using UnityEngine; +#if IL2CPPBEPINEX || IL2CPPMELON +using AssetBundleRequest = UnityEngine.Il2CppAssetBundleRequest; +#endif + namespace S1API.AssetBundles { /// From ada9cddb4eabb22c0206b03d9b66e1ae54c6967b Mon Sep 17 00:00:00 2001 From: MaxtorCoder Date: Sun, 4 May 2025 13:04:25 +0200 Subject: [PATCH 16/16] fix: Add AssetBundle caching and fix `overrideAssembly` parameter --- S1API/AssetBundles/AssetLoader.cs | 35 +++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/S1API/AssetBundles/AssetLoader.cs b/S1API/AssetBundles/AssetLoader.cs index 9e27fe9e..15c4f590 100644 --- a/S1API/AssetBundles/AssetLoader.cs +++ b/S1API/AssetBundles/AssetLoader.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Reflection; #if IL2CPPBEPINEX || IL2CPPMELON @@ -18,21 +19,24 @@ namespace S1API.AssetBundles public static class AssetLoader { private static readonly Log _logger = new Log("AssetLoader"); + private static readonly Dictionary _cachedAssetBundles = new Dictionary(); #if IL2CPPMELON || IL2CPPBEPINEX /// /// Loads an Il2Cpp AssetBundle from an embedded resource stream by name. /// /// The full embedded resource name (including namespace path). + /// The assembly to load the embedded resource from. /// The loaded Il2CppAssetBundle, or throws on failure. - public static WrappedAssetBundle GetAssetBundleFromStream(string fullResourceName) + public static WrappedAssetBundle GetAssetBundleFromStream(string fullResourceName, Assembly overrideAssembly) { - // Attempt to find the embedded resource in the executing assembly - Assembly assembly = Assembly.GetExecutingAssembly(); + if (_cachedAssetBundles.TryGetValue(fullResourceName, out WrappedAssetBundle cachedWrappedAssetBundle)) + return cachedWrappedAssetBundle; - using Stream? stream = assembly.GetManifestResourceStream(fullResourceName); + // Attempt to find the embedded resource in the executing assembly + using Stream? stream = overrideAssembly.GetManifestResourceStream(fullResourceName); if (stream == null) - throw new Exception($"Embedded resource '{fullResourceName}' not found in {assembly.FullName}."); // hoping these throws will be melon/bepinex-agnostic + throw new Exception($"Embedded resource '{fullResourceName}' not found in {overrideAssembly.FullName}."); // hoping these throws will be melon/bepinex-agnostic // Read the stream into a byte array byte[] data = new byte[stream.Length]; @@ -43,20 +47,29 @@ public static WrappedAssetBundle GetAssetBundleFromStream(string fullResourceNam if (bundle == null) throw new Exception($"Failed to load AssetBundle from memory: {fullResourceName}"); - return new WrappedAssetBundle(bundle); + WrappedAssetBundle wrappedAssetBundle = new WrappedAssetBundle(bundle); + _cachedAssetBundles.TryAdd(fullResourceName, wrappedAssetBundle); + return wrappedAssetBundle; } #elif MONOMELON || MONOBEPINEX /// /// Load a instance by resource name. /// /// The full embedded resource name (including namespace path); + /// The assembly to load the embedded resource from. /// The loaded AssetBundle instance - public static WrappedAssetBundle GetAssetBundleFromStream(string fullResourceName) + public static WrappedAssetBundle GetAssetBundleFromStream(string fullResourceName, Assembly overrideAssembly) { + // Attempt to retrieve the cached asset bundle + if (_cachedAssetBundles.TryGetValue(fullResourceName, out WrappedAssetBundle cachedWrappedAssetBundle)) + return cachedWrappedAssetBundle; + // Attempt to find the embedded resource in the executing assembly - var assembly = Assembly.GetExecutingAssembly(); - var stream = assembly.GetManifestResourceStream(fullResourceName); - return new WrappedAssetBundle(AssetBundle.LoadFromStream(stream)); + var stream = overrideAssembly.GetManifestResourceStream(fullResourceName); + + WrappedAssetBundle wrappedAssetBundle = new WrappedAssetBundle(AssetBundle.LoadFromStream(stream)); + _cachedAssetBundles.TryAdd(fullResourceName, wrappedAssetBundle); + return wrappedAssetBundle; } #endif @@ -110,7 +123,7 @@ public static T EasyLoad(string bundleName, string objectName, Assembly assem public static T EasyLoad(string bundleName, string objectName, Assembly assemblyOverride, out WrappedAssetBundle bundle) where T : Object { // Get the asset bundle from the assembly - bundle = GetAssetBundleFromStream($"{assemblyOverride.GetName().Name}.{bundleName}"); + bundle = GetAssetBundleFromStream($"{assemblyOverride.GetName().Name}.{bundleName}", assemblyOverride); // Load the asset from the bundle return bundle.LoadAsset(objectName);