public void CreatePauseMenuButtons(Level level, TextMenu menu, bool minimal) { List <TextMenu.Item> items = menu.GetItems(); int index; // Find the options button and place our button below it. string cleanedOptions = Dialog.Clean("menu_pause_options"); index = items.FindIndex(_ => { TextMenu.Button other = (_ as TextMenu.Button); if (other == null) { return(false); } return(other.Label == cleanedOptions); }); if (index != -1) { index++; } // Otherwise, place it below the last button. else { index = items.Count; } TextMenu.Item itemModOptions = null; menu.Insert(index, itemModOptions = new TextMenu.Button(Dialog.Clean("menu_pause_modoptions")).Pressed(() => { int returnIndex = menu.IndexOf(itemModOptions); menu.RemoveSelf(); level.Paused = true; TextMenu options = OuiModOptions.CreateMenu(true, LevelExt.PauseSnapshot); options.OnESC = options.OnCancel = () => { Audio.Play("event:/ui/main/button_back"); options.CloseAndRun(Everest.SaveSettings(), () => level.Pause(returnIndex, minimal, false)); }; options.OnPause = () => { Audio.Play("event:/ui/main/button_back"); options.CloseAndRun(Everest.SaveSettings(), () => { level.Paused = false; Engine.FreezeTimer = 0.15f; }); }; level.Add(options); })); }
public override IEnumerator Leave(Oui next) { Audio.Play("event:/ui/main/whoosh_large_out"); menu.Focused = false; yield return(Everest.SaveSettings()); for (float p = 0f; p < 1f; p += Engine.DeltaTime * 4f) { menu.X = onScreenX + 1920f * Ease.CubeIn(p); alpha = 1f - Ease.CubeIn(p); yield return(null); } menu.Visible = Visible = false; menu.RemoveSelf(); menu = null; }
public static TextMenu CreateMenu(bool inGame, EventInstance snapshot) { TextMenu menu = new TextMenu(); menu.Add(new TextMenu.Header($"{Dialog.Clean("modoptions_title")} v.{Everest.VersionString}")); Everest.InvokeTyped( "CreateModMenuSection", new Type[] { typeof(TextMenu), typeof(bool), typeof(EventInstance) }, menu, inGame, snapshot ); if (menu.Height > menu.ScrollableMinSize) { menu.Position.Y = menu.ScrollTargetY; } return(menu); }
internal static void LoadAuto() { Directory.CreateDirectory(PathMods = Path.Combine(PathEverest, "Mods")); Directory.CreateDirectory(PathCache = Path.Combine(PathMods, "Cache")); PathBlacklist = Path.Combine(PathMods, "blacklist.txt"); if (File.Exists(PathBlacklist)) { _Blacklist = File.ReadAllLines(PathBlacklist).Select(l => (l.StartsWith("#") ? "" : l).Trim()).ToList(); } else { using (StreamWriter writer = File.CreateText(PathBlacklist)) { writer.WriteLine("# This is the blacklist. Lines starting with # are ignored."); writer.WriteLine("ExampleFolder"); writer.WriteLine("SomeMod.zip"); } } if (!string.IsNullOrEmpty(NameWhitelist)) { PathWhitelist = Path.Combine(PathMods, NameWhitelist); if (File.Exists(PathWhitelist)) { _Whitelist = File.ReadAllLines(PathWhitelist).Select(l => (l.StartsWith("#") ? "" : l).Trim()).ToList(); } } PathModOptionsOrder = Path.Combine(PathMods, "modoptionsorder.txt"); if (File.Exists(PathModOptionsOrder)) { _ModOptionsOrder = File.ReadAllLines(PathModOptionsOrder).Select(l => (l.StartsWith("#") ? "" : l).Trim()).ToList(); } else { using (StreamWriter writer = File.CreateText(PathModOptionsOrder)) { writer.WriteLine("# This is the Mod Options order file. Lines starting with # are ignored."); writer.WriteLine("# Mod folders and archives in this file will be displayed in the same order in the Mod Options menu."); writer.WriteLine("# To define the position of the \"Everest Core\" options, put \"Everest\" on a line."); writer.WriteLine("ExampleFolder"); writer.WriteLine("SomeMod.zip"); } } PathUpdaterBlacklist = Path.Combine(PathMods, "updaterblacklist.txt"); if (File.Exists(PathUpdaterBlacklist)) { _UpdaterBlacklist = File.ReadAllLines(PathUpdaterBlacklist).Select(l => (l.StartsWith("#") ? "" : l).Trim()).ToList(); } else { using (StreamWriter writer = File.CreateText(PathUpdaterBlacklist)) { writer.WriteLine("# This is the Updater Blacklist. Lines starting with # are ignored."); writer.WriteLine("# If you put the name of a mod zip in this file, it won't be auto-updated and it won't show update notifications on the title screen."); writer.WriteLine("SomeMod.zip"); } } Stopwatch watch = Stopwatch.StartNew(); enforceOptionalDependencies = true; string[] files = Directory.GetFiles(PathMods); for (int i = 0; i < files.Length; i++) { string file = Path.GetFileName(files[i]); if (!file.EndsWith(".zip") || _Blacklist.Contains(file)) { continue; } if (_Whitelist != null && !_Whitelist.Contains(file)) { continue; } LoadZip(Path.Combine(PathMods, file)); } files = Directory.GetDirectories(PathMods); for (int i = 0; i < files.Length; i++) { string file = Path.GetFileName(files[i]); if (file == "Cache" || _Blacklist.Contains(file)) { continue; } if (_Whitelist != null && !_Whitelist.Contains(file)) { continue; } LoadDir(Path.Combine(PathMods, file)); } enforceOptionalDependencies = false; Logger.Log(LogLevel.Info, "loader", "Loading mods with unsatisfied optional dependencies (if any)"); Everest.CheckDependenciesOfDelayedMods(); watch.Stop(); Logger.Log(LogLevel.Verbose, "loader", $"ALL MODS LOADED IN {watch.ElapsedMilliseconds}ms"); try { Watcher = new FileSystemWatcher { Path = PathMods, NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastWrite }; Watcher.Created += LoadAutoUpdated; Watcher.EnableRaisingEvents = true; AutoLoadNewMods = true; } catch (Exception e) { Logger.Log(LogLevel.Warn, "loader", $"Failed watching folder: {PathMods}"); e.LogDetailed(); Watcher?.Dispose(); Watcher = null; } }
/// <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); } bool temporaryASM = false; try { MonoModder modder = Modder; modder.Input = stream; modder.OutputPath = cachedPath; modder.MissingDependencyResolver = depResolver; 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 { 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); } }
/// <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. If you're running this at runtime, pass at least Everest.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(EverestModuleMetadata meta, 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); 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(meta).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}"); 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 = 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; } 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 (!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? } // Fix old mods built against HookIL instead of ILContext. _Modder.RelinkMap["MonoMod.RuntimeDetour.HookGen.ILManipulator"] = "MonoMod.Cil.ILContext/Manipulator"; _Modder.RelinkMap["MonoMod.RuntimeDetour.HookGen.HookIL"] = "MonoMod.Cil.ILContext"; _Modder.RelinkMap["MonoMod.RuntimeDetour.HookGen.HookILCursor"] = "MonoMod.Cil.ILCursor"; _Modder.RelinkMap["MonoMod.RuntimeDetour.HookGen.HookILLabel"] = "MonoMod.Cil.ILLabel"; _Modder.RelinkMap["MonoMod.RuntimeDetour.HookGen.HookExtensions"] = "MonoMod.Cil.ILPatternMatchingExt"; _Shim("MonoMod.Utils.ReflectionHelper", typeof(MonoModUpdateShim._ReflectionHelper)); _Shim("MonoMod.Cil.ILCursor", typeof(MonoModUpdateShim._ILCursor)); // If no entry for MonoMod.Utils exists already, add one. modder.MapDependency(_Modder.Module, "MonoMod.Utils"); } 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); } }