From 448feacbaaad6fed8cac0f16f72d24a241ba0b51 Mon Sep 17 00:00:00 2001 From: lwb <q3213421wrwqr> Date: 星期三, 13 一月 2021 19:08:06 +0800 Subject: [PATCH] 9527 InjectFix --- Assets/IFix/Editor/ILFixEditor.cs.meta | 12 IFixToolKit/Mono.Cecil.Mdb.dll | 0 Assets/IFix/Editor.meta | 9 Assets/Editor/AssetBundleBrowser/AssetBundleBuildTab.cs | 37 + Assets/IFix.meta | 8 IFixToolKit/Mono.Cecil.Pdb.dll | 0 Assets/Plugins/IFix.Core.dll.meta | 33 + Assets/IFix/Editor/ILFixEditor.cs | 875 ++++++++++++++++++++++++++++++++++++++++++++++ Assets/IFix/Editor/Configure.cs.meta | 12 IFixToolKit/Mono.Cecil.dll | 0 IFixToolKit/IFix.exe.mdb | 0 Assets/Plugins/IFix.Core.dll | 0 Assets/IFix/Editor/Configure.cs | 152 ++++++++ 13 files changed, 1,136 insertions(+), 2 deletions(-) diff --git a/Assets/Editor/AssetBundleBrowser/AssetBundleBuildTab.cs b/Assets/Editor/AssetBundleBrowser/AssetBundleBuildTab.cs index ab2b5ea..56f7575 100644 --- a/Assets/Editor/AssetBundleBrowser/AssetBundleBuildTab.cs +++ b/Assets/Editor/AssetBundleBrowser/AssetBundleBuildTab.cs @@ -6,6 +6,7 @@ using UnityEngine.AssetBundles.AssetBundleDataSource; using Assets.Editor.Tool; +using IFix.Editor; namespace UnityEngine.AssetBundles { @@ -366,6 +367,11 @@ EditorApplication.delayCall += ExcuteBuildMobEffectShader; } + if (GUILayout.Button("patch")) + { + EditorApplication.delayCall += ExcuteBuildPatch; + } + EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); @@ -550,6 +556,7 @@ ExcuteBuildUI(); ExcuteBuildBuiltIn(); ExcuteBuildLua(); + ExcuteBuildPatch(); AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate); @@ -630,6 +637,31 @@ UpdateLuaSetting.SetAllLuaAssetBundleName(); ExcuteBuildAsset("lua"); LuaBuildHelper.OnPostBuild(); + } + + private void ExcuteBuildPatch() + { + string sourcePath; + if (m_UserData.m_BuildTarget == ValidBuildTarget.Android) + { + IFixEditor.CompileToAndroid(); + sourcePath = ResourcesPath.PATCH_ANDROID; + } + else if (m_UserData.m_BuildTarget == ValidBuildTarget.iOS) + { + IFixEditor.CompileToIOS(); + sourcePath = ResourcesPath.PATCH_IOS; + } + else + { + IFixEditor.Patch(); + sourcePath = ResourcesPath.PATCH_EDITOR; + } + var outputPath = Application.dataPath.Replace("Assets", m_UserData.m_OutputPath); + outputPath = StringUtility.Contact(outputPath, "/patch"); + if (Directory.Exists(outputPath)) + Directory.Delete(outputPath, true); + DirectoryCopy(sourcePath, outputPath, ".meta"); } private void ExcuteBuildLevels() @@ -768,7 +800,7 @@ } } - private static void DirectoryCopy(string sourceDirName, string destDirName) + private static void DirectoryCopy(string sourceDirName, string destDirName, string excludeEx = null) { // Get the subdirectories for the specified directory. DirectoryInfo dir = new DirectoryInfo(sourceDirName); @@ -784,7 +816,8 @@ foreach (FileInfo file in files) { string temppath = Path.Combine(destDirName, file.Name); - file.CopyTo(temppath, false); + if (excludeEx == null || file.Extension != excludeEx) + file.CopyTo(temppath, false); } DirectoryInfo[] dirs = dir.GetDirectories(); diff --git a/Assets/IFix.meta b/Assets/IFix.meta new file mode 100644 index 0000000..e5cdf9e --- /dev/null +++ b/Assets/IFix.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: efab94b801c78c54289af83a0e4e82fb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/IFix/Editor.meta b/Assets/IFix/Editor.meta new file mode 100644 index 0000000..3a5016d --- /dev/null +++ b/Assets/IFix/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 89d3afd1ac6383a4bb803e86127d5ed3 +folderAsset: yes +timeCreated: 1514965388 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/IFix/Editor/Configure.cs b/Assets/IFix/Editor/Configure.cs new file mode 100644 index 0000000..413f449 --- /dev/null +++ b/Assets/IFix/Editor/Configure.cs @@ -0,0 +1,152 @@ +/* + * Tencent is pleased to support the open source community by making InjectFix available. + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. + * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. + */ + +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Collections; +using System; + +/************************************************************************************************ + * 閰嶇疆 + * 1銆両Fix銆両nterpret銆丷everseWrapper椤绘斁鍒颁竴涓墦浜咰onfigure鏍囩鐨勭被閲岋紱 + * 2銆両Fix銆両nterpret銆丷everseWrapper鍧囩敤鎵撲簡鐩稿簲鏍囩鐨勫睘鎬ф潵琛ㄧず锛� + * 3銆両Fix銆両nterpret銆丷everseWrapper閰嶇疆椤绘斁鍒癊ditor鐩綍涓嬶紱 +*************************************************************************************************/ + +namespace IFix +{ + + [Configure] + public class InterpertConfig + { + [IFix] + static IEnumerable<Type> ToProcess + { + get + { + return (from type in Assembly.Load("Assembly-CSharp").GetTypes() + where type.Namespace == null || (!type.Namespace.StartsWith("XLua") && !type.Namespace.StartsWith("IFix")) + select type); + } + } + } + + //鏀剧疆閰嶇疆鐨� + [AttributeUsage(AttributeTargets.Class)] + public class ConfigureAttribute : Attribute + { + + } + + //榛樿鎵ц鍘熺敓浠g爜锛岃兘鍒囨崲鍒拌В鏋愭墽琛岋紝蹇呴』鏀惧湪鏍囪浜咰onfigure鐨勭被閲� + [AttributeUsage(AttributeTargets.Property)] + public class IFixAttribute : Attribute + { + } + + //鐢熸垚鍙嶅悜锛堣В鏋愯皟鐢ㄥ師鐢燂級灏佽鍣紝鍔犻�熻皟鐢ㄦ�ц兘 + [AttributeUsage(AttributeTargets.Property)] + public class ReverseWrapperAttribute : Attribute + { + } + + [AttributeUsage(AttributeTargets.Method)] + public class FilterAttribute : Attribute + { + } + + public static class Configure + { + // + public static Dictionary<string, List<KeyValuePair<object, int>>> GetConfigureByTags(List<string> tags) + { + var types = from assembly in AppDomain.CurrentDomain.GetAssemblies() + where !(assembly.ManifestModule is System.Reflection.Emit.ModuleBuilder) + from type in assembly.GetTypes() + where type.IsDefined(typeof(ConfigureAttribute), false) + select type; + var tagsMap = tags.ToDictionary(t => t, t => new List<KeyValuePair<object, int>>()); + + foreach (var type in types) + { + foreach (var prop in type.GetProperties(BindingFlags.Static | BindingFlags.Public + | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) + { + if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType)) + { + foreach (var ca in prop.GetCustomAttributes(false)) + { + int flag = 0; + var fp = ca.GetType().GetProperty("Flag"); + if (fp != null) + { + flag = (int)fp.GetValue(ca, null); + } + List<KeyValuePair<object, int>> infos; + if (tagsMap.TryGetValue(ca.GetType().ToString(), out infos)) + { + foreach (var applyTo in prop.GetValue(null, null) as IEnumerable) + { + infos.Add(new KeyValuePair<object, int>(applyTo, flag)); + } + } + } + } + } + } + return tagsMap; + } + + public static List<MethodInfo> GetFilters() + { + var types = from assembly in AppDomain.CurrentDomain.GetAssemblies() + where !(assembly.ManifestModule is System.Reflection.Emit.ModuleBuilder) + from type in assembly.GetTypes() + where type.IsDefined(typeof(ConfigureAttribute), false) + select type; + + List<MethodInfo> filters = new List<MethodInfo>(); + foreach (var type in types) + { + foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public + | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) + { + if (method.IsDefined(typeof(IFix.FilterAttribute), false)) + { + filters.Add(method); + } + } + } + return filters; + } + + public static IEnumerable<MethodInfo> GetTagMethods(Type tagType, string searchAssembly) + { + return (from assembly in AppDomain.CurrentDomain.GetAssemblies() + where !(assembly.ManifestModule is System.Reflection.Emit.ModuleBuilder) + && (assembly.GetName().Name == searchAssembly) + where assembly.CodeBase.IndexOf("ScriptAssemblies") != -1 + from type in assembly.GetTypes() + from method in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public + | BindingFlags.NonPublic) + where method.IsDefined(tagType, false) + select method); + } + public static IEnumerable<Type> GetTagClasses(Type tagType, string searchAssembly) + { + return (from assembly in AppDomain.CurrentDomain.GetAssemblies() + where !(assembly.ManifestModule is System.Reflection.Emit.ModuleBuilder) + && (assembly.GetName().Name == searchAssembly) + where assembly.CodeBase.IndexOf("ScriptAssemblies") != -1 + from type in assembly.GetTypes() + where type.IsDefined(tagType, false) + select type + ); + } + } +} diff --git a/Assets/IFix/Editor/Configure.cs.meta b/Assets/IFix/Editor/Configure.cs.meta new file mode 100644 index 0000000..7b960a2 --- /dev/null +++ b/Assets/IFix/Editor/Configure.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: aca776d82af199a4b9018dcc5c8cceec +timeCreated: 1514363894 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/IFix/Editor/ILFixEditor.cs b/Assets/IFix/Editor/ILFixEditor.cs new file mode 100644 index 0000000..4368443 --- /dev/null +++ b/Assets/IFix/Editor/ILFixEditor.cs @@ -0,0 +1,875 @@ +/* + * Tencent is pleased to support the open source community by making InjectFix available. + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. + * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. + */ + +using UnityEngine; +using UnityEditor; +using System.Collections.Generic; +using System.IO; +using System; +using System.Linq; +using System.Diagnostics; +using System.Text.RegularExpressions; +using System.Text; +using System.Reflection; +#if UNITY_2018_3_OR_NEWER +using UnityEditor.Build.Player; +#endif + +namespace IFix.Editor +{ + //鐗堟湰閫夋嫨绐楀彛 + public class VersionSelector : EditorWindow + { + public string buttonText = "Patch"; + public string[] options = new string[] { }; + public int index = 0; + public Action<int> callback = null; + + public static void Show(string[] options, Action<int> callback, string buttonText = "Patch") + { + VersionSelector window = GetWindow<VersionSelector>(); + window.options = options; + window.callback = callback; + window.buttonText = buttonText; + window.Show(); + } + + void OnGUI() + { + index = EditorGUILayout.Popup("Please select a version: ", index, options); + if (GUILayout.Button(buttonText)) + doPatch(); + } + + void doPatch() + { + if (callback != null) + { + callback(index); + } + Close(); + } + } + + public class IFixEditor + { + //澶囦唤鐩綍 + const string BACKUP_PATH = "./IFixDllBackup"; + //澶囦唤鏂囦欢鐨勬椂闂存埑鐢熸垚鏍煎紡 + const string TIMESTAMP_FORMAT = "yyyyMMddHHmmss"; + + //娉ㄥ叆鐨勭洰鏍囨枃浠跺す + private static string targetAssembliesFolder = ""; + + //system("mono ifix.exe [args]") + public static void CallIFix(List<string> args) + { +#if UNITY_EDITOR_OSX + var mono_path = Path.Combine(Path.GetDirectoryName(typeof(UnityEngine.Debug).Module.FullyQualifiedName), + "../MonoBleedingEdge/bin/mono"); + if(!File.Exists(mono_path)) + { + mono_path = Path.Combine(Path.GetDirectoryName(typeof(UnityEngine.Debug).Module.FullyQualifiedName), + "../../MonoBleedingEdge/bin/mono"); + } +#elif UNITY_EDITOR_WIN + var mono_path = Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), + "Data/MonoBleedingEdge/bin/mono.exe"); +#endif + if (!File.Exists(mono_path)) + { + UnityEngine.Debug.LogError("can not find mono!"); + } + var inject_tool_path = "./IFixToolKit/IFix.exe"; + //"--runtime = v4.0.30319" + if (!File.Exists(inject_tool_path)) + { + UnityEngine.Debug.LogError("please install the ToolKit"); + return; + } + + Process hotfix_injection = new Process(); + hotfix_injection.StartInfo.FileName = mono_path; +#if UNITY_5_6_OR_NEWER + hotfix_injection.StartInfo.Arguments = "--debug --runtime=v4.0.30319 \"" + inject_tool_path + "\" \"" +#else + hotfix_injection.StartInfo.Arguments = "--debug \"" + inject_tool_path + "\" \"" +#endif + + string.Join("\" \"", args.ToArray()) + "\""; + hotfix_injection.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; + hotfix_injection.StartInfo.RedirectStandardOutput = true; + hotfix_injection.StartInfo.UseShellExecute = false; + hotfix_injection.StartInfo.CreateNoWindow = true; + hotfix_injection.Start(); + + //UnityEngine.Debug.Log(hotfix_injection.StartInfo.FileName); + //UnityEngine.Debug.Log(hotfix_injection.StartInfo.Arguments); + + StringBuilder exceptionInfo = null; + while (!hotfix_injection.StandardOutput.EndOfStream) + { + string line = hotfix_injection.StandardOutput.ReadLine(); + if (exceptionInfo != null) + { + exceptionInfo.AppendLine(line); + } + else + { + if (line.StartsWith("Warning:")) + { + UnityEngine.Debug.LogWarning(line); + } + else if (line.StartsWith("Error:")) + { + UnityEngine.Debug.LogError(line); + } + else if (line.StartsWith("Unhandled Exception:")) + { + exceptionInfo = new StringBuilder(line); + } + else + { + UnityEngine.Debug.Log(line); + } + } + } + hotfix_injection.WaitForExit(); + if (exceptionInfo != null) + { + UnityEngine.Debug.LogError(exceptionInfo); + } + } + + [MenuItem("InjectFix/Inject", false, 1)] + public static void InjectAssemblys() + { + if (EditorApplication.isCompiling || Application.isPlaying) + { + UnityEngine.Debug.LogError("compiling or playing"); + return; + } + EditorUtility.DisplayProgressBar("Inject", "injecting...", 0); + try + { + InjectAllAssemblys(); + } + catch (Exception e) + { + UnityEngine.Debug.LogError(e); + } + EditorUtility.ClearProgressBar(); + } + + public static bool AutoInject = true; //鍙互鍦ㄥ閮ㄧ鐢ㄦ帀鑷姩娉ㄥ叆 + + public static bool InjectOnce = false; //AutoInjectAssemblys鍙皟鐢ㄤ竴娆★紝鍙互闃叉鑷姩鍖栨墦鍖呮椂锛屽緢澶氬満鏅鑷碅utoInjectAssemblys琚娆¤皟鐢� + + static bool injected = false; + + [UnityEditor.Callbacks.PostProcessScene] + public static void AutoInjectAssemblys() + { + if (AutoInject && !injected) + { + InjectAllAssemblys(); + if (InjectOnce) + { + injected = true; + } + } + } + + //鑾峰彇澶囦唤鏂囦欢淇℃伅 + public static void GetBackupInfo(out string[] backups, out string[] timestamps) + { + string pattern = @"Assembly-CSharp-(\d{14})\.dll$"; + Regex r = new Regex(pattern); + + var allBackup = Directory.GetFiles(BACKUP_PATH).Where(path => r.Match(path).Success) + .Select(path => path.Replace('\\', '/')).ToList(); + allBackup.Sort(); + + backups = allBackup.Select(path => r.Match(path).Groups[1].Captures[0].Value).ToArray(); + timestamps = allBackup.Select(path => DateTime.ParseExact(r.Match(path).Groups[1].Captures[0].Value, + TIMESTAMP_FORMAT, System.Globalization.CultureInfo.InvariantCulture) + .ToString("yyyy-MM-dd hh:mm:ss tt")).ToArray(); + } + + //閫夋嫨澶囦唤 + public static void SelectBackup(string buttonText, Action<string> cb) + { + string[] backups; + string[] timestamps; + GetBackupInfo(out backups, out timestamps); + + VersionSelector.Show(timestamps.ToArray(), index => + { + cb(backups[index]); + }, buttonText); + } + + /// <summary> + /// 瀵规寚瀹氱殑绋嬪簭闆嗘敞鍏� + /// </summary> + /// <param name="assembly">绋嬪簭闆嗚矾寰�</param> + public static void InjectAssembly(string assembly) + { + var configure = Configure.GetConfigureByTags(new List<string>() { + "IFix.IFixAttribute", + "IFix.InterpretAttribute", + "IFix.ReverseWrapperAttribute", + }); + + var filters = Configure.GetFilters(); + + var processCfgPath = "./process_cfg"; + + //璇ョ▼搴忛泦鏄惁鏈夐厤缃簡浜涚被锛屽鏋滄病鏈夊氨璺宠繃娉ㄥ叆鎿嶄綔 + bool hasSomethingToDo = false; + + var blackList = new List<MethodInfo>(); + + using (BinaryWriter writer = new BinaryWriter(new FileStream(processCfgPath, FileMode.Create, + FileAccess.Write))) + { + writer.Write(configure.Count); + + foreach (var kv in configure) + { + writer.Write(kv.Key); + + var typeList = kv.Value.Where(item => item.Key is Type) + .Select(item => new KeyValuePair<Type, int>(item.Key as Type, item.Value)) + .Where(item => item.Key.Assembly.GetName().Name == assembly) + .ToList(); + writer.Write(typeList.Count); + + if (typeList.Count > 0) + { + hasSomethingToDo = true; + } + + foreach (var cfgItem in typeList) + { + writer.Write(GetCecilTypeName(cfgItem.Key)); + writer.Write(cfgItem.Value); + if (filters.Count > 0 && kv.Key == "IFix.IFixAttribute") + { + foreach (var method in cfgItem.Key.GetMethods(BindingFlags.Instance + | BindingFlags.Static | BindingFlags.Public + | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) + { + foreach (var filter in filters) + { + if ((bool)filter.Invoke(null, new object[] + { + method + })) + { + blackList.Add(method); + } + } + } + } + } + } + + writeMethods(writer, blackList); + } + + if (hasSomethingToDo) + { + + var core_path = "./Assets/Plugins/IFix.Core.dll"; + var assembly_path = string.Format("./Library/{0}/{1}.dll", targetAssembliesFolder, assembly); + var patch_path = string.Format("./{0}.ill.bytes", assembly); + List<string> args = new List<string>() { "-inject", core_path, assembly_path, + processCfgPath, patch_path, assembly_path }; + + foreach (var path in + (from asm in AppDomain.CurrentDomain.GetAssemblies() + select Path.GetDirectoryName(asm.ManifestModule.FullyQualifiedName)).Distinct()) + { + try + { + //UnityEngine.Debug.Log("searchPath:" + path); + args.Add(path); + } + catch { } + } + + CallIFix(args); + } + + File.Delete(processCfgPath); + } + + /// <summary> + /// 瀵筰njectAssemblys閲岀殑绋嬪簭闆嗚繘琛屾敞鍏ワ紝鐒跺悗澶囦唤 + /// </summary> + public static void InjectAllAssemblys() + { + if (EditorApplication.isCompiling || Application.isPlaying) + { + return; + } + + targetAssembliesFolder = GetScriptAssembliesFolder(); + + foreach (var assembly in injectAssemblys) + { + InjectAssembly(assembly); + } + + //doBackup(DateTime.Now.ToString(TIMESTAMP_FORMAT)); + + AssetDatabase.Refresh(); + } + + private static string GetScriptAssembliesFolder() + { + var assembliesFolder = "PlayerScriptAssemblies"; + if (!Directory.Exists(string.Format("./Library/{0}/", assembliesFolder))) + { + assembliesFolder = "ScriptAssemblies"; + } + return assembliesFolder; + } + + //榛樿鐨勬敞鍏ュ強澶囦唤绋嬪簭闆� + //鍙﹀鍙互鐩存帴璋冪敤InjectAssembly瀵瑰叾瀹冪▼搴忛泦杩涜娉ㄥ叆銆� + static string[] injectAssemblys = new string[] + { + "Assembly-CSharp", + "Assembly-CSharp-firstpass" + }; + + /// <summary> + /// 鎶婃敞鍏ュ悗鐨勭▼搴忛泦澶囦唤 + /// </summary> + /// <param name="ts">鏃堕棿鎴�</param> + static void doBackup(string ts) + { + if (!Directory.Exists(BACKUP_PATH)) + { + Directory.CreateDirectory(BACKUP_PATH); + } + + var scriptAssembliesDir = string.Format("./Library/{0}/", targetAssembliesFolder); + + foreach (var assembly in injectAssemblys) + { + var dllFile = string.Format("{0}{1}.dll", scriptAssembliesDir, assembly); + if (!File.Exists(dllFile)) + { + continue; + } + File.Copy(dllFile, string.Format("{0}/{1}-{2}.dll", BACKUP_PATH, assembly, ts), true); + + var mdbFile = string.Format("{0}{1}.dll.mdb", scriptAssembliesDir, assembly); + if (File.Exists(mdbFile)) + { + File.Copy(mdbFile, string.Format("{0}/{1}-{2}.dll.mdb", BACKUP_PATH, assembly, ts), true); + } + + var pdbFile = string.Format("{0}{1}.dll.pdb", scriptAssembliesDir, assembly); + if (File.Exists(pdbFile)) + { + File.Copy(pdbFile, string.Format("{0}/{1}-{2}.dll.pdb", BACKUP_PATH, assembly, ts), true); + } + } + } + + /// <summary> + /// 鎭㈠鏌愪釜閫夊畾鐨勫浠� + /// </summary> + /// <param name="ts">鏃堕棿鎴�</param> + static void doRestore(string ts) + { + var scriptAssembliesDir = string.Format("./Library/{0}/", targetAssembliesFolder); + + foreach (var assembly in injectAssemblys) + { + var dllFile = string.Format("{0}/{1}-{2}.dll", BACKUP_PATH, assembly, ts); + if (!File.Exists(dllFile)) + { + continue; + } + File.Copy(dllFile, string.Format("{0}{1}.dll", scriptAssembliesDir, assembly), true); + UnityEngine.Debug.Log("Revert to: " + dllFile); + + var mdbFile = string.Format("{0}/{1}-{2}.dll.mdb", BACKUP_PATH, assembly, ts); + if (File.Exists(mdbFile)) + { + File.Copy(mdbFile, string.Format("{0}{1}.dll.mdb", scriptAssembliesDir, assembly), true); + UnityEngine.Debug.Log("Revert to: " + mdbFile); + } + + var pdbFile = string.Format("{0}/{1}-{2}.dll.pdb", BACKUP_PATH, assembly, ts); + if (File.Exists(pdbFile)) + { + File.Copy(pdbFile, string.Format("{0}{1}.dll.pdb", scriptAssembliesDir, assembly), true); + UnityEngine.Debug.Log("Revert to: " + pdbFile); + } + } + } + + //cecil閲岀殑绫诲悕琛ㄧず鍜�.net鏍囧噯骞朵笉涓�鏍凤紝杩欓噷鍋氫簺杞崲 + static string GetCecilTypeName(Type type) + { + if (type.IsByRef && type.GetElementType().IsGenericType) + { + return GetCecilTypeName(type.GetElementType()) + "&"; + } + else if (type.IsGenericType) + { + if (type.IsGenericTypeDefinition) + { + return type.ToString().Replace('+', '/').Replace('[', '<').Replace(']', '>'); + } + else + { + return Regex.Replace(type.ToString().Replace('+', '/'), @"(`\d).+", "$1") + + "<" + string.Join(",", type.GetGenericArguments().Select(t => GetCecilTypeName(t)) + .ToArray()) + ">"; + } + } + else + { + return type.FullName.Replace('+', '/'); + } + } + + //鐩墠鏀寔鐨勫钩鍙扮紪璇� + public enum Platform + { + android, + ios, + standalone + } + + //缂撳瓨锛氳В鏋愬ソ鐨勭紪璇戝弬鏁� + private static Dictionary<Platform, string> compileTemplates = new Dictionary<Platform, string>(); + + //瑙f瀽unity鐨勭紪璇戝弬鏁� + private static string parseCompileTemplate(string path) + { + return string.Join("\n", File.ReadAllLines(path).Where(line => !line.StartsWith("Assets/") + && !line.StartsWith("\"Assets/") + && !line.StartsWith("'Assets/") + && !line.StartsWith("-r:Assets/") + && !line.StartsWith("-r:\"Assets/") + && !line.StartsWith("-r:'Assets/") + && !line.StartsWith("-out") + ).ToArray()); + } + + //瀵硅矾寰勯澶勭悊锛岀劧鍚庢坊鍔犲埌StringBuilder + //瑙勫垯锛氬鏋滆矾寰勫惈绌烘牸锛屽垯鍔犱笂鍙屽紩鍙� + static void appendFile(StringBuilder sb, string path) + { + if (path.IndexOf(' ') > 0) + { + sb.Append('"'); + sb.Append(path); + sb.Append('"'); + sb.AppendLine(); + } + else + { + sb.AppendLine(path); + } + } + + //鑷姩鍔犲叆婧愮爜 + private static void appendDirectory(StringBuilder src, string dir) + { + foreach (var file in Directory.GetFiles(dir)) + { + //鎺掗櫎璋僂ditor涓嬬殑涓滆タ + if (file.IndexOf(Path.DirectorySeparatorChar + "Editor" + Path.DirectorySeparatorChar) > 0) + { + continue; + } + //鎺掗櫎Assembly-CSharp-firstpass + if (file.Substring(file.Length - 3).ToLower() == ".cs") + { + if (file.StartsWith("Assets" + Path.DirectorySeparatorChar + "Plugins") || + file.StartsWith("Assets" + Path.DirectorySeparatorChar + "Standard Assets") || + file.StartsWith("Assets" + Path.DirectorySeparatorChar + "Pro Standard Assets")) + { + continue; + } + appendFile(src, file); + } + } + + foreach (var subDir in Directory.GetDirectories(dir)) + { + appendDirectory(src, subDir); + } + } + + //閫氳繃妯℃澘鏂囦欢锛岃幏鍙栫紪璇戝弬鏁� + private static string getCompileArguments(Platform platform, string output) + { + string compileTemplate; + if (!compileTemplates.TryGetValue(platform, out compileTemplate)) + { +#if UNITY_EDITOR_WIN + var hostPlatform = "win"; +#elif UNITY_EDITOR_OSX + var hostPlatform = "osx"; +#else + var hostPlatform = "linux"; +#endif + var path = "IFixToolKit/" + platform + "." + hostPlatform + ".tpl"; + if (!File.Exists(path)) + { + path = "IFixToolKit/" + platform + ".tpl"; + if (!File.Exists(path)) + { + throw new InvalidOperationException("please put template file for " + platform + + " in IFixToolKit directory!"); + } + } + compileTemplate = parseCompileTemplate(path); + compileTemplates.Add(platform, compileTemplate); + } + StringBuilder cmd = new StringBuilder(); + StringBuilder src = new StringBuilder(); + StringBuilder dll = new StringBuilder(); + + appendDirectory(src, "Assets"); + var projectDir = Application.dataPath.Replace(Path.DirectorySeparatorChar, '/'); + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + try + { +#if (UNITY_EDITOR || XLUA_GENERAL) && !NET_STANDARD_2_0 + if (!(assembly.ManifestModule is System.Reflection.Emit.ModuleBuilder)) + { +#endif + var assemblyPath = assembly.ManifestModule.FullyQualifiedName + .Replace(Path.DirectorySeparatorChar, '/'); + if (assemblyPath.StartsWith(projectDir)) + { + dll.Append("-r:"); + appendFile(dll, assemblyPath.Replace(projectDir, "Assets")); + } +#if (UNITY_EDITOR || XLUA_GENERAL) && !NET_STANDARD_2_0 + } +#endif + } + catch { } + } + + cmd.AppendLine(compileTemplate); + cmd.Append(dll.ToString()); + cmd.Append(src.ToString()); + cmd.AppendLine("-sdk:2"); + cmd.Append("-out:"); + appendFile(cmd, output); + + return cmd.ToString(); + } + + //1銆佽В鏋愮紪璇戝弬鏁帮紙澶勭悊鍏冩枃浠朵箣澶栫殑缂栬瘧鍙傛暟锛� + //2銆佹悳绱㈠伐绋嬬殑c#婧愮爜锛屽姞涓婄紪璇戝弬鏁扮紪璇� + //3銆佺紪璇慉ssembly-CSharp.dll + //TODO: 鍙敮鎸丄ssembly-CSharp.dll锛屼絾杈冩柊鐗堟湰Unity宸茬粡鏀寔澶歞ll鎷嗗垎 + //TODO: 鐩墠鐨勫仛娉曟尯绻佺悙鐨勶紝闇�瑕佺敤鎴峰幓鑾峰彇Unity鐨勭紪璇戝懡浠ゆ枃浠讹紝鏇村ソ鐨勫仛娉曞簲璇ユ槸鐩存帴 + public static void Compile(string compileArgFile) + { +#if UNITY_EDITOR_OSX + var monoPath = Path.Combine(Path.GetDirectoryName(typeof(UnityEngine.Debug).Module.FullyQualifiedName), + "../MonoBleedingEdge/bin/mono"); + var mcsPath = Path.Combine(Path.GetDirectoryName(typeof(UnityEngine.Debug).Module.FullyQualifiedName), + "../MonoBleedingEdge/lib/mono/4.5/mcs.exe"); + if(!File.Exists(monoPath)) + { + monoPath = Path.Combine(Path.GetDirectoryName(typeof(UnityEngine.Debug).Module.FullyQualifiedName), + "../../MonoBleedingEdge/bin/mono"); + mcsPath = Path.Combine(Path.GetDirectoryName(typeof(UnityEngine.Debug).Module.FullyQualifiedName), + "../../MonoBleedingEdge/lib/mono/4.5/mcs.exe"); + } +#elif UNITY_EDITOR_WIN + var monoPath = Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), + "Data/MonoBleedingEdge/bin/mono.exe"); + var mcsPath = Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), + "Data/MonoBleedingEdge/lib/mono/4.5/mcs.exe"); +#endif + if (!File.Exists(monoPath)) + { + UnityEngine.Debug.LogError("can not find mono!"); + } + + Process compileProcess = new Process(); + compileProcess.StartInfo.FileName = monoPath; + compileProcess.StartInfo.Arguments = "\"" + mcsPath + "\" " + "@" + compileArgFile; + compileProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; + compileProcess.StartInfo.RedirectStandardOutput = true; + compileProcess.StartInfo.RedirectStandardError = true; + compileProcess.StartInfo.UseShellExecute = false; + compileProcess.StartInfo.CreateNoWindow = true; + compileProcess.Start(); + + //UnityEngine.Debug.Log(hotfix_injection.StartInfo.FileName); + //UnityEngine.Debug.Log(hotfix_injection.StartInfo.Arguments); + + while (!compileProcess.StandardError.EndOfStream) + { + UnityEngine.Debug.LogError(compileProcess.StandardError.ReadLine()); + } + + while (!compileProcess.StandardOutput.EndOfStream) + { + UnityEngine.Debug.Log(compileProcess.StandardOutput.ReadLine()); + } + + compileProcess.WaitForExit(); + } + + //鐢熸垚鐗瑰畾骞冲彴鐨刾atch + public static void GenPlatformPatch(Platform platform, string patchOutputDir, + string corePath = "./Assets/Plugins/IFix.Core.dll") + { + var outputDir = "Temp/ifix"; + Directory.CreateDirectory("Temp"); + Directory.CreateDirectory(outputDir); +#if UNITY_2018_3_OR_NEWER + ScriptCompilationSettings scriptCompilationSettings = new ScriptCompilationSettings(); + if (platform == Platform.android) + { + scriptCompilationSettings.group = BuildTargetGroup.Android; + scriptCompilationSettings.target = BuildTarget.Android; + } + else if (platform == Platform.ios) + { + scriptCompilationSettings.group = BuildTargetGroup.iOS; + scriptCompilationSettings.target = BuildTarget.iOS; + } + else + { + scriptCompilationSettings.group = BuildTargetGroup.Standalone; + scriptCompilationSettings.target = BuildTarget.StandaloneWindows; + } + + ScriptCompilationResult scriptCompilationResult = PlayerBuildInterface.CompilePlayerScripts(scriptCompilationSettings, outputDir); + + if (Directory.Exists(patchOutputDir)) + Directory.Delete(patchOutputDir, true); + Directory.CreateDirectory(patchOutputDir); + foreach (var assembly in injectAssemblys) + { + GenPatch(assembly, string.Format("{0}/{1}.dll", outputDir, assembly), + "./Assets/Plugins/IFix.Core.dll", string.Format("{0}{1}.patch.bytes", patchOutputDir, assembly)); + } +#else + throw new NotImplementedException(); + //var compileArgFile = "Temp/ifix/unity_" + platform + "_compile_argument"; + //var tmpDllPath = "Temp/ifix/Assembly-CSharp.dll"; + //File.WriteAllText(compileArgFile, getCompileArguments(platform, tmpDllPath)); + //鍏堢紪璇慸ll鍒癟emp鐩綍涓� + //Compile(compileArgFile); + //瀵圭紪璇戝悗鐨刣ll鐢熸垚琛ヤ竵 + //GenPatch("Assembly-CSharp", tmpDllPath, corePath, patchPath); + + //File.Delete(compileArgFile); + //File.Delete(tmpDllPath); + //File.Delete(tmpDllPath + ".mdb"); +#endif + } + + //鎶婃柟娉曠鍚嶅啓鍏ユ枃浠� + //鐢变簬鐩墠涓嶆敮鎸佹硾鍨嬪嚱鏁扮殑patch锛屾墍浠ュ嚱鏁扮鍚嶄负鏂规硶鍚�+鍙傛暟绫诲瀷 + static void writeMethods(BinaryWriter writer, List<MethodInfo> methods) + { + var methodGroups = methods.GroupBy(m => m.DeclaringType).ToList(); + writer.Write(methodGroups.Count); + foreach (var methodGroup in methodGroups) + { + writer.Write(GetCecilTypeName(methodGroup.Key)); + writer.Write(methodGroup.Count()); + foreach (var method in methodGroup) + { + writer.Write(method.Name); + writer.Write(GetCecilTypeName(method.ReturnType)); + writer.Write(method.GetParameters().Length); + foreach (var parameter in method.GetParameters()) + { + writer.Write(parameter.IsOut); + writer.Write(GetCecilTypeName(parameter.ParameterType)); + } + } + } + } + + static void writeClasses(BinaryWriter writer, List<Type> classes) + { + writer.Write(classes.Count); + foreach (var classGroup in classes) + { + writer.Write(GetCecilTypeName(classGroup)); + } + } + + static bool hasGenericParameter(Type type) + { + if (type.IsByRef || type.IsArray) + { + return hasGenericParameter(type.GetElementType()); + } + if (type.IsGenericType) + { + foreach (var typeArg in type.GetGenericArguments()) + { + if (hasGenericParameter(typeArg)) + { + return true; + } + } + return false; + } + return type.IsGenericParameter; + } + + static bool hasGenericParameter(MethodBase method) + { + if (method.IsGenericMethodDefinition || method.IsGenericMethod) return true; + if (!method.IsConstructor && hasGenericParameter((method as MethodInfo).ReturnType)) return true; + + foreach (var param in method.GetParameters()) + { + if (hasGenericParameter(param.ParameterType)) + { + return true; + } + } + return false; + + } + + /// <summary> + /// 鐢熸垚patch + /// </summary> + /// <param name="assembly">绋嬪簭闆嗗悕锛岀敤鏉ヨ繃婊ら厤缃�</param> + /// <param name="assemblyCSharpPath">绋嬪簭闆嗚矾寰�</param> + /// <param name="corePath">IFix.Core.dll鎵�鍦ㄨ矾寰�</param> + /// <param name="patchPath">鐢熸垚鐨刾atch鐨勪繚瀛樿矾寰�</param> + public static void GenPatch(string assembly, string assemblyCSharpPath + = "./Library/ScriptAssemblies/Assembly-CSharp.dll", + string corePath = "./Assets/Plugins/IFix.Core.dll", string patchPath = ResourcesPath.PATCH_EDITOR) + { + var patchMethods = Configure.GetTagMethods(typeof(PatchAttribute), assembly).ToList(); + var genericMethod = patchMethods.FirstOrDefault(m => hasGenericParameter(m)); + if (genericMethod != null) + { + throw new InvalidDataException("not support generic method: " + genericMethod); + } + + if (patchMethods.Count == 0) + { + return; + } + + var newMethods = Configure.GetTagMethods(typeof(InterpretAttribute), assembly).ToList(); + var newClasses = Configure.GetTagClasses(typeof(InterpretAttribute), assembly).ToList(); + genericMethod = newMethods.FirstOrDefault(m => hasGenericParameter(m)); + if (genericMethod != null) + { + throw new InvalidDataException("not support generic method: " + genericMethod); + } + + var processCfgPath = "./process_cfg"; + + using (BinaryWriter writer = new BinaryWriter(new FileStream(processCfgPath, FileMode.Create, + FileAccess.Write))) + { + writeMethods(writer, patchMethods); + writeMethods(writer, newMethods); + writeClasses(writer, newClasses); + } + + List<string> args = new List<string>() { "-patch", corePath, assemblyCSharpPath, "null", + processCfgPath, patchPath }; + + foreach (var path in + (from asm in AppDomain.CurrentDomain.GetAssemblies() + select Path.GetDirectoryName(asm.ManifestModule.FullyQualifiedName)).Distinct()) + { + try + { + //UnityEngine.Debug.Log("searchPath:" + path); + args.Add(path); + } + catch { } + } + + CallIFix(args); + + File.Delete(processCfgPath); + + AssetDatabase.Refresh(); + } + + [MenuItem("InjectFix/Fix", false, 2)] + public static void Patch() + { + EditorUtility.DisplayProgressBar("Generate Patch for Edtior", "patching...", 0); + try + { + if (Directory.Exists(ResourcesPath.PATCH_EDITOR)) + Directory.Delete(ResourcesPath.PATCH_EDITOR, true); + Directory.CreateDirectory(ResourcesPath.PATCH_EDITOR); + foreach (var assembly in injectAssemblys) + { + var assembly_path = string.Format("./Library/{0}/{1}.dll", GetScriptAssembliesFolder(), assembly); + GenPatch(assembly, assembly_path, "./Assets/Plugins/IFix.Core.dll", + string.Format("{0}{1}.patch.bytes", ResourcesPath.PATCH_EDITOR, assembly)); + } + } + catch (Exception e) + { + UnityEngine.Debug.LogError(e); + } + EditorUtility.ClearProgressBar(); + } + +#if UNITY_2018_3_OR_NEWER + [MenuItem("InjectFix/Fix(Android)", false, 3)] + public static void CompileToAndroid() + { + EditorUtility.DisplayProgressBar("Generate Patch for Android", "patching...", 0); + try + { + GenPlatformPatch(Platform.android, ResourcesPath.PATCH_ANDROID); + } + catch (Exception e) + { + UnityEngine.Debug.LogError(e); + } + EditorUtility.ClearProgressBar(); + } + + [MenuItem("InjectFix/Fix(IOS)", false, 4)] + public static void CompileToIOS() + { + EditorUtility.DisplayProgressBar("Generate Patch for IOS", "patching...", 0); + try + { + GenPlatformPatch(Platform.ios, ResourcesPath.PATCH_IOS); + } + catch (Exception e) + { + UnityEngine.Debug.LogError(e); + } + EditorUtility.ClearProgressBar(); + } +#endif + } +} diff --git a/Assets/IFix/Editor/ILFixEditor.cs.meta b/Assets/IFix/Editor/ILFixEditor.cs.meta new file mode 100644 index 0000000..37eb3ba --- /dev/null +++ b/Assets/IFix/Editor/ILFixEditor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: ea1d006806821c945ad3dfda116f04f7 +timeCreated: 1514863799 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/IFix.Core.dll b/Assets/Plugins/IFix.Core.dll new file mode 100644 index 0000000..30fe492 --- /dev/null +++ b/Assets/Plugins/IFix.Core.dll Binary files differ diff --git a/Assets/Plugins/IFix.Core.dll.meta b/Assets/Plugins/IFix.Core.dll.meta new file mode 100644 index 0000000..ae7e328 --- /dev/null +++ b/Assets/Plugins/IFix.Core.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7c6cf326f60d243479929e308c3123fb +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/IFixToolKit/IFix.exe.mdb b/IFixToolKit/IFix.exe.mdb new file mode 100644 index 0000000..154928f --- /dev/null +++ b/IFixToolKit/IFix.exe.mdb Binary files differ diff --git a/IFixToolKit/Mono.Cecil.Mdb.dll b/IFixToolKit/Mono.Cecil.Mdb.dll new file mode 100644 index 0000000..4ddd104 --- /dev/null +++ b/IFixToolKit/Mono.Cecil.Mdb.dll Binary files differ diff --git a/IFixToolKit/Mono.Cecil.Pdb.dll b/IFixToolKit/Mono.Cecil.Pdb.dll new file mode 100644 index 0000000..ddfe05e --- /dev/null +++ b/IFixToolKit/Mono.Cecil.Pdb.dll Binary files differ diff --git a/IFixToolKit/Mono.Cecil.dll b/IFixToolKit/Mono.Cecil.dll new file mode 100644 index 0000000..cb3321e --- /dev/null +++ b/IFixToolKit/Mono.Cecil.dll Binary files differ -- Gitblit v1.8.0