public static void RestartAndClearSymbols(this InstallerWindow ins) { if (ins.MainMod == null) { return; } if (ETGFinder.Version != ETGFinder.VersionLastRun && ETGFinder.VersionLastRun != null) { ins.ClearBackup(); } Process reboot = new Process(); reboot.StartInfo.FileName = Assembly.GetEntryAssembly().Location; reboot.StartInfo.Arguments = "--clearsymbols \"" + ins.MainModDir + "\""; reboot.StartInfo.CreateNoWindow = true; reboot.StartInfo.UseShellExecute = true; if (ETGFinder.Platform.HasFlag(ETGPlatform.Unix)) { reboot.StartInfo.Arguments = "\"" + reboot.StartInfo.FileName + "\" " + reboot.StartInfo.Arguments; reboot.StartInfo.FileName = "mono"; } reboot.Start(); Environment.Exit(1); }
public static bool BackupETG(this InstallerWindow ins) { string pathGame = ins.MainModDir; string pathBackup = Path.Combine(pathGame, "ModBackup"); if (!Directory.Exists(pathBackup)) { Directory.CreateDirectory(pathBackup); } if (!File.Exists(ExePath)) { return(false); } ins.Log("Backing up: ").LogLine(ETGFinder.MainName); ExeBackupPath = Path.Combine(pathBackup, ETGFinder.MainName); if (File.Exists(ExeBackupPath)) { File.Delete(ExeBackupPath); } File.Copy(ExePath, ExeBackupPath); if (ETGFinder.Platform.HasFlag(ETGPlatform.Windows)) { ins.Log("Backing up: ").LogLine("UnityPlayer.dll"); WindowsUnityPlayerBackupPath = Path.Combine(pathBackup, "UnityPlayer.dll"); if (File.Exists(WindowsUnityPlayerBackupPath)) { File.Delete(WindowsUnityPlayerBackupPath); } File.Move(WindowsUnityPlayerPath, WindowsUnityPlayerBackupPath); } return(true); }
public static void PatchExe(this InstallerWindow ins) { using (FileStream fo = File.OpenWrite(ExePath)) { using (FileStream fi = File.OpenRead(ExeBackupPath)) { using (BinaryWriter bo = new BinaryWriter(fo)) { using (BinaryReader bi = new BinaryReader(fi)) { ins.PatchExe(bi, bo); } } } } if (ETGFinder.Platform.HasFlag(ETGPlatform.Windows)) { // Windows doesn't have an executable bit } else if (ETGFinder.Platform.HasFlag(ETGPlatform.Unix)) { Process chmod = new Process(); chmod.StartInfo.FileName = "chmod"; chmod.StartInfo.Arguments = "a+x \"" + ExePath + "\""; chmod.StartInfo.CreateNoWindow = true; chmod.StartInfo.UseShellExecute = false; chmod.Start(); chmod.WaitForExit(); } }
public static void ExeLoaded(this InstallerWindow ins, string path) { ins.ExePathBox.Text = path; Task.Run(delegate() { ins.ExeSelected(path, " [saved]"); }); }
public static void ClearCache(this InstallerWindow ins) { if (ins.MainMod == null) { return; } string pathGame = ins.MainModDir; string pathCache = Path.Combine(pathGame, "ModCache"); if (!Directory.Exists(pathCache)) { return; } ins.LogLine("Clearing mod cache..."); string[] files = Directory.GetFiles(pathCache); ins.InitProgress("Clearing mod cache", files.Length + 1); for (int i = 0; i < files.Length; i++) { string file = Path.GetFileName(files[i]); ins.Log("Removing: ").LogLine(file); ins.SetProgress("Removing: " + file, i); File.Delete(files[i]); } ins.EndProgress("Clearing cache complete."); }
public static void ClearSymbols(this InstallerWindow ins, string path = null) { if (path == null) { if (ins.MainMod == null) { return; } path = ins.MainModDir; ins.LogLine("Clearing symbols..."); } string[] files = Directory.GetFiles(path); ins.InitProgress("Clearing symbols", files.Length + 1); for (int i = 0; i < files.Length; i++) { string file = Path.GetFileName(files[i]); if (!file.EndsWith(".pdb") && !file.EndsWith(".mdb")) { continue; } ins.Log("Removing: ").LogLine(file); ins.SetProgress("Removing: " + file, i); File.Delete(files[i]); } ins.EndProgress("Clearing symbols complete."); }
public static bool PrepareDeobfuscator(this InstallerWindow ins) { if (PrepareHook == null) { return(true); } return(PrepareHook.Invoke(ins)); }
public static bool Deobfuscate(this InstallerWindow ins, string file) { if (DeobfuscateHook == null) { return(true); } return(DeobfuscateHook.Invoke(ins, file)); }
public static bool UnzipMod(this InstallerWindow ins, byte[] data) { if (data == null) { return(false); } using (MemoryStream ms = new MemoryStream(data, 0, data.Length, false, true)) { return(ins.UnzipMod(ms)); } }
public static string GetAdvanced(this InstallerWindow ins, char split) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < ins.AdvancedPathBoxes.Count; i++) { sb.Append(ins.AdvancedPathBoxes[i].Text); if (i < ins.AdvancedPathBoxes.Count - 1) { sb.Append(split); } } return(sb.ToString()); }
public static void SetAdvanced(this InstallerWindow ins, string[] paths) { while (0 < ins.AdvancedPathBoxes.Count) { ins.AdvancedPanel.Controls.Remove(ins.AdvancedPathBoxes[0]); ins.AdvancedPathBoxes.RemoveAt(0); } while (0 < ins.AdvancedRemoveButtons.Count) { ins.AdvancedPanel.Controls.Remove(ins.AdvancedRemoveButtons[0]); ins.AdvancedRemoveButtons.RemoveAt(0); } ins.AddManualPathRows(paths); }
public static void Install(this InstallerWindow ins) { ins.ChangeRobot(ins.RobotImageInstalling); #if DEBUG ins.Install_(); #else try { ins.Install_(); } catch (Exception e) { ins.ChangeRobot(ins.RobotImageError); ins.Invoke(() => ins.ViewLogButton.Text = "ERROR! Click here to get the log!"); ins.LogLine(e.ToString()); return; } #endif ins.ChangeRobot(ins.RobotImageFinished); }
public static byte[] DownloadCached(this InstallerWindow ins, string url, string cached) { byte[] data = ins.ReadDataFromCache(cached); if (data != null) { return(data); } data = ins.Download(url); if (data == null) { return(null); } ins.WriteDataToCache(cached, data); return(data); }
public static void WriteDataToCache(this InstallerWindow ins, string cached, byte[] data) { string pathGame = ins.MainModDir; string pathCache = Path.Combine(pathGame, "ModCache"); if (!Directory.Exists(pathCache)) { Directory.CreateDirectory(pathCache); } string cachedPath = Path.Combine(pathCache, cached); if (File.Exists(cachedPath)) { File.Delete(cachedPath); } ins.Log("Writing to cache: ").LogLine(cached); File.WriteAllBytes(cachedPath, data); }
public static byte[] ReadDataFromCache(this InstallerWindow ins, string cached) { string pathGame = ins.MainModDir; string pathCache = Path.Combine(pathGame, "ModCache"); if (!Directory.Exists(pathCache)) { Directory.CreateDirectory(pathCache); } string cachedPath = Path.Combine(pathCache, cached); if (!File.Exists(cachedPath)) { return(null); } ins.Log("Reading from cache: ").LogLine(cached); return(File.ReadAllBytes(cachedPath)); }
public static bool Backup(this InstallerWindow ins, string file) { string pathGame = ins.MainModDir; string pathBackup = Path.Combine(pathGame, "ModBackup"); if (!Directory.Exists(pathBackup)) { Directory.CreateDirectory(pathBackup); } string origPath = Path.Combine(pathGame, file); if (!File.Exists(origPath)) { return(false); } ins.Log("Backing up: ").LogLine(file); File.Copy(origPath, Path.Combine(pathBackup, file), true); return(true); }
public static bool BackupETG(this InstallerWindow ins) { string pathGame = ins.MainModDir; string pathBackup = Path.Combine(pathGame, "ModBackup"); if (!Directory.Exists(pathBackup)) { Directory.CreateDirectory(pathBackup); } if (!File.Exists(ExePath)) { return(false); } ins.Log("Backing up: ").LogLine(ETGFinder.MainName); ExeBackupPath = Path.Combine(pathBackup, ETGFinder.MainName); if (File.Exists(ExeBackupPath)) { File.Delete(ExeBackupPath); } File.Move(ExePath, ExeBackupPath); return(true); }
public static bool UnzipMod(this InstallerWindow ins, Stream zs) { string platform = ""; if (ETGFinder.Platform.HasFlag(ETGPlatform.Windows)) { platform = "win32"; } else if (ETGFinder.Platform.HasFlag(ETGPlatform.MacOS)) { platform = "osx"; } else if (ETGFinder.Platform.HasFlag(ETGPlatform.Linux)) { platform = ETGFinder.Platform.HasFlag(ETGPlatform.X64) ? "lib64" : "lib"; } string fallback = "ETGMOD"; string prefix = fallback; if (ETGFinder.Version.Contains("b")) { prefix += "-BETA"; } fallback += "/"; prefix += "/"; string pathGame = ins.MainModDir; ins.Log("Checking for ").Log(prefix).LogLine("..."); using (ZipArchive zip = new ZipArchive(zs, ZipArchiveMode.Read)) { int prefixCount = 0; int fallbackCount = 0; int noneCount = 0; ins.InitProgress("Scanning ZIP", zip.Entries.Count); for (int i = 0; i < zip.Entries.Count; i++) { ins.SetProgress(i); ZipArchiveEntry entry = zip.Entries[i]; ins.Log("Entry: ").Log(entry.FullName).Log(": ").Log(entry.Length.ToString()).LogLine(" bytes"); if (entry.FullName == "InstallerVersion.txt") { ins.LogLine("Found version file."); using (Stream s = entry.Open()) { using (StreamReader sr = new StreamReader(s)) { Version minv = new Version(sr.ReadLine().Trim()); if (InstallerWindow.Version < minv) { ins.LogLine("There's a new ETGMod Installer version!"); ins.LogLine("Visit https://modthegungeon.github.io/#download to download it."); ins.Log("(Minimum installer version for this ETGMod version: ").LogLine(minv.ToString()).Log(")"); ins.Invoke(() => ins.Progress.BrushProgress = new System.Drawing.SolidBrush(System.Drawing.Color.FromArgb(255, 63, 63, 91)) ); ins.InitProgress("Installer update required!", 1).SetProgress(1); return(false); } } } continue; } string entryName = entry.FullName; if (entry.FullName.StartsWith(prefix)) { prefixCount++; } else if (entry.FullName.StartsWith(fallback)) { fallbackCount++; } else { noneCount++; } } if (0 < prefixCount) { ins.Log(prefix).LogLine(" found."); ins.InitProgress("Extracting ZIP", prefixCount); } else if (0 < fallbackCount) { ins.Log("Didn't find ").Log(prefix).Log(", falling back to ").Log(fallback).LogLine("."); prefix = fallback; ins.InitProgress("Extracting ZIP", fallbackCount); } else { ins.LogLine("Is this even a ETGMod ZIP? uh..."); prefix = ""; ins.InitProgress("Extracting ZIP", noneCount); } int extracted = 0; for (int i = 0; i < zip.Entries.Count; i++) { ZipArchiveEntry entry = zip.Entries[i]; if (!entry.FullName.StartsWith(prefix) || entry.FullName == prefix) { continue; } ins.SetProgress(++extracted); string entryName = entry.FullName.Substring(prefix.Length); if (entryName.StartsWith("LIBS/")) { entryName = entryName.Substring(5); if (!entryName.StartsWith(platform + "/")) { continue; } entryName = entryName.Substring(platform.Length + 1); } entryName = entryName.Replace('/', Path.DirectorySeparatorChar); string path = Path.Combine(pathGame, entryName); ins.Log("Extracting: ").Log(entry.FullName).Log(" -> ").LogLine(path); if (entry.Length == 0 && entry.CompressedLength == 0) { Directory.CreateDirectory(path); } else { entry.ExtractToFile(path, true); } } ins.EndProgress("Extracted ZIP."); } return(true); }
public InstallerWindow() { Instance = this; HandleCreated += onHandleCreated; OnRobotFrameChangedHandler = new EventHandler(OnRobotFrameChanged); OpenExeDialog = new OpenFileDialog() { Title = "Select " + ETGFinder.MainName, AutoUpgradeEnabled = true, CheckFileExists = true, CheckPathExists = true, ValidateNames = true, Multiselect = false, ShowReadOnly = false, Filter = ETGFinder.MainName + "|" + ETGFinder.MainName, FilterIndex = 0 }; OpenExeDialog.FileOk += (object senderFileOk, CancelEventArgs eFileOk) => Task.Run(() => this.ExeSelected(OpenExeDialog.FileNames[0])); OpenModDialog = new OpenFileDialog() { Title = "Select ETGMod Backend", AutoUpgradeEnabled = true, CheckFileExists = true, CheckPathExists = true, ValidateNames = true, Multiselect = true, ShowReadOnly = false, Filter = "ETGMod DLL|*.mm.dll|ETGMod ZIP|*.zip|All files|*.*", FilterIndex = 0 }; OpenModDialog.FileOk += (object senderFileOk, CancelEventArgs eFileOk) => AddManualPathRows(OpenModDialog.FileNames); Text = "Mod the Gungeon Installer"; if (ETGFinder.Platform.HasFlag(ETGPlatform.Unix)) { FormBorderStyle = FormBorderStyle.Sizable; } else { FormBorderStyle = FormBorderStyle.FixedDialog; } ResizeRedraw = false; MaximizeBox = false; MinimizeBox = true; StartPosition = FormStartPosition.CenterScreen; //Setting the font doesn't change anything... /*PrivateFontCollection pfc = LoadAsset<PrivateFontCollection>("fonts.uni05_53"); * for (int i = 0; i < pfc.Families.Length; i++) { * Console.WriteLine("Font " + i + ": " + pfc.Families[i]); * } * GlobalFont = new Font(pfc.Families[0], 8f);*/ if (GlobalFont == null) { if (ETGFinder.Platform.HasFlag(ETGPlatform.Windows)) { GlobalFont = new Font("Segoe UI", 8f, FontStyle.Regular); } else if (ETGFinder.Platform.HasFlag(ETGPlatform.MacOS)) { GlobalFont = new Font("Lucida Grande", 8f, FontStyle.Regular); // With Helvetica Neue: Erter Tne Girgeor } else { GlobalFont = new Font("DejaVu Sans", 8f, FontStyle.Regular); } } AllowDrop = true; DragDrop += onDragDrop; DragEnter += delegate(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop) && VersionTabs.Enabled) { e.Effect = DragDropEffects.Copy; VersionTabs.SelectedIndex = 2; } }; BackgroundImage = LoadAsset <Image>("background"); BackgroundImageLayout = ImageLayout.Center; RobotImageIdle = LoadAsset <Image>("icons.idle"); RobotImageInstalling = LoadAsset <Image>("icons.installing.gif"); RobotImageError = LoadAsset <Image>("icons.error.gif"); RobotImageFinished = LoadAsset <Image>("icons.finished.gif"); Controls.Add(RobotPictureBox = new PictureBox() { BackColor = Color.Transparent, Image = RobotImageIdle, Bounds = new Rectangle(150, 100, RobotImageIdle.Width, RobotImageIdle.Height) }); //ShuffleIconColors(); OrigIcon = LoadAsset <Bitmap>("icons.icon"); Icon = Icon.FromHandle(OrigIcon.GetHicon()); ResetSize(); SizeChanged += ResetSize; #if !DEBUG Version = new Version(Version.Major, Version.Minor, Version.Build); #endif Controls.Add(new Label() { Bounds = new Rectangle(448, 338, 308, 16), Font = GlobalFont, TextAlign = ContentAlignment.BottomRight, #if DEBUG Text = "DEBUG " + Version.Revision, #else Text = "v" + Version, #endif BackColor = Color.Transparent, ForeColor = Color.FromArgb(127, 0, 0, 0) }); Controls.Add(LogBox = new RichTextBox() { Bounds = new Rectangle(0, 0, 443, 315), Font = GlobalFont, ReadOnly = true, Multiline = true, //ScrollBars = System.Windows.Forms.ScrollBars.Vertical, DetectUrls = true, ShortcutsEnabled = true, BackColor = Color.Black, ForeColor = Color.White, BorderStyle = BorderStyle.None, WordWrap = true, Text = "ETGMod Installer v" + Version + "\n", Visible = false, }); Controls.Add(ViewLogButton = new Button() { Text = "Show log", Font = GlobalFont, Bounds = new Rectangle(0, 316, 442, 40), Visible = true }); ViewLogButton.Click += delegate(object sender, EventArgs e) { ToggleLog(); }; Controls.Add(Progress = new CustomProgress() { Bounds = new Rectangle(448, 313, 312, 24), Font = GlobalFont, Text = "Idle." }); Add(new Label() { Font = GlobalFont, TextAlign = ContentAlignment.MiddleCenter, Text = !ETGFinder.Platform.HasFlag(ETGPlatform.MacOS) ? ("Step 1: Select " + ETGFinder.MainName) : ("Step 1: Drag-n-drop EtG_OSX.app here"), BackColor = Color.Transparent, ForeColor = Color.Black }); Add(ExePathBox = new TextBox() { Font = GlobalFont, ReadOnly = true }); ExePathBox.Width -= 32; Controls.Add(ExePathButton = new Button() { Bounds = new Rectangle(ExePathBox.Bounds.X + ExePathBox.Bounds.Width, ExePathBox.Bounds.Y, 32, ExePathBox.Bounds.Height), Font = GlobalFont, Image = LoadAsset <Image>("icons.open"), ImageAlign = ContentAlignment.MiddleCenter }); ExePathButton.Click += (object senderClick, EventArgs eClick) => OpenExeDialog.ShowDialog(this); AddOffset += 2; Add(ExeStatusLabel = new Label() { Font = GlobalFont, TextAlign = ContentAlignment.MiddleCenter, Text = "No " + ETGFinder.MainName + " selected", BackColor = Color.FromArgb(127, 255, 63, 63), ForeColor = Color.Black }); AddOffset += 2; Add(new Label() { Font = GlobalFont, TextAlign = ContentAlignment.MiddleCenter, Text = "Step 2: Choose your backends", BackColor = Color.Transparent, ForeColor = Color.Black }); Controls.Add(InstallButton = new Button() { Bounds = new Rectangle(448, 313 - 1 - ExePathButton.Size.Height, 312 - 32, ExePathButton.Size.Height), Font = GlobalFont, TextAlign = ContentAlignment.MiddleCenter, Text = "Step 3: Install ETGMod", Enabled = false }); InstallButton.Click += (object senderClick, EventArgs eClick) => { if (ShowLogOnInstall) { ShowLog(); } Task.Run((Action)this.Install); }; Controls.Add(UninstallButton = new Button() { Bounds = new Rectangle(InstallButton.Bounds.X + InstallButton.Bounds.Width, InstallButton.Bounds.Y, 32, InstallButton.Bounds.Height), Font = GlobalFont, Image = LoadAsset <Image>("icons.uninstall"), ImageAlign = ContentAlignment.MiddleCenter }); UninstallButton.Click += (object senderClick, EventArgs eClick) => Task.Run(delegate() { if (ShowLogOnInstall) { ShowLog(); } this.Uninstall(); this.ClearCache(); this.ExeSelected(ExePathBox.Text, " [just uninstalled]"); this.SetMainEnabled(true); }); Controls.Add(VersionTabs = new TabControl() { Bounds = new Rectangle(448, 4 + 26 * AddIndex + AddOffset, 312, InstallButton.Location.Y - (4 + 26 * AddIndex + AddOffset)), Font = GlobalFont, BackColor = Color.Transparent }); if (ETGFinder.Platform.HasFlag(ETGPlatform.MacOS)) { VersionTabs.BackColor = Color.White; // Mono's WinForms implementation on macOS just sucks. VersionTabs.SelectedIndexChanged += delegate(object sender, EventArgs e) { for (int i = 0; i < VersionTabs.TabPages.Count; i++) { if (i == VersionTabs.SelectedIndex) { VersionTabs.TabPages[i].ShowDeep(); continue; } VersionTabs.TabPages[i].HideDeep(); } RefreshManualPanel(); }; } VersionTabs.TabPages.Add(new TabPage("Backends")); VersionTabs.TabPages[0].Controls.Add(APIModsList = new ListBox() { Font = GlobalFont, Dock = DockStyle.Fill, MultiColumn = true, SelectionMode = SelectionMode.MultiExtended }); APIModsList.SelectedValueChanged += delegate(object sender, EventArgs e) { if (!RepoHelper.IsOffline && !APIModsList.SelectedIndices.Contains(0)) { APIModsList.SelectedIndices.Add(0); } }; APIModsList.MultiColumn = false; APIModsList.DrawMode = DrawMode.OwnerDrawFixed; APIModsList.DrawItem += APIModsListDrawItem; VersionTabs.TabPages.Add(new TabPage("Advanced")); VersionTabs.TabPages[1].Controls.Add(AdvancedPanel = new Panel() { Font = GlobalFont, Dock = DockStyle.Fill, AutoScroll = true }); AdvancedPanel.Controls.Add(AdvancedInfoLabel = new Label() { Text = "Note: This isn't where you install mods!", ImageAlign = ContentAlignment.MiddleCenter, TextAlign = ContentAlignment.MiddleCenter, }); AdvancedPanel.Controls.Add(AdvancedAddButton = new Button() { Font = GlobalFont, Image = LoadAsset <Image>("icons.open"), ImageAlign = ContentAlignment.MiddleCenter }); AdvancedAddButton.Click += (object senderClick, EventArgs eClick) => OpenModDialog.ShowDialog(this); AdvancedPanel.Controls.Add(AdvancedLabel = new Label() { Font = GlobalFont, Text = "or drag-and-drop a folder / .zip here", TextAlign = ContentAlignment.MiddleCenter }); AdvancedPanel.Controls.Add(AdvancedAutoRunCheckbox = new CheckBox() { Font = GlobalFont, Text = "CLOSE " + ETGFinder.MainName + " && run when mod installed", TextAlign = ContentAlignment.MiddleCenter }); if (ETGFinder.Platform.HasFlag(ETGPlatform.MacOS)) { AdvancedAutoRunCheckbox.Text = "CLOSE Gungeon && run when mod installed"; } AdvancedPanel.Controls.Add(AdvancedShowLogOnInstallCheckbox = new CheckBox() { Font = GlobalFont, Text = "Automatically show log when installing", TextAlign = ContentAlignment.MiddleCenter }); AdvancedShowLogOnInstallCheckbox.CheckedChanged += delegate(object sender, EventArgs e) { ETGInstallerSettings.Save(); }; AdvancedPanel.Controls.Add(AdvancedOfflineCheckbox = new CheckBox() { Font = GlobalFont, Text = "Offline mode - only use the APIs here", TextAlign = ContentAlignment.MiddleCenter }); AdvancedOfflineCheckbox.CheckedChanged += delegate(object senderCheck, EventArgs eCheck) { ETGModder.IsOffline = RepoHelper.IsOffline = AdvancedOfflineCheckbox.Checked; DownloadModsList(); ETGInstallerSettings.Save(); }; AdvancedPanel.Controls.Add(AdvancedBinaryWrappedCheckbox = new CheckBox() { Font = GlobalFont, Text = ETGFinder.MainName + " is a wrapper, use EtG.bin", TextAlign = ContentAlignment.MiddleCenter }); AdvancedBinaryWrappedCheckbox.CheckedChanged += delegate(object senderCheck, EventArgs eCheck) { ETGFinder.IsBinaryWrapped = AdvancedBinaryWrappedCheckbox.Checked; ETGFinder.FindETG(); ETGInstallerSettings.Save(); }; RefreshManualPanel(); }
public static void PatchExe(this InstallerWindow ins, BinaryReader bi, BinaryWriter bo) { BinaryHelper.Replace(bi, bo, NativeResourceReplacementMap); }
public static void Uninstall(this InstallerWindow ins) { if (ins.MainMod == null) { return; } ins.MainMod.Dispose(); // Uninstall can be invoked without the installer running ins.Invoke(() => ExePath = ins.ExePathBox.Text).Wait(); if (ETGFinder.IsBinaryWrapped) { ExePath = Path.Combine(Directory.GetParent(ExePath).FullName, ETGFinder.MainName); } string pathGame = ins.MainModDir; string pathBackup = Path.Combine(pathGame, "ModBackup"); if (!Directory.Exists(pathBackup)) { return; } string[] files = Directory.GetFiles(pathGame); ins.InitProgress("Removing leftover files", files.Length + 1); for (int i = 0; i < files.Length; i++) { string file = Path.GetFileName(files[i]); if (!file.Contains(".mm.")) { continue; } ins.Log("Removing: ").LogLine(file); ins.SetProgress("Removing: " + file, i); File.Delete(files[i]); } if (ins.ModVersion != null) { ins.Log("Found previous mod installation: ").LogLine(ins.ModVersion); ins.LogLine("Reverting to unmodded backup..."); } else { ins.LogLine("No previous mod installation found."); ins.LogLine("Still reverting to unmodded backup..."); } string etgBackup = Path.Combine(pathBackup, ETGFinder.MainName); ins.Log("Reverting: ").LogLine(ETGFinder.MainName); if (File.Exists(etgBackup)) { File.Delete(ExePath); File.Move(etgBackup, ExePath); } else { ins.Log("WARNING: Backup not found for ").LogLine(ETGFinder.MainName); } files = Directory.GetFiles(pathBackup); ins.InitProgress("Uninstalling ETGMod", files.Length + 1); for (int i = 0; i < files.Length; i++) { string file = Path.GetFileName(files[i]); ins.Log("Reverting: ").LogLine(file); ins.SetProgress("Reverting: " + file, i); string origPath = Path.Combine(pathGame, file); File.Delete(origPath); File.Move(files[i], origPath); } ins.LogLine("Reloading Assembly-CSharp.dll"); ins.SetProgress("Reloading Assembly-CSharp.dll", files.Length); ins.MainMod = new MonoModder() { InputPath = ins.MainModIn }; ins.MainMod.SetupETGModder(); #if DEBUG if (LogPath == null) { ins.MainMod.Read(); // Read main module first ins.MainMod.ReadMod(ins.MainModDir); // ... then mods ins.MainMod.MapDependencies(); // ... then all dependencies } else using (FileStream fileStream = File.Open(LogPath, FileMode.Append)) { using (StreamWriter streamWriter = new StreamWriter(fileStream)) { ins.MainMod.Logger = (string s) => ins.OnActivity(); ins.MainMod.Logger += (string s) => streamWriter.WriteLine(s); // MonoMod.MonoModSymbolReader.MDBDEBUG = true; #endif ins.MainMod.Read(); // Read main module first ins.MainMod.ReadMod(ins.MainModDir); // ... then mods ins.MainMod.MapDependencies(); // ... then all dependencies #if DEBUG Mono.Cecil.TypeDefinition etgMod = ins.MainMod.Module.GetType("ETGMod"); if (etgMod != null) { for (int i = 0; i < etgMod.Methods.Count; i++) { Mono.Cecil.Cil.MethodBody body = etgMod.Methods[i].Body; } } } } ins.MainMod.Logger = null; #endif ins.EndProgress("Uninstalling complete."); }
public static bool Mod(this InstallerWindow ins, string file) { string inPath = Path.Combine(ins.MainModDir, file); string outPath = Path.Combine(ins.MainModDir, file + ".tmp.dll"); MonoModder monomod = new MonoModder() { InputPath = inPath, OutputPath = outPath }; monomod.SetupETGModder(); using (FileStream fileStream = File.Open(LogPath, FileMode.Append)) { using (StreamWriter streamWriter = new StreamWriter(fileStream)) { monomod.Logger = (string s) => ins.OnActivity(); monomod.Logger += (string s) => streamWriter.WriteLine(s); // Unity wants .mdbs // monomod.WriterParameters.SymbolWriterProvider = new Mono.Cecil.Mdb.MdbWriterProvider(); string db = Path.ChangeExtension(inPath, "pdb"); string dbTmp = Path.ChangeExtension(outPath, "pdb"); if (!File.Exists(db)) { db = inPath + ".mdb"; dbTmp = outPath + ".mdb"; } #if !DEBUG RETRY: try { #endif monomod.Read(); // Read main module first monomod.ReadMod(Directory.GetParent(inPath).FullName); // ... then mods monomod.MapDependencies(); // ... then all dependencies monomod.AutoPatch(); // Patch, monomod.Write(); // and write. monomod.Dispose(); // Finally, dispose, because file access happens now. File.Delete(inPath); File.Move(outPath, inPath); if (File.Exists(db)) { File.Delete(db); } if (File.Exists(dbTmp)) { File.Move(dbTmp, db); } return(true); #if !DEBUG } catch (ArgumentException e) { monomod.Dispose(); if (File.Exists(db)) { File.Delete(db); if (File.Exists(dbTmp)) { File.Delete(dbTmp); } goto RETRY; } ins.LogLine(e.ToString()); throw; return(false); } catch (Exception e) { monomod.Dispose(); ins.LogLine(e.ToString()); throw; return(false); } #endif } } }
private static void Install_(this InstallerWindow ins) { ins .Invoke(() => ExePath = ins.ExePathBox.Text) .Invoke(() => AutoRun = ins.AdvancedAutoRunCheckbox.Checked) .Invoke(ETGInstallerSettings.Save) .SetMainEnabled(false) .Wait(); if (ETGFinder.IsBinaryWrapped) { ExePath = Path.Combine(Directory.GetParent(ExePath).FullName, ETGFinder.MainName); } Directory.SetCurrentDirectory(ins.MainModDir); if (AutoRun) { string etg = ETGFinder.ProcessName; Process[] processes = Process.GetProcesses("."); for (int i = 0; i < processes.Length; i++) { Process p = processes[i]; try { if (p.ProcessName != etg) { p.Dispose(); continue; } p.Kill(); p.Dispose(); Thread.Sleep(250); } catch (Exception) { //probably the service acting up or a process quitting p.Dispose(); } } } ins.LogLine("Entering the Modgeon"); //Clean the game from any previous installation if (ETGFinder.Version != ETGFinder.VersionLastRun && ETGFinder.VersionLastRun != null) { ins.ClearBackup(); } else { ins.Uninstall(); } // We need to reload the main dependencies anyway. // As they've been patched, Assembly-CSharp.dll will otherwise refer to the .mm assemblies. // And as their dependencies get patched, we need to actually unload their symbol readers here. ins.MainMod?.Dispose(); ins.Backup("UnityEngine.dll"); ins.Backup("Assembly-CSharp.dll"); ins.BackupETG(); ins.PrepareDeobfuscator(); ins.Deobfuscate("Assembly-CSharp.dll"); //Setup the files and MonoMod instances int mi = -1; if (OverridePaths == null && !IsOffline) { mi = 0; ins.LogLine("Mod #0: Base"); //Check if the revision online is newer RepoHelper.RevisionFile = Path.Combine(ins.MainModDir, "ModCache", "ETGMOD_REVISION.txt"); int revisionOnline = RepoHelper.RevisionOnline; if (RepoHelper.Revision < revisionOnline) { string cachedPath = Path.Combine(ins.MainModDir, "ModCache", "ETGMOD.zip"); if (File.Exists(cachedPath)) { File.Delete(cachedPath); } } if (!IsOffline && !ins.UnzipMod(ins.DownloadCached(RepoHelper.ETGModURL, "ETGMOD.zip"))) { OnInstalled?.Invoke(false); return; } RepoHelper.Revision = revisionOnline; } int[] selectedIndices = null; ins.Invoke(delegate() { int[] _selectedIndices = new int[ins.APIModsList.SelectedIndices.Count]; ins.APIModsList.SelectedIndices.CopyTo(_selectedIndices, 0); selectedIndices = _selectedIndices; }); while (selectedIndices == null) { Thread.Sleep(100); } for (int i = 0; i < selectedIndices.Length; i++) { Tuple <string, string> t = ins.APIMods[selectedIndices[i]]; if (string.IsNullOrEmpty(t.Item2)) { continue; } ins.Log("Mod #").Log((++mi).ToString()).Log(": ").LogLine(t.Item1); if (!ins.UnzipMod(ins.DownloadCached(t.Item2, t.Item1 + ".zip"))) { OnInstalled?.Invoke(false); return; } } List <string> paths = OverridePaths; if (paths == null) { paths = new List <string>(); for (int pi = 0; pi < ins.AdvancedPathBoxes.Count; pi++) { paths.Add(ins.AdvancedPathBoxes[pi].Text); } } for (int pi = 0; pi < paths.Count; pi++) { string path = paths[pi]; if (path.ToLower().EndsWith(".zip")) { ins.Log("Mod #").Log((++mi).ToString()).Log(": ZIP: ").LogLine(path); if (!ins.UnzipMod(File.OpenRead(path))) { OnInstalled?.Invoke(false); return; } } else if (path.ToLower().EndsWith(".mm.dll")) { ins.Log("Mod #").Log((++mi).ToString()).Log(": DLL: ").LogLine(path); File.Copy(path, Path.Combine(ins.MainModDir, Path.GetFileName(path)), true); string pdb = Path.ChangeExtension(path, "pdb"); string mdb = path + ".mdb"; if (File.Exists(pdb)) { File.Copy(pdb, Path.Combine(ins.MainModDir, Path.GetFileName(pdb)), true); } else if (File.Exists(mdb)) { File.Copy(mdb, Path.Combine(ins.MainModDir, Path.GetFileName(mdb)), true); } } else { ins.Log("Mod #").Log((++mi).ToString()).Log(": Folder: ").LogLine(path); string pathGame = ins.MainModDir; string[] files = Directory.GetFiles(path); ins.InitProgress("Copying ETGMod", files.Length); for (int i = 0; i < files.Length; i++) { string file = Path.GetFileName(files[i]); if (!file.Contains(".mm.")) { ins.SetProgress("Skipping: " + file, i); continue; } ins.Log("Copying: ").LogLine(file); ins.SetProgress("Copying: " + file, i); string origPath = Path.Combine(pathGame, file); File.Copy(files[i], origPath, true); } ins.EndProgress("Copying ETGMod complete."); } } if (Blacklist.Count != 0) { ins.LogLine(); ins.Log(Blacklist.Count.ToString()).LogLine(" mods on the blacklist - removing them!"); for (int i = 0; i < Blacklist.Count; i++) { string blacklisted = Blacklist[i]; string pathGame = ins.MainModDir; string blacklistedPath = Path.Combine(pathGame, blacklisted); ins.Log(blacklisted).Log(" blacklisted - "); if (!File.Exists(blacklistedPath)) { ins.LogLine("Not found though."); continue; } ins.LogLine("BURN THE WITCH!"); File.Delete(blacklistedPath); } ins.LogLine(); } LogPath = Path.Combine(ins.MainModDir, "ETGModInstallLog.txt"); if (File.Exists(LogPath)) { File.Delete(LogPath); } ins.LogLine(); ins.LogLine("Now comes the real \"modding\" / patching process."); ins.LogLine("It may seem like the Installer may be stuck sometimes. Go make"); ins.LogLine("yourself a coffee in the meantime - it doesn't get stuck."); ins.LogLine("It may *crash*, though - and in this case, debug stuff appears"); ins.LogLine("here. Please put that debug stuff onto http://pastebin.com/ and"); ins.LogLine("send it to the #modding channel in https://discord.gg/etg"); ins.LogLine(); ins.PatchExe(); if (!ins.Mod("UnityEngine.dll")) { OnInstalled?.Invoke(false); return; } if (!ins.Mod("Assembly-CSharp.dll")) { OnInstalled?.Invoke(false); return; } ins.EndProgress("Modding complete."); ins.LogLine("Back with the coffee? We're done! Look at the top-right!"); ins.LogLine("You should see [just installed]. Feel free to start EtG."); ins.LogLine("If EtG crashes with ETGMod, go to the EtG folder (that one"); ins.Log("where ").Log(ETGFinder.MainName).LogLine(" is, basically the path at the top-right),"); ins.LogLine("then go to EtG_Data (that scary folder with many files) and"); ins.LogLine("upload output_log.txt to #modding in https://discord.gg/etg"); ins.LogLine("Good luck - Have fun!"); ins.ExeSelected(ExePath, " [just installed]"); if (AutoRun) { Process etg = new Process(); etg.StartInfo.FileName = ExePath; etg.Start(); } OnInstalled?.Invoke(true); }
public static byte[] Download(this InstallerWindow ins, string url) { if (IsOffline) { return(null); } byte[] data = null; ins.Log("Downloading ").Log(url).LogLine("..."); ins.InitProgress("Starting download", 1); DateTime timeStart = DateTime.Now; using (WebClient wc = new WebClient()) { using (Stream s = wc.OpenRead(url)) { long sLength; if (s.CanSeek) { //Mono sLength = s.Length; } else { //.NET sLength = getLength(url); } data = new byte[sLength]; long progressSize = sLength; int progressScale = 1; while (progressSize > int.MaxValue) { progressScale *= 10; progressSize = sLength / progressScale; } ins.InitProgress("Downloading", (int)progressSize); DateTime timeLast = timeStart; //if downloading to another stream, use CopyTo int read; int readForSpeed = 0; int pos = 0; int speed = 0; TimeSpan td; while (pos < data.Length) { read = s.Read(data, pos, Math.Min(2048, data.Length - pos)); pos += read; readForSpeed += read; td = (DateTime.Now - timeLast); if (td.TotalMilliseconds > 100) { speed = (int)((readForSpeed / 1024D) / (double)td.TotalSeconds); readForSpeed = 0; timeLast = DateTime.Now; } ins.SetProgress( "Downloading - " + (int)(Math.Round(100D * ((double)(pos / progressScale) / (double)progressSize))) + "%, " + speed + " KiB/s", (int)(pos / progressScale) ); } } } ins.EndProgress("Download complete"); string logSize = (data.Length / 1024D).ToString(CultureInfo.InvariantCulture); logSize = logSize.Substring(0, Math.Min(logSize.IndexOf('.') + 3, logSize.Length)); string logTime = (DateTime.Now - timeStart).TotalSeconds.ToString(CultureInfo.InvariantCulture); logTime = logTime.Substring(0, Math.Min(logTime.IndexOf('.') + 3, logTime.Length)); ins.Log("Download complete, ").Log(logSize).Log(" KiB in ").Log(logTime).LogLine(" s."); return(data); }
public static void ExeSelected(this InstallerWindow ins, string path, string suffix = null) { if (InstallerWindow.InstantClearSymbols != null) { InstallerWindow.Instance.ClearSymbols(InstallerWindow.InstantClearSymbols); InstallerWindow.InstantClearSymbols = null; } if (string.IsNullOrEmpty(path)) { path = null; } string origPath = path; ins.Invoke(delegate() { ins.InstallButton.Enabled = false; ins.ExePathBox.Text = origPath; ins.ExeStatusLabel.Text = "EtG [checking version]"; if (suffix != null) { ins.ExeStatusLabel.Text += suffix; } ins.ExeStatusLabel.BackColor = Color.FromArgb(127, 255, 255, 63); }); if (path != null && (ins.MainMod == null || ins.MainModIn != path)) { if (!Platform.HasFlag(ETGPlatform.MacOS)) { path = Path.Combine(Directory.GetParent(path).FullName, "EtG_Data", "Managed", "Assembly-CSharp.dll"); } else { path = Path.Combine(Directory.GetParent(path).Parent.Parent.FullName, "Resources", "Data", "Managed", "Assembly-CSharp.dll"); } if (!File.Exists(path)) { path = null; } } ins.ModVersion = null; ins.MainMod?.Dispose(); if (path == null) { ins.MainMod = null; ins.Invoke(delegate() { ins.ExeStatusLabel.Text = "No " + MainName + " selected"; ins.ExeStatusLabel.BackColor = Color.FromArgb(127, 255, 63, 63); ins.ExePathBox.Text = ""; ins.InstallButton.Enabled = false; OnExeSelected?.Invoke(false); }); return; } ins.MainMod = new MonoModder() { InputPath = ins.MainModIn = path // Output set when actually patching }; ins.MainMod.SetupETGModder(); ins.MainModDir = Directory.GetParent(ins.MainModIn).FullName; //We want to read the assembly now already. Writing is also handled manually. try { ins.MainMod.Read(false); } catch (BadImageFormatException) { //this is not the assembly we need... ins.ExeSelected(null); return; } /* catch (MonoSymbolFileException) { * // Mono.Cecil keeps the file handle for itself; We need to restart here. * ins.MainMod.Dispose(); * ins.RestartAndClearSymbols(); * return; * }*/catch (Exception e) { //Something went wrong. ins.Log("Something went horribly wrong after you've selected ").LogLine(MainName); ins.LogLine("Blame Zatherz and send him this log ASAP!"); ins.Log("PATH: ").LogLine(path); ins.Log("DIR: ").LogLine(ins.MainModDir); ins.LogLine(e.ToString()); ins.ExeSelected(null); return; } TypeDefinition ModType = ins.MainMod.Module.GetType("ETGMod"); if (ModType != null) { MethodDefinition ModCctor = null; for (int i = 0; i < ModType.Methods.Count; i++) { if (ModType.Methods[i].IsStatic && ModType.Methods[i].IsConstructor) { ModCctor = ModType.Methods[i]; break; } } /* * .method private hidebysig specialname rtspecialname static * void .cctor () cil managed * { * // Method begins at RVA 0x2d6cec * // Code size 184 (0xb8) * .maxstack 3 * * IL_0000: ldc.i4.0 * IL_0001: ldc.i4.1 * IL_0002: ldc.i4.0 * IL_0003: newobj instance void [mscorlib]System.Version::.ctor(int32, int32, int32) * IL_0008: stsfld class [mscorlib]System.Version ETGMod::BaseVersion * IL_000d: ldc.i4.0 * IL_000e: stsfld int32 ETGMod::BaseTravisBuild * IL_0013: ldc.i4.1 * IL_0014: ldstr "debug" * IL_0019: newobj instance void ETGMod/Profile::.ctor(int32, string) * IL_001e: stsfld class ETGMod/Profile ETGMod::BaseProfile */ if (ModCctor != null) { string modVersion = ""; string modBuild = ""; string modProfile = ""; for (int i = 0; i < ModCctor.Body.Instructions.Count; i++) { Instruction instructionField = ModCctor.Body.Instructions[i]; if (instructionField.OpCode != OpCodes.Stsfld) { continue; } FieldReference field = (FieldReference)instructionField.Operand; if (field.Name == "BaseVersion") { int count = ((MethodReference)ModCctor.Body.Instructions[i - 1].Operand).Parameters.Count; for (int ii = i - count - 1; ii < i - 1; ii++) { modVersion += ModCctor.Body.Instructions[ii].GetInt(); if (ii < i - 2) { modVersion += "."; } } } if (field.Name == "BaseTravisBuild") { int build = ModCctor.Body.Instructions[i - 1].GetInt(); if (build != 0) { modBuild = "-" + build; } } if (field.Name == "BaseProfile") { string profile = ModCctor.Body.Instructions[i - 2].Operand as string; if (!string.IsNullOrEmpty(profile)) { modProfile = "-" + profile; } } } ins.ModVersion = modVersion + modBuild + modProfile; } } ins.Invoke(delegate() { ins.ExeStatusLabel.Text = "Enter The Gungeon"; ins.ExeStatusLabel.BackColor = Color.FromArgb(127, 63, 255, 91); if (ins.ModVersion != null) { ins.ExeStatusLabel.Text += " [Mod:"; ins.ExeStatusLabel.Text += ins.ModVersion; ins.ExeStatusLabel.Text += "]"; } if (suffix != null) { ins.ExeStatusLabel.Text += suffix; } ins.ExePathBox.Text = origPath; ins.InstallButton.Enabled = true; ETGInstallerSettings.Save(); OnExeSelected?.Invoke(true); }); }