using System; using System.IO; using System.Linq; using System.Reflection; using UnityEngine; namespace TH1_Logic.Hotfix { public static class HotfixBootstrap { private static bool _initialized; public static bool IsLoaded { get; private set; } public static string LoadedAssemblyFullName { get; private set; } [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] private static void AutoInitialize() { InitializeFromStreamingAssets(false); } public static bool InitializeFromStreamingAssets(bool requireHotfix) { if (_initialized) return IsLoaded; try { var root = Path.Combine(Application.streamingAssetsPath, HotfixManifest.RootFolderName); LoadAotMetadata(root); var hotfixPath = Path.Combine(root, HotfixManifest.HotfixDllFolderName, HotfixManifest.HotfixAssemblyFileName); if (!File.Exists(hotfixPath)) { if (requireHotfix) { Debug.LogWarning($"[TH1.Hotfix] Hotfix dll missing: {hotfixPath}"); } return false; } var assembly = Assembly.Load(File.ReadAllBytes(hotfixPath)); LoadedAssemblyFullName = assembly.FullName; InvokeHotfixEntry(assembly); _initialized = true; IsLoaded = true; Debug.Log($"[TH1.Hotfix] Loaded {LoadedAssemblyFullName}"); return true; } catch (Exception e) { Debug.LogError($"[TH1.Hotfix] Initialize failed: {e}"); return false; } } private static void LoadAotMetadata(string root) { var runtimeApiType = FindType("HybridCLR.RuntimeApi"); var modeType = FindType("HybridCLR.HomologousImageMode"); if (runtimeApiType == null || modeType == null) { return; } var method = runtimeApiType.GetMethod( "LoadMetadataForAOTAssembly", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(byte[]), modeType }, null); if (method == null) { Debug.LogWarning("[TH1.Hotfix] HybridCLR RuntimeApi.LoadMetadataForAOTAssembly not found."); return; } var mode = Enum.Parse(modeType, "SuperSet"); var aotDir = Path.Combine(root, HotfixManifest.AotMetadataFolderName); foreach (var fileName in HotfixManifest.AotMetadataAssemblyFileNames) { var path = Path.Combine(aotDir, fileName); if (!File.Exists(path)) continue; var result = method.Invoke(null, new object[] { File.ReadAllBytes(path), mode }); Debug.Log($"[TH1.Hotfix] Load AOT metadata {fileName}: {result}"); } } private static void InvokeHotfixEntry(Assembly assembly) { var entryType = assembly.GetType(HotfixManifest.EntryTypeName); if (entryType == null) { Debug.LogWarning($"[TH1.Hotfix] Entry type not found: {HotfixManifest.EntryTypeName}"); return; } var method = entryType.GetMethod(HotfixManifest.EntryMethodName, BindingFlags.Public | BindingFlags.Static); if (method == null) { Debug.LogWarning($"[TH1.Hotfix] Entry method not found: {HotfixManifest.EntryMethodName}"); return; } method.Invoke(null, null); } private static Type FindType(string fullName) { return AppDomain.CurrentDomain.GetAssemblies() .Select(assembly => assembly.GetType(fullName, false)) .FirstOrDefault(type => type != null); } } }