using System; using System.IO; using System.Linq; using System.Reflection; using TH1_Logic.Hotfix; using UnityEditor; using UnityEngine; namespace TH1_Logic.Editor.HybridCLR { public static class TH1HybridCLRBuildTools { private const string HybridClrInstallerMenu = "HybridCLR/Installer..."; private const string HybridClrGenerateAllMenu = "HybridCLR/Generate/All"; private const string HybridClrCompileDllMenu = "HybridCLR/CompileDll/ActiveBuildTarget"; [MenuItem("Tools/TH1/iOS Migration/HybridCLR/1. Run HybridCLR Installer")] public static void RunHybridClrInstaller() { ExecuteHybridClrMenu(HybridClrInstallerMenu); } [MenuItem("Tools/TH1/iOS Migration/HybridCLR/2. Configure TH1 Hotfix Settings")] public static void ConfigureHotfixSettings() { var settingsType = FindType("HybridCLR.Editor.Settings.HybridCLRSettings"); if (settingsType == null) { Debug.LogWarning("[TH1.HybridCLR] HybridCLR package is not available yet. Let Unity resolve packages, then run this menu again."); return; } var instance = settingsType.GetProperty("Instance", BindingFlags.Public | BindingFlags.Static)?.GetValue(null); if (instance == null) { Debug.LogWarning("[TH1.HybridCLR] HybridCLRSettings.Instance is unavailable."); return; } SetBoolField(settingsType, instance, "enable", true); AddStringToArrayField(settingsType, instance, "hotUpdateAssemblies", HotfixManifest.HotfixAssemblyName); AddStringToArrayField(settingsType, instance, "patchAOTAssemblies", "mscorlib"); AddStringToArrayField(settingsType, instance, "patchAOTAssemblies", "System"); AddStringToArrayField(settingsType, instance, "patchAOTAssemblies", "System.Core"); settingsType.GetMethod("Save", BindingFlags.Public | BindingFlags.Static)?.Invoke(null, null); Debug.Log("[TH1.HybridCLR] Configured HybridCLRSettings for TH1.Hotfix."); } [MenuItem("Tools/TH1/iOS Migration/HybridCLR/3. Generate All")] public static void GenerateAll() { ExecuteHybridClrMenu(HybridClrGenerateAllMenu); } [MenuItem("Tools/TH1/iOS Migration/HybridCLR/4. Compile Hotfix Dll")] public static void CompileHotfixDll() { if (!ExecuteHybridClrMenu(HybridClrCompileDllMenu)) { Debug.LogWarning("[TH1.HybridCLR] Compile menu was not found. Run HybridCLR/Generate/All or use the package CompileDll menu manually."); } } [MenuItem("Tools/TH1/iOS Migration/HybridCLR/5. Copy Hotfix Artifacts To StreamingAssets")] public static void CopyHotfixArtifactsToStreamingAssets() { CopyHotfixDll(); CopyAotMetadataDlls(); AssetDatabase.Refresh(); } [MenuItem("Tools/TH1/iOS Migration/HybridCLR/6. Test Load StreamingAssets Hotfix In Editor")] public static void TestLoadHotfixInEditor() { var loaded = HotfixBootstrap.InitializeFromStreamingAssets(true); Debug.Log($"[TH1.HybridCLR] Test load result: {loaded}, assembly: {HotfixBootstrap.LoadedAssemblyFullName}"); } private static void CopyHotfixDll() { var buildTarget = EditorUserBuildSettings.activeBuildTarget.ToString(); var source = Path.Combine("HybridCLRData", "HotUpdateDlls", buildTarget, HotfixManifest.HotfixAssemblyName + ".dll"); if (!File.Exists(source)) { source = Path.Combine("Library", "ScriptAssemblies", HotfixManifest.HotfixAssemblyName + ".dll"); } var destination = Path.Combine( Application.streamingAssetsPath, HotfixManifest.RootFolderName, HotfixManifest.HotfixDllFolderName, HotfixManifest.HotfixAssemblyFileName); CopyFile(source, destination, "hotfix dll"); } private static void CopyAotMetadataDlls() { var buildTarget = EditorUserBuildSettings.activeBuildTarget.ToString(); var sourceDir = Path.Combine("HybridCLRData", "AssembliesPostIl2CppStrip", buildTarget); var destinationDir = Path.Combine(Application.streamingAssetsPath, HotfixManifest.RootFolderName, HotfixManifest.AotMetadataFolderName); if (!Directory.Exists(sourceDir)) { Debug.LogWarning($"[TH1.HybridCLR] AOT metadata source missing: {sourceDir}. Run HybridCLR/Generate/All after an IL2CPP target is selected."); return; } Directory.CreateDirectory(destinationDir); foreach (var fileName in HotfixManifest.AotMetadataAssemblyFileNames) { var source = Path.Combine(sourceDir, fileName.Replace(".bytes", "")); var destination = Path.Combine(destinationDir, fileName); if (!File.Exists(source)) { Debug.LogWarning($"[TH1.HybridCLR] AOT metadata dll missing: {source}"); continue; } File.Copy(source, destination, true); Debug.Log($"[TH1.HybridCLR] Copied AOT metadata: {destination}"); } } private static bool ExecuteHybridClrMenu(string menuPath) { if (EditorApplication.ExecuteMenuItem(menuPath)) { return true; } Debug.LogWarning($"[TH1.HybridCLR] Unity menu not found: {menuPath}. Make sure the HybridCLR package has been resolved."); return false; } private static void CopyFile(string source, string destination, string label) { if (!File.Exists(source)) { Debug.LogWarning($"[TH1.HybridCLR] {label} source missing: {source}"); return; } Directory.CreateDirectory(Path.GetDirectoryName(destination)); File.Copy(source, destination, true); Debug.Log($"[TH1.HybridCLR] Copied {label}: {destination}"); } private static Type FindType(string fullName) { return AppDomain.CurrentDomain.GetAssemblies() .Select(assembly => assembly.GetType(fullName, false)) .FirstOrDefault(type => type != null); } private static void SetBoolField(Type type, object instance, string fieldName, bool value) { type.GetField(fieldName, BindingFlags.Public | BindingFlags.Instance)?.SetValue(instance, value); } private static void AddStringToArrayField(Type type, object instance, string fieldName, string value) { var field = type.GetField(fieldName, BindingFlags.Public | BindingFlags.Instance); if (field == null) return; var values = ((string[])field.GetValue(instance) ?? Array.Empty()).ToList(); if (!values.Contains(value)) { values.Add(value); field.SetValue(instance, values.ToArray()); } } } }