/********* ** Public methods *********/ /// <summary>Construct an instance.</summary> /// <param name="targetPlatform">The current game platform.</param> /// <param name="monitor">Encapsulates monitoring and logging.</param> /// <param name="paranoidMode">Whether to detect paranoid mode issues.</param> public AssemblyLoader(Platform targetPlatform, IMonitor monitor, bool paranoidMode) { this.Monitor = monitor; this.ParanoidMode = paranoidMode; this.AssemblyMap = this.TrackForDisposal(Constants.GetAssemblyMap(targetPlatform)); this.AssemblyDefinitionResolver = this.TrackForDisposal(new AssemblyDefinitionResolver()); this.AssemblyDefinitionResolver.AddSearchDirectory(Constants.ExecutionPath); this.AssemblyDefinitionResolver.AddSearchDirectory(Constants.InternalFilesPath); // generate type => assembly lookup for types which should be rewritten this.TypeAssemblies = new Dictionary <string, Assembly>(); foreach (Assembly assembly in this.AssemblyMap.Targets) { ModuleDefinition module = this.AssemblyMap.TargetModules[assembly]; foreach (TypeDefinition type in module.GetTypes()) { if (!type.IsPublic) { continue; // no need to rewrite } if (type.Namespace.Contains("<")) { continue; // ignore assembly metadata } this.TypeAssemblies[type.FullName] = assembly; } } }
/********* ** Public methods *********/ /// <summary>Construct an instance.</summary> /// <param name="targetPlatform">The current game platform.</param> /// <param name="monitor">Encapsulates monitoring and logging.</param> /// <param name="isDeveloperMode">Whether to enable developer mode logging.</param> public AssemblyLoader(Platform targetPlatform, IMonitor monitor, bool isDeveloperMode) { this.Monitor = monitor; this.IsDeveloperMode = isDeveloperMode; this.AssemblyMap = Constants.GetAssemblyMap(targetPlatform); this.AssemblyDefinitionResolver = new AssemblyDefinitionResolver(); // generate type => assembly lookup for types which should be rewritten this.TypeAssemblies = new Dictionary <string, Assembly>(); foreach (Assembly assembly in this.AssemblyMap.Targets) { ModuleDefinition module = this.AssemblyMap.TargetModules[assembly]; foreach (TypeDefinition type in module.GetTypes()) { if (!type.IsPublic) { continue; // no need to rewrite } if (type.Namespace.Contains("<")) { continue; // ignore assembly metadata } this.TypeAssemblies[type.FullName] = assembly; } } }
/// <summary>Preprocess and load an assembly.</summary> /// <param name="assemblyPath">The assembly file path.</param> /// <param name="assumeCompatible">Assume the mod is compatible, even if incompatible code is detected.</param> /// <returns>Returns the rewrite metadata for the preprocessed assembly.</returns> /// <exception cref="IncompatibleInstructionException">An incompatible CIL instruction was found while rewriting the assembly.</exception> public Assembly Load(string assemblyPath, bool assumeCompatible) { // get referenced local assemblies AssemblyParseResult[] assemblies; { AssemblyDefinitionResolver resolver = new AssemblyDefinitionResolver(); HashSet <string> visitedAssemblyNames = new HashSet <string>(AppDomain.CurrentDomain.GetAssemblies().Select(p => p.GetName().Name)); // don't try loading assemblies that are already loaded assemblies = this.GetReferencedLocalAssemblies(new FileInfo(assemblyPath), visitedAssemblyNames, resolver).ToArray(); if (!assemblies.Any()) { throw new InvalidOperationException($"Could not load '{assemblyPath}' because it doesn't exist."); } resolver.Add(assemblies.Select(p => p.Definition).ToArray()); } // rewrite & load assemblies in leaf-to-root order bool oneAssembly = assemblies.Length == 1; Assembly lastAssembly = null; foreach (AssemblyParseResult assembly in assemblies) { bool changed = this.RewriteAssembly(assembly.Definition, assumeCompatible, logPrefix: " "); if (changed) { if (!oneAssembly) { this.Monitor.Log($" Loading {assembly.File.Name}.dll (rewritten in memory)...", LogLevel.Trace); } using (MemoryStream outStream = new MemoryStream()) { assembly.Definition.Write(outStream); byte[] bytes = outStream.ToArray(); lastAssembly = Assembly.Load(bytes); } } else { if (!oneAssembly) { this.Monitor.Log($" Loading {assembly.File.Name}.dll...", LogLevel.Trace); } lastAssembly = Assembly.UnsafeLoadFrom(assembly.File.FullName); } } // last assembly loaded is the root return(lastAssembly); }
/// <summary>Preprocess and load an assembly.</summary> /// <param name="mod">The mod for which the assembly is being loaded.</param> /// <param name="assemblyPath">The assembly file path.</param> /// <param name="assumeCompatible">Assume the mod is compatible, even if incompatible code is detected.</param> /// <returns>Returns the rewrite metadata for the preprocessed assembly.</returns> /// <exception cref="IncompatibleInstructionException">An incompatible CIL instruction was found while rewriting the assembly.</exception> public Assembly Load(IModMetadata mod, string assemblyPath, bool assumeCompatible) { // get referenced local assemblies AssemblyParseResult[] assemblies; { AssemblyDefinitionResolver resolver = new AssemblyDefinitionResolver(); HashSet <string> visitedAssemblyNames = new HashSet <string>(AppDomain.CurrentDomain.GetAssemblies().Select(p => p.GetName().Name)); // don't try loading assemblies that are already loaded assemblies = this.GetReferencedLocalAssemblies(new FileInfo(assemblyPath), visitedAssemblyNames, resolver).ToArray(); } // validate load if (!assemblies.Any() || assemblies[0].Status == AssemblyLoadStatus.Failed) { throw new SAssemblyLoadFailedException(!File.Exists(assemblyPath) ? $"Could not load '{assemblyPath}' because it doesn't exist." : $"Could not load '{assemblyPath}'." ); } if (assemblies.Last().Status == AssemblyLoadStatus.AlreadyLoaded) // mod assembly is last in dependency order { throw new SAssemblyLoadFailedException($"Could not load '{assemblyPath}' because it was already loaded. Do you have two copies of this mod?"); } // rewrite & load assemblies in leaf-to-root order bool oneAssembly = assemblies.Length == 1; Assembly lastAssembly = null; HashSet <string> loggedMessages = new HashSet <string>(); foreach (AssemblyParseResult assembly in assemblies) { if (assembly.Status == AssemblyLoadStatus.AlreadyLoaded) { continue; } bool changed = this.RewriteAssembly(mod, assembly.Definition, assumeCompatible, loggedMessages, logPrefix: " "); if (changed) { if (!oneAssembly) { this.Monitor.Log($" Loading {assembly.File.Name} (rewritten in memory)...", LogLevel.Trace); } using (MemoryStream outStream = new MemoryStream()) { assembly.Definition.Write(outStream); byte[] bytes = outStream.ToArray(); lastAssembly = Assembly.Load(bytes); } } else { if (!oneAssembly) { this.Monitor.Log($" Loading {assembly.File.Name}...", LogLevel.Trace); } lastAssembly = Assembly.UnsafeLoadFrom(assembly.File.FullName); } } // last assembly loaded is the root return(lastAssembly); }