示例#1
0
 private static MissingDependencyResolver GenerateModDependencyResolver(ModMetadata meta)
 => (mod, main, name, fullName) => {
     string result;
     Stream stream = meta.OpenStream(out result, name + ".dll");
     if (stream == null)
     {
         return(null);
     }
     using (stream) {
         return(ModuleDefinition.ReadModule(stream, mod.GenReaderParameters(false)));
     }
 };
示例#2
0
        /// <summary>
        /// Relink a .dll to point towards the game's assembly 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. If you're running this at runtime, pass at least Relinker.GetChecksum(Metadata)</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(ModMetadata meta, Stream stream,
                                                   MissingDependencyResolver depResolver = null, string[] checksumsExtra = null, Action <MonoModder> prePatch = null)
        {
            string cachedPath         = GetCachedPath(meta);
            string cachedChecksumPath = cachedPath.Substring(0, cachedPath.Length - 4) + ".sum";

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

            checksums[1] = GetChecksum(meta);

            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}");
                try {
                    return(Assembly.LoadFrom(cachedPath));
                } catch (Exception e) {
                    Logger.Log(LogLevel.Warn, "relinker", $"Failed loading {meta}");
                    e.LogDetailed();
                    return(null);
                }
            }

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

            try {
                MonoModder modder = Modder;

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

                string symbolPath;
                modder.ReaderParameters.SymbolStream = meta.OpenStream(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;
                }

                modder.Read();

                modder.ReaderParameters.ReadSymbols = false;

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

                modder.MapDependencies();

                if (RuntimeRuleContainer != null)
                {
                    modder.ParseRules(RuntimeRuleContainer);
                    RuntimeRuleContainer = null;
                }

                prePatch?.Invoke(modder);

                modder.AutoPatch();

                modder.Write();
            } catch (Exception e) {
                Logger.Log(LogLevel.Warn, "relinker", $"Failed relinking {meta}");
                e.LogDetailed();
                return(null);
            } finally {
                Modder.ClearCaches(moduleSpecific: true);
                Modder.Module.Dispose();
                Modder.Module = null;
                Modder.ReaderParameters.SymbolStream?.Dispose();
            }

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

            Logger.Log(LogLevel.Verbose, "relinker", $"Loading assembly for {meta}");
            try {
                return(Assembly.LoadFrom(cachedPath));
            } catch (Exception e) {
                Logger.Log(LogLevel.Warn, "relinker", $"Failed loading {meta}");
                e.LogDetailed();
                return(null);
            }
        }
示例#3
0
        /// <summary>
        /// Load a mod .dll given its metadata at runtime. Doesn't load the mod content.
        /// </summary>
        /// <param name="meta">Metadata of the mod to load.</param>
        public static void LoadMod(ModMetadata meta)
        {
            if (meta == null)
            {
                return;
            }

            // Add an AssemblyResolve handler for all bundled libraries.
            AppDomain.CurrentDomain.AssemblyResolve += GenerateModAssemblyResolver(meta);

            // Load the actual assembly.
            Assembly asm = null;

            if (!string.IsNullOrEmpty(meta.DLL))
            {
                if (meta.Prelinked && File.Exists(meta.DLL))
                {
                    asm = Assembly.LoadFrom(meta.DLL);
                }
                else if (File.Exists(meta.DLL))
                {
                    using (FileStream stream = File.OpenRead(meta.DLL))
                        asm = Relinker.GetRelinkedAssembly(meta, stream);
                }
                else
                {
                    string result;
                    Stream stream = meta.OpenStream(out result, meta.DLL);
                    if (stream != null)
                    {
                        using (stream) {
                            if (meta.Prelinked)
                            {
                                if (stream is MemoryStream)
                                {
                                    asm = Assembly.Load(((MemoryStream)stream).GetBuffer());
                                }
                                else
                                {
                                    using (MemoryStream ms = new MemoryStream()) {
                                        byte[] buffer = new byte[2048];
                                        int    read;
                                        while (0 < (read = stream.Read(buffer, 0, buffer.Length)))
                                        {
                                            ms.Write(buffer, 0, read);
                                        }
                                        asm = Assembly.Load(ms.ToArray());
                                    }
                                }
                            }
                            else
                            {
                                asm = Relinker.GetRelinkedAssembly(meta, stream);
                            }
                        }
                    }
                    else
                    {
                        throw new DllNotFoundException($"Cannot find DLL {meta.DLL} in mod {meta}");
                    }
                }
            }

            if (asm != null)
            {
                LoadModAssembly(meta, asm);
            }
        }