コード例 #1
0
 /// <summary>
 /// Creates a system of multiple assembly resolvers. The latest resolver is fired first. The list can be cleared.
 /// </summary>
 /// <param name="resolver"></param>
 /// <param name="handler"></param>
 internal static void RegisterSpecialResolveFailureHandler(this BaseAssemblyResolver resolver,
                                                           AssemblyResolveEventHandler handler)
 {
     if (_resolveDictionary.ContainsKey(resolver))
     {
         _resolveDictionary[resolver].Add(handler);
     }
     else
     {
         _resolveDictionary[resolver] = new List <AssemblyResolveEventHandler> {
             handler
         };
         resolver.ResolveFailure += (sender, name) => {
             foreach (var curHandler in _resolveDictionary[resolver].Reverse())
             {
                 var assembly = curHandler(sender, name);
                 if (assembly != null)
                 {
                     return(assembly);
                 }
             }
             return(null);
         };
     }
 }
コード例 #2
0
        /// <summary>
        /// Loads and returns the topological sorted list of unique assemblies to rewrite.
        /// </summary>
        internal static IEnumerable <AssemblyInfo> LoadAssembliesToRewrite(RewritingOptions options,
                                                                           AssemblyResolveEventHandler handler)
        {
            // Add all explicitly requested assemblies.
            var assemblies = new HashSet <AssemblyInfo>();

            foreach (string path in options.AssemblyPaths)
            {
                if (!assemblies.Any(assembly => assembly.FilePath == path))
                {
                    var name = Path.GetFileName(path);
                    if (options.IsAssemblyIgnored(name))
                    {
                        throw new InvalidOperationException($"Rewriting assembly '{name}' ({path}) that is in the ignore list.");
                    }

                    assemblies.Add(new AssemblyInfo(name, path, options, handler));
                }
            }

            // Find direct dependencies to each assembly and load them, if the corresponding option is enabled.
            foreach (var assembly in assemblies)
            {
                assembly.LoadDependencies(assemblies, handler);
            }

            // Validate that all assemblies are eligible for rewriting.
            foreach (var assembly in assemblies)
            {
                assembly.ValidateAssembly();
            }

            return(SortAssemblies(assemblies));
        }
コード例 #3
0
        /// <summary>
        /// Initializes a new instance of the <see cref="AssemblyInfo"/> class.
        /// </summary>
        private AssemblyInfo(string name, string path, RewritingOptions options, AssemblyResolveEventHandler handler)
        {
            this.Name         = name;
            this.FilePath     = path;
            this.Dependencies = new HashSet <AssemblyInfo>();
            this.Options      = options;
            this.IsRewritten  = false;
            this.IsDisposed   = false;

            // TODO: can we reuse it, or do we need a new one for each assembly?
            var assemblyResolver = new DefaultAssemblyResolver();

            // Add known search directories for resolving assemblies.
            assemblyResolver.AddSearchDirectory(
                Path.GetDirectoryName(typeof(Types.Threading.Tasks.Task).Assembly.Location));
            assemblyResolver.AddSearchDirectory(this.Options.AssembliesDirectory);
            if (this.Options.DependencySearchPaths != null)
            {
                foreach (var dependencySearchPath in this.Options.DependencySearchPaths)
                {
                    assemblyResolver.AddSearchDirectory(dependencySearchPath);
                }
            }

            // Add the assembly resolution error handler.
            assemblyResolver.ResolveFailure += handler;

            this.Resolver = assemblyResolver;
            var readerParameters = new ReaderParameters()
            {
                AssemblyResolver = assemblyResolver,
                ReadSymbols      = this.IsSymbolFileAvailable()
            };

            this.Definition = AssemblyDefinition.ReadAssembly(this.FilePath, readerParameters);
            this.FullName   = this.Definition.FullName;
        }
