Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of GDG ScheduleI Traditional Chinese Translation v2.0.4
FontPatcher.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using FontPatcher; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem.Collections.Generic; using Il2CppTMPro; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.TextCore; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Main), "GDG字體修補dll", "1.0.0", "GDG遊戲聯合公國-午夜的大叔", null)] [assembly: MelonGame(null, null)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("FontPatcher")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("FontPatcher")] [assembly: AssemblyTitle("FontPatcher")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace FontPatcher { public class Main : MelonMod { private static readonly MelonPreferences_Category ConfigCategory = MelonPreferences.CreateCategory("GDG_FontPatcher", "GDG 字體修補設定"); private static MelonPreferences_Entry<string> _fontRegex; internal static Main Instance; internal static List<TMP_FontAsset> LoadedFonts = new List<TMP_FontAsset>(); internal static HashSet<int> PatchedFontIds = new HashSet<int>(); internal static Regex FontNameRegex; private bool _fontsLoaded; private static readonly HashSet<string> SkipExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".dll", ".cfg", ".ini", ".txt", ".json", ".xml", ".log", ".md", ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".pdb", ".mdb", ".cs", ".csproj", ".sln", ".yml", ".yaml", ".zip", ".7z", ".rar", ".exe", ".bat", ".sh", ".ico", ".svg", ".ttf", ".otf", ".woff" }; private float _lastPatchTime; private int _swapCount; private Material _donorMaterial; public override void OnInitializeMelon() { Instance = this; _fontRegex = ConfigCategory.CreateEntry<string>("FontNameRegex", ".*", "字體名稱匹配", "正則表達式,匹配要修補的遊戲字體名稱", false, false, (ValueValidator)null, (string)null); ((MelonBase)this).LoggerInstance.Msg("========================================"); ((MelonBase)this).LoggerInstance.Msg(" GDG 字體修補dll v1.0.0"); ((MelonBase)this).LoggerInstance.Msg("========================================"); MoveAutoTranslator(); } private void MoveAutoTranslator() { try { string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "AutoTranslator"); if (!Directory.Exists(text)) { ((MelonBase)this).LoggerInstance.Msg("AutoTranslator 資料夾不存在於模組目錄,可能已搬移過"); return; } string text2 = Path.Combine(MelonEnvironment.GameRootDirectory, "AutoTranslator"); ((MelonBase)this).LoggerInstance.Msg("開始進行翻譯文本與設定檔搬移作業..."); DirectoryCopy(text, text2, copySubDirs: true); ((MelonBase)this).LoggerInstance.Msg("✅ 已覆蓋 AutoTranslator → " + text2); Directory.Delete(text, recursive: true); ((MelonBase)this).LoggerInstance.Msg("✅ 已刪除模組目錄的 AutoTranslator(搬移完成)"); } catch (Exception ex) { ((MelonBase)this).LoggerInstance.Error("AutoTranslator 搬移失敗: " + ex.Message); } } private void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs) { DirectoryInfo directoryInfo = new DirectoryInfo(sourceDirName); DirectoryInfo[] directories = directoryInfo.GetDirectories(); Directory.CreateDirectory(destDirName); FileInfo[] files = directoryInfo.GetFiles(); foreach (FileInfo fileInfo in files) { string destFileName = Path.Combine(destDirName, fileInfo.Name); fileInfo.CopyTo(destFileName, overwrite: true); } if (copySubDirs) { DirectoryInfo[] array = directories; foreach (DirectoryInfo directoryInfo2 in array) { string destDirName2 = Path.Combine(destDirName, directoryInfo2.Name); DirectoryCopy(directoryInfo2.FullName, destDirName2, copySubDirs); } } } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { if (!_fontsLoaded) { _fontsLoaded = true; ((MelonBase)this).LoggerInstance.Msg("場景 [" + sceneName + "] 觸發字體載入..."); LoadFontBundles(); if (LoadedFonts.Count > 0) { FontNameRegex = new Regex(_fontRegex.Value); } } if (LoadedFonts.Count == 0) { if (buildIndex <= 1) { ((MelonBase)this).LoggerInstance.Warning("未找到任何字體檔案,字體修補停用"); } } else { PatchAllFonts("場景 [" + sceneName + "]"); SwapFontsOnTextComponents(); } } public override void OnLateUpdate() { if (LoadedFonts.Count != 0 && _fontsLoaded) { float time = Time.time; float num = ((time < 10f) ? 0.5f : 2f); if (!(time - _lastPatchTime < num)) { _lastPatchTime = time; PatchAllFonts(null); SwapFontsOnTextComponents(); } } } private void SwapFontsOnTextComponents() { TMP_FontAsset val = LoadedFonts[0]; int num = 0; Il2CppArrayBase<TextMeshProUGUI> val2 = Resources.FindObjectsOfTypeAll<TextMeshProUGUI>(); if (val2 == null) { return; } foreach (TextMeshProUGUI item in val2) { if ((Object)(object)item == (Object)null || (Object)(object)((TMP_Text)item).font == (Object)(object)val) { continue; } string text = null; try { text = ((TMP_Text)item).text; } catch { continue; } if (string.IsNullOrEmpty(text)) { continue; } bool flag = false; string text2 = text; foreach (char c in text2) { if (c >= '一' && c <= '\u9fff') { flag = true; break; } } if (flag) { ((TMP_Text)item).font = val; num++; } } if (num > 0 && _swapCount == 0) { ((MelonBase)this).LoggerInstance.Msg($"直接替換字體: {num} 個含中文的文字元件"); } _swapCount += num; } private void PatchAllFonts(string source) { Il2CppArrayBase<TMP_FontAsset> val = Resources.FindObjectsOfTypeAll<TMP_FontAsset>(); if (val == null) { return; } int num = 0; foreach (TMP_FontAsset item in val) { if ((Object)(object)item == (Object)null) { continue; } int instanceID = ((Object)item).GetInstanceID(); if (PatchedFontIds.Contains(instanceID) || LoadedFonts.Contains(item) || (Object)(object)((TMP_Asset)item).material == (Object)null) { continue; } string input = ((Object)item).name ?? ""; if (FontNameRegex != null && !FontNameRegex.IsMatch(input)) { continue; } if (item.fallbackFontAssetTable == null) { item.fallbackFontAssetTable = new List<TMP_FontAsset>(); } int num2 = 0; foreach (TMP_FontAsset loadedFont in LoadedFonts) { if ((Object)(object)loadedFont != (Object)null) { item.fallbackFontAssetTable.Add(loadedFont); num2++; } } PatchedFontIds.Add(instanceID); if (num2 > 0) { num++; } } if (num <= 0 || source == null) { return; } ((MelonBase)this).LoggerInstance.Msg($"{source} 已修補 {num} 個字體(累計 {PatchedFontIds.Count} 個)"); foreach (TMP_FontAsset item2 in val) { if (!((Object)(object)item2 == (Object)null) && item2.fallbackFontAssetTable != null && !LoadedFonts.Contains(item2) && item2.fallbackFontAssetTable.Count > 0) { _ = item2.fallbackFontAssetTable[0]; bool value = item2.HasCharacter('中', true, true); ((MelonBase)this).LoggerInstance.Msg($" 驗證 [{((Object)item2).name}]: fallback數={item2.fallbackFontAssetTable.Count}, HasCharacter('中',searchFallbacks)={value}"); break; } } } private void InitFontAsset(TMP_FontAsset font) { //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)((TMP_Asset)font).material == (Object)null && (Object)(object)font.atlasTexture != (Object)null) { ((MelonBase)this).LoggerInstance.Msg(" material 為 null,從遊戲字體複製..."); if ((Object)(object)_donorMaterial == (Object)null) { foreach (TMP_FontAsset item in Resources.FindObjectsOfTypeAll<TMP_FontAsset>()) { if ((Object)(object)item != (Object)null && (Object)(object)((TMP_Asset)item).material != (Object)null && !LoadedFonts.Contains(item)) { _donorMaterial = ((TMP_Asset)item).material; ((MelonBase)this).LoggerInstance.Msg(" donor material: " + ((Object)item).name + " → " + ((Object)_donorMaterial).name); break; } } } if ((Object)(object)_donorMaterial != (Object)null) { Material val = Object.Instantiate<Material>(_donorMaterial); ((Object)val).name = ((Object)font).name + " Material"; val.mainTexture = (Texture)(object)font.atlasTexture; val.SetFloat(Shader.PropertyToID("_TextureWidth"), (float)((Texture)font.atlasTexture).width); val.SetFloat(Shader.PropertyToID("_TextureHeight"), (float)((Texture)font.atlasTexture).height); val.SetFloat(Shader.PropertyToID("_GradientScale"), (float)(font.atlasPadding + 1)); ((TMP_Asset)font).material = val; ((MelonBase)this).LoggerInstance.Msg($" 已建立 material: {((Object)val).name} (shader={((Object)val.shader).name})"); } else { ((MelonBase)this).LoggerInstance.Warning(" 找不到可用的 donor material!"); } } font.ReadFontAssetDefinition(); Texture2D atlasTexture = font.atlasTexture; Material material = ((TMP_Asset)font).material; int value = font.characterTable?.Count ?? 0; bool value2 = font.HasCharacter('中', false, false); string text = "N/A"; try { Dictionary<uint, TMP_Character> characterLookupTable = font.characterLookupTable; TMP_Character val2 = default(TMP_Character); if (characterLookupTable != null && characterLookupTable.TryGetValue(20013u, ref val2) && val2 != null) { Glyph glyph = ((TMP_TextElement)val2).glyph; if (glyph != null) { GlyphRect glyphRect = glyph.glyphRect; text = $"glyphIdx={glyph.index}, rect=({((GlyphRect)(ref glyphRect)).x},{((GlyphRect)(ref glyphRect)).y},{((GlyphRect)(ref glyphRect)).width},{((GlyphRect)(ref glyphRect)).height}), atlasIdx={glyph.atlasIndex}"; } else { text = "glyph=null!"; } } } catch (Exception ex) { text = "error: " + ex.Message; } ((MelonBase)this).LoggerInstance.Msg($" 初始化: atlas={(((Object)(object)atlasTexture != (Object)null) ? $"{((Texture)atlasTexture).width}x{((Texture)atlasTexture).height}" : "null")}, material={(((Object)(object)material != (Object)null) ? ((Object)material).name : "null")}, 字元數={value}, HasChar('中')={value2}"); ((MelonBase)this).LoggerInstance.Msg(" glyph('中'): " + text); } catch (Exception ex2) { ((MelonBase)this).LoggerInstance.Warning(" InitFontAsset 失敗: " + ex2.Message); } } private void LoadFontBundles() { string gameRootDirectory = MelonEnvironment.GameRootDirectory; string modsDirectory = MelonEnvironment.ModsDirectory; string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); HashSet<string> searchDirs = new HashSet<string>(StringComparer.OrdinalIgnoreCase); TryAdd(gameRootDirectory); TryAdd(Path.Combine(gameRootDirectory, "FontPatcher")); TryAdd(Path.Combine(gameRootDirectory, "AutoTranslator")); TryAdd(Path.Combine(gameRootDirectory, "AutoTranslator", "FontPatcher")); TryAdd(directoryName); TryAdd(Path.Combine(directoryName, "FontPatcher")); TryAdd(Path.Combine(directoryName, "AutoTranslator")); TryAdd(Path.Combine(directoryName, "AutoTranslator", "FontPatcher")); TryAdd(modsDirectory); if (Directory.Exists(modsDirectory)) { string[] directories = Directory.GetDirectories(modsDirectory); foreach (string obj in directories) { TryAdd(obj); TryAdd(Path.Combine(obj, "FontPatcher")); TryAdd(Path.Combine(obj, "AutoTranslator")); TryAdd(Path.Combine(obj, "AutoTranslator", "FontPatcher")); } } ((MelonBase)this).LoggerInstance.Msg($"掃描 {searchDirs.Count} 個目錄..."); Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); foreach (string item in searchDirs) { bool flag = Path.GetFileName(item).Equals("FontPatcher", StringComparison.OrdinalIgnoreCase); string[] directories = Directory.GetFiles(item); foreach (string path in directories) { string extension = Path.GetExtension(path); if ((string.IsNullOrEmpty(extension) || !SkipExtensions.Contains(extension)) && (flag || string.IsNullOrEmpty(extension))) { string fileName = Path.GetFileName(path); if (!dictionary.ContainsKey(fileName)) { dictionary[fileName] = Path.GetFullPath(path); } } } } if (dictionary.Count == 0) { ((MelonBase)this).LoggerInstance.Warning("所有目錄中都找不到字體檔案"); { foreach (string item2 in searchDirs.Take(8)) { ((MelonBase)this).LoggerInstance.Warning(" 已搜尋: " + item2); } return; } } ((MelonBase)this).LoggerInstance.Msg($"找到 {dictionary.Count} 個候選字體檔案"); int num = 0; int num2 = 0; foreach (KeyValuePair<string, string> item3 in dictionary) { string key = item3.Key; string value = item3.Value; ((MelonBase)this).LoggerInstance.Msg("嘗試載入: " + value); try { Il2CppAssetBundle il2CppAssetBundle = Il2CppAssetBundleManager.LoadFromFile(value); if (il2CppAssetBundle == null) { ((MelonBase)this).LoggerInstance.Warning(" [" + key + "] LoadFromFile 回傳 null"); num2++; continue; } ((MelonBase)this).LoggerInstance.Msg(" [" + key + "] AssetBundle 已開啟"); bool flag2 = false; string[] array = null; try { Il2CppStringArray allAssetNames = il2CppAssetBundle.GetAllAssetNames(); if (allAssetNames != null && ((Il2CppArrayBase<string>)(object)allAssetNames).Length > 0) { array = new string[((Il2CppArrayBase<string>)(object)allAssetNames).Length]; for (int j = 0; j < ((Il2CppArrayBase<string>)(object)allAssetNames).Length; j++) { array[j] = ((Il2CppArrayBase<string>)(object)allAssetNames)[j]; ((MelonBase)this).LoggerInstance.Msg($" asset[{j}]: {array[j]}"); } } } catch (Exception ex) { ((MelonBase)this).LoggerInstance.Msg(" GetAllAssetNames 失敗: " + ex.Message); } if (array != null) { string[] directories = array; foreach (string text in directories) { try { TMP_FontAsset val = il2CppAssetBundle.LoadAsset<TMP_FontAsset>(text); if ((Object)(object)val != (Object)null) { ((MelonBase)this).LoggerInstance.Msg(" LoadAsset(\"" + text + "\") → " + ((Object)val).name); ((MelonBase)this).LoggerInstance.Msg(" material(載入時)=" + (((Object)(object)((TMP_Asset)val).material != (Object)null) ? ((Object)((TMP_Asset)val).material).name : "null")); InitFontAsset(val); LoadedFonts.Add(val); flag2 = true; num++; } } catch { } } } if (!flag2) { try { Il2CppReferenceArray<TMP_FontAsset> val2 = il2CppAssetBundle.LoadAllAssets<TMP_FontAsset>(); if (val2 != null && ((Il2CppArrayBase<TMP_FontAsset>)(object)val2).Length > 0) { foreach (TMP_FontAsset item4 in (Il2CppArrayBase<TMP_FontAsset>)(object)val2) { if ((Object)(object)item4 != (Object)null) { InitFontAsset(item4); ((MelonBase)this).LoggerInstance.Msg(" → 字體: " + ((Object)item4).name); LoadedFonts.Add(item4); flag2 = true; } } if (flag2) { num++; } } } catch (Exception ex2) { ((MelonBase)this).LoggerInstance.Msg(" LoadAllAssets 跳過: " + ex2.Message); } } if (flag2) { continue; } try { Il2CppStringArray allAssetNames2 = il2CppAssetBundle.GetAllAssetNames(); if (allAssetNames2 != null && ((Il2CppArrayBase<string>)(object)allAssetNames2).Length > 0) { ((MelonBase)this).LoggerInstance.Warning(" [" + key + "] 無 TMP_FontAsset,包含的資產:"); foreach (string item5 in (Il2CppArrayBase<string>)(object)allAssetNames2) { ((MelonBase)this).LoggerInstance.Warning(" - " + item5); } } else { ((MelonBase)this).LoggerInstance.Warning(" [" + key + "] AssetBundle 為空"); } } catch { } il2CppAssetBundle.Unload(unloadAllLoadedObjects: false); num2++; } catch (Exception ex3) { ((MelonBase)this).LoggerInstance.Warning(" [" + key + "] 載入失敗: " + ex3.Message); num2++; } } ((MelonBase)this).LoggerInstance.Msg($"字體載入完成: {num} 成功, {num2} 失敗, 共 {LoadedFonts.Count} 個字體資產"); void TryAdd(string p) { try { if (Directory.Exists(p)) { searchDirs.Add(Path.GetFullPath(p)); } } catch { } } } } } namespace UnityEngine { public class Il2CppAssetBundle { private delegate bool ContainsDelegate(IntPtr _this, IntPtr name); private delegate IntPtr GetAllAssetNamesDelegate(IntPtr _this); private delegate IntPtr LoadAsset_InternalDelegate(IntPtr _this, IntPtr name, IntPtr type); private delegate IntPtr LoadAssetWithSubAssets_InternalDelegate(IntPtr _this, IntPtr name, IntPtr type); private delegate void UnloadDelegate(IntPtr _this, bool unloadAllObjects); private IntPtr bundleptr = IntPtr.Zero; private static readonly ContainsDelegate ContainsDelegateField; private static readonly GetAllAssetNamesDelegate GetAllAssetNamesDelegateField; private static readonly LoadAsset_InternalDelegate LoadAsset_InternalDelegateField; private static readonly LoadAssetWithSubAssets_InternalDelegate LoadAssetWithSubAssets_InternalDelegateField; private static readonly UnloadDelegate UnloadDelegateField; public Il2CppAssetBundle(IntPtr ptr) { bundleptr = ptr; } static Il2CppAssetBundle() { ContainsDelegateField = IL2CPP.ResolveICall<ContainsDelegate>("UnityEngine.AssetBundle::Contains"); GetAllAssetNamesDelegateField = IL2CPP.ResolveICall<GetAllAssetNamesDelegate>("UnityEngine.AssetBundle::GetAllAssetNames"); LoadAsset_InternalDelegateField = IL2CPP.ResolveICall<LoadAsset_InternalDelegate>("UnityEngine.AssetBundle::LoadAsset_Internal(System.String,System.Type)"); LoadAssetWithSubAssets_InternalDelegateField = IL2CPP.ResolveICall<LoadAssetWithSubAssets_InternalDelegate>("UnityEngine.AssetBundle::LoadAssetWithSubAssets_Internal"); UnloadDelegateField = IL2CPP.ResolveICall<UnloadDelegate>("UnityEngine.AssetBundle::Unload"); } 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 GetAllAssetNames() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown 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); if (!(intPtr != IntPtr.Zero)) { return null; } return new Il2CppStringArray(intPtr); } public T LoadAsset<T>(string name) where T : Il2CppObjectBase { //IL_002c: Unknown result type (might be due to invalid IL or missing references) IntPtr pointer = ((Il2CppObjectBase)Il2CppType.Of<T>()).Pointer; IntPtr intPtr = LoadAssetRaw(name, pointer); if (intPtr == IntPtr.Zero) { return default(T); } return new Il2CppObjectBase(intPtr).Cast<T>(); } private IntPtr LoadAssetRaw(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 Il2CppReferenceArray<T> LoadAllAssets<T>() where T : Il2CppObjectBase { IntPtr pointer = ((Il2CppObjectBase)Il2CppType.Of<T>()).Pointer; IntPtr intPtr = LoadAllAssetsRaw(pointer); if (!(intPtr != IntPtr.Zero)) { return null; } return new Il2CppReferenceArray<T>(intPtr); } private IntPtr LoadAllAssetsRaw(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 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); } } public class Il2CppAssetBundleManager { private delegate IntPtr LoadFromFile_InternalDelegate(IntPtr path, uint crc, ulong offset); private delegate IntPtr UnloadAllAssetBundlesDelegate(bool unloadAllObjects); private static readonly LoadFromFile_InternalDelegate LoadFromFile_InternalDelegateField; private static readonly UnloadAllAssetBundlesDelegate UnloadAllAssetBundlesDelegateField; static Il2CppAssetBundleManager() { LoadFromFile_InternalDelegateField = IL2CPP.ResolveICall<LoadFromFile_InternalDelegate>("UnityEngine.AssetBundle::LoadFromFile_Internal(System.String,System.UInt32,System.UInt64)"); UnloadAllAssetBundlesDelegateField = IL2CPP.ResolveICall<UnloadAllAssetBundlesDelegate>("UnityEngine.AssetBundle::UnloadAllAssetBundles"); } public static Il2CppAssetBundle LoadFromFile(string path) { return LoadFromFile(path, 0u, 0uL); } public static Il2CppAssetBundle LoadFromFile(string path, uint crc) { return LoadFromFile(path, crc, 0uL); } public static Il2CppAssetBundle LoadFromFile(string path, uint crc, ulong offset) { if (string.IsNullOrEmpty(path)) { throw new ArgumentException("The input asset bundle path cannot be null or empty."); } if (LoadFromFile_InternalDelegateField == null) { throw new NullReferenceException("The LoadFromFile_InternalDelegateField cannot be null."); } IntPtr intPtr = LoadFromFile_InternalDelegateField(IL2CPP.ManagedStringToIl2Cpp(path), crc, offset); if (!(intPtr != IntPtr.Zero)) { return null; } return new Il2CppAssetBundle(intPtr); } public static void UnloadAllAssetBundles(bool unloadAllObjects) { if (UnloadAllAssetBundlesDelegateField == null) { throw new NullReferenceException("The UnloadAllAssetBundlesDelegateField cannot be null."); } UnloadAllAssetBundlesDelegateField(unloadAllObjects); } } }