/// <summary>Retrieves the version from the game files</summary> /// <returns>An instance of <see cref="System.Version"/> containing the game's Unity version</returns> /// <exception cref="NotSupportedException">Thrown when an unsupported version of Unity is in use</exception> static Version GetUnityVersion() { string dataPath = PathDiscovery.FindDataDirectory(); var dataFiles = new[] { "globalgamemanagers", "mainData" }; if (!dataFiles.Any(p => File.Exists(Path.Combine(dataPath, p)))) { throw new NotSupportedException("Unsupported Unity version."); } string dataFile = dataFiles.First(p => File.Exists(Path.Combine(dataPath, p))); using (FileStream ggm = File.OpenRead(Path.Combine(dataPath, dataFile))) using (var reader = new BinaryReader(ggm)) { reader.ReadUInt32(); // metadataSize reader.ReadUInt32(); // fileSize uint format = reader.ReadUInt32(); reader.ReadUInt32(); // dataOffset if (format >= 9) { reader.ReadUInt32(); // endianness } if (7 <= format && format <= 13) { reader.ReadUInt32(); // longObjectIDs } var sb = new StringBuilder(); for (;;) { byte b = reader.ReadByte(); if (b == 0) { break; } sb.Append((char)b); } string[] split = sb.ToString().Split('.', 'b', 'f', 'p'); return(new Version ( Convert.ToInt32(split.Length > 0 ? split[0] : "0"), // major Convert.ToInt32(split.Length > 1 ? split[1] : "0"), // minor Convert.ToInt32(split.Length > 2 ? split[2] : "0"), // build // do we really care about this? Convert.ToInt32(split.Length > 3 ? split[3] : "0") // revision )); } }
static void Main() { string managedPath = PathDiscovery.FindManagedDirectory(); var resolver = new DefaultAssemblyResolver(); resolver.AddSearchDirectory(managedPath); Version version = GetUnityVersion(); // as of Unity 2017.2, the UnityEngine assembly has been split into multiple assemblies // the assembly containing the GameObject class is UnityEngine.CoreModule string coreName = version.Major < 2017 || (version.Major == 2017 && version.Minor == 1) ? "UnityEngine" : "UnityEngine.CoreModule"; string corePath = PathDiscovery.GetAssemblyPath(resolver, coreName); byte[] coreData = File.ReadAllBytes(corePath); Console.WriteLine("Unity {0} detected.", version); ModuleDefinition unityCore = ModuleDefinition.ReadModule( new MemoryStream(coreData), new ReaderParameters { AssemblyResolver = resolver }); TypeDefinition gameObject = unityCore.GetType("UnityEngine", "GameObject"); // UPM works by adding a static constructor to the GameObject class, // which calls an initialization function in PluginManager.Core SetStaticCtor(gameObject, GenStaticCtor(unityCore, il => { ModuleDefinition upm = ModuleDefinition.ReadModule(CoreLibrary); TypeDefinition upmMain = upm.GetType("PluginManager.Core", "PluginManager"); MethodDefinition upmInit = upmMain.Methods.Single(m => m.Name == "Initialize"); il.Emit(OpCodes.Call, unityCore.ImportReference(upmInit)); il.Emit(OpCodes.Ret); upm.Dispose(); })); unityCore.Write(corePath); // We need to copy PluginManager.Core.dll into the Managed directory File.Copy(CoreLibrary, Path.Combine(managedPath, CoreLibrary), true); Console.WriteLine("UPM installed."); }