コード例 #4
0
            /// <summary>
            /// Relink a .dll to point towards Celeste.exe and FNA / XNA properly at runtime, then load it.
            /// </summary>
            /// <param name="meta">The mod metadata, used for caching, among other things.</param>
            /// <param name="stream">The stream to read the .dll from.</param>
            /// <param name="depResolver">An optional dependency resolver.</param>
            /// <param name="checksumsExtra">Any optional checksums</param>
            /// <param name="prePatch">An optional step executed before patching, but after MonoMod has loaded the input assembly.</param>
            /// <returns>The loaded, relinked assembly.</returns>
            public static Assembly GetRelinkedAssembly(EverestModuleMetadata meta, string asmname, Stream stream,
                                                       MissingDependencyResolver depResolver = null, string[] checksumsExtra = null, Action <MonoModder> prePatch = null)
            {
                if (!Flags.SupportRelinkingMods)
                {
                    Logger.Log(LogLevel.Warn, "relinker", "Relinker disabled!");
                    return(null);
                }

                string cachedPath         = GetCachedPath(meta, asmname);
                string cachedChecksumPath = cachedPath.Substring(0, cachedPath.Length - 4) + ".sum";

                string[] checksums = new string[2 + (checksumsExtra?.Length ?? 0)];
                if (GameChecksum == null)
                {
                    GameChecksum = Everest.GetChecksum(Assembly.GetAssembly(typeof(Relinker)).Location).ToHexadecimalString();
                }
                checksums[0] = GameChecksum;

                checksums[1] = Everest.GetChecksum(ref stream).ToHexadecimalString();

                if (checksumsExtra != null)
                {
                    for (int i = 0; i < checksumsExtra.Length; i++)
                    {
                        checksums[i + 2] = checksumsExtra[i];
                    }
                }

                if (File.Exists(cachedPath) && File.Exists(cachedChecksumPath) &&
                    ChecksumsEqual(checksums, File.ReadAllLines(cachedChecksumPath)))
                {
                    Logger.Log(LogLevel.Verbose, "relinker", $"Loading cached assembly for {meta} - {asmname}");
                    try {
                        Assembly asm = Assembly.LoadFrom(cachedPath);
                        _RelinkedAssemblies.Add(asm);
                        return(asm);
                    } catch (Exception e) {
                        Logger.Log(LogLevel.Warn, "relinker", $"Failed loading {meta} - {asmname}");
                        e.LogDetailed();
                        return(null);
                    }
                }

                if (depResolver == null)
                {
                    depResolver = GenerateModDependencyResolver(meta);
                }

                AssemblyResolveEventHandler resolver = (s, r) => {
                    ModuleDefinition dep = depResolver(Modder, Modder.Module, r.Name, r.FullName);
                    if (dep != null)
                    {
                        return(dep.Assembly);
                    }

                    return(OnRelinkerResolveFailure(s, r));
                };

                bool temporaryASM = false;

                try {
                    MonoModder modder = Modder;

                    modder.Input      = stream;
                    modder.OutputPath = cachedPath;
                    modder.MissingDependencyResolver = depResolver;

                    ((DefaultAssemblyResolver)modder.AssemblyResolver).ResolveFailure += resolver;

                    string symbolPath;
                    modder.ReaderParameters.SymbolStream = OpenStream(meta, out symbolPath, meta.DLL.Substring(0, meta.DLL.Length - 4) + ".pdb", meta.DLL + ".mdb");
                    modder.ReaderParameters.ReadSymbols  = modder.ReaderParameters.SymbolStream != null;
                    if (modder.ReaderParameters.SymbolReaderProvider != null &&
                        modder.ReaderParameters.SymbolReaderProvider is RelinkerSymbolReaderProvider)
                    {
                        ((RelinkerSymbolReaderProvider)modder.ReaderParameters.SymbolReaderProvider).Format =
                            string.IsNullOrEmpty(symbolPath) ? DebugSymbolFormat.Auto :
                            symbolPath.EndsWith(".mdb") ? DebugSymbolFormat.MDB :
                            symbolPath.EndsWith(".pdb") ? DebugSymbolFormat.PDB :
                            DebugSymbolFormat.Auto;
                    }

                    try {
                        modder.ReaderParameters.ReadSymbols = true;
                        modder.Read();
                    } catch {
                        modder.ReaderParameters.SymbolStream?.Dispose();
                        modder.ReaderParameters.SymbolStream = null;
                        modder.ReaderParameters.ReadSymbols  = false;
                        stream.Seek(0, SeekOrigin.Begin);
                        modder.Read();
                    }

                    if (modder.ReaderParameters.SymbolReaderProvider != null &&
                        modder.ReaderParameters.SymbolReaderProvider is RelinkerSymbolReaderProvider)
                    {
                        ((RelinkerSymbolReaderProvider)modder.ReaderParameters.SymbolReaderProvider).Format = DebugSymbolFormat.Auto;
                    }

                    modder.MapDependencies();

                    if (!RuntimeRulesParsed)
                    {
                        RuntimeRulesParsed = true;

                        InitMMSharedData();

                        string rulesPath = Path.Combine(
                            Path.GetDirectoryName(typeof(Celeste).Assembly.Location),
                            Path.GetFileNameWithoutExtension(typeof(Celeste).Assembly.Location) + ".Mod.mm.dll"
                            );
                        if (!File.Exists(rulesPath))
                        {
                            // Fallback if someone renamed Celeste.exe
                            rulesPath = Path.Combine(
                                Path.GetDirectoryName(typeof(Celeste).Assembly.Location),
                                "Celeste.Mod.mm.dll"
                                );
                        }
                        if (File.Exists(rulesPath))
                        {
                            ModuleDefinition rules = ModuleDefinition.ReadModule(rulesPath, new ReaderParameters(ReadingMode.Immediate));
                            modder.ParseRules(rules);
                            rules.Dispose(); // Is this safe?
                        }
                    }

                    prePatch?.Invoke(modder);

                    modder.ParseRules(modder.Module);

                    modder.AutoPatch();

RetryWrite:
                    try {
                        modder.WriterParameters.WriteSymbols = true;
                        modder.Write();
                    } catch {
                        try {
                            modder.WriterParameters.WriteSymbols = false;
                            modder.Write();
                        } catch when(!temporaryASM)
                        {
                            temporaryASM = true;
                            long stamp = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;

                            cachedPath          = Path.Combine(Path.GetTempPath(), $"Everest.Relinked.{Path.GetFileNameWithoutExtension(cachedPath)}.{stamp}.dll");
                            modder.Module.Name += "." + stamp;
                            modder.Module.Assembly.Name.Name += "." + stamp;
                            modder.OutputPath = cachedPath;
                            modder.WriterParameters.WriteSymbols = true;
                            goto RetryWrite;
                        }
                    }
                } catch (Exception e) {
                    Logger.Log(LogLevel.Warn, "relinker", $"Failed relinking {meta} - {asmname}");
                    e.LogDetailed();
                    return(null);
                } finally {
                    ((DefaultAssemblyResolver)Modder.AssemblyResolver).ResolveFailure -= resolver;
                    Modder.ReaderParameters.SymbolStream?.Dispose();
                    if (SharedModder)
                    {
                        Modder.ClearCaches(moduleSpecific: true);
                        Modder.Module?.Dispose();
                        Modder.Module = null;
                    }
                    else
                    {
                        Modder.Dispose();
                        Modder = null;
                    }
                }

                if (File.Exists(cachedChecksumPath))
                {
                    File.Delete(cachedChecksumPath);
                }
                if (!temporaryASM)
                {
                    File.WriteAllLines(cachedChecksumPath, checksums);
                }

                Logger.Log(LogLevel.Verbose, "relinker", $"Loading assembly for {meta} - {asmname}");
                try {
                    Assembly asm = Assembly.LoadFrom(cachedPath);
                    _RelinkedAssemblies.Add(asm);
                    return(asm);
                } catch (Exception e) {
                    Logger.Log(LogLevel.Warn, "relinker", $"Failed loading {meta} - {asmname}");
                    e.LogDetailed();
                    return(null);
                }
            }
コード例 #5
0
ファイル: Hacks.cs プロジェクト: GregRos/Patchwork
        /// <summary>
        /// Creates a system of multiple assembly resolvers. The latest resolver is fired first. The list can be cleared.
        /// </summary>
        /// <param name="resolver"></param>
        /// <param name="handler"></param>
        internal static void RegisterSpecialResolveFailureHandler(this BaseAssemblyResolver resolver,
			AssemblyResolveEventHandler handler)
        {
            if (_resolveDictionary.ContainsKey(resolver)) {
                _resolveDictionary[resolver].Add(handler);
            } else {
                _resolveDictionary[resolver] = new List<AssemblyResolveEventHandler> {
                    handler
                };
                resolver.ResolveFailure += (sender, name) => {
                    foreach (var curHandler in _resolveDictionary[resolver].Reverse()) {
                        var assembly = curHandler(sender, name);
                        if (assembly != null) {
                            return assembly;
                        }
                    }
                    return null;
                };
            }
        }