using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Navigation; using System.IO; using System.Net; using ModernWpf.Controls; using System.Windows.Forms; using System.Media; using Path = System.IO.Path; using File = System.IO.File; using EZPPClient_Installer.Utils; using IWshRuntimeLibrary; using Semver; #if DEBUG using MessageBox = System.Windows.MessageBox; #endif namespace EZPPClient_Installer { public partial class MainWindow : Window { public static MainWindow Instance { get; private set; } [DllImport("kernel32.dll")] private static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, SymbolicLink dwFlags); private static SemVersion VERSION = new SemVersion(1, 1, 1); public static string RELEASESTREAM; public MainWindow() { var alreadyRunning = System.Diagnostics.Process.GetProcessesByName(System.IO.Path.GetFileNameWithoutExtension(System.Reflection.Assembly.GetEntryAssembly().Location)).Count() > 1; if (alreadyRunning) { this.Close(); return; } Instance = this; InitializeComponent(); Init(); } private async void Init() { UpdateInstallButton(false); UpdateState status = await CheckForInstallerUpdates(); switch (status) { case UpdateState.UpdateFound: ShowInstallerUpdateDialog(); return; case UpdateState.Error: return; } string versionString = "v" + VERSION; InstallerWindow.Title = "EZPPClient Installer " + versionString.Replace(",", "."); await FetchReleaseStreams(); string osuPath = OsuUtil.osuInstallDir(); if (osuPath.EndsWith(@"\EZPPClient")) { osuPath = osuPath.Replace(@"\EZPPClient", ""); } bool isValidFolder = await isValidOsuFolder(osuPath); UpdateInstallButton(!string.IsNullOrEmpty(osuPath)); if(string.IsNullOrEmpty(osuPath)) folderTextbox.Text = ""; if (!string.IsNullOrEmpty(osuPath) && isValidFolder) { bool foundInstall = isEZPPClientInstallationFound(osuPath); if (foundInstall) { string osuEZPPPath = osuPath + @"\EZPPClient"; Uninstall_Button.Visibility = Visibility.Visible; UpdateInstallButton(false); List outdatedFiles = await DownloadUtil.FilesToDownload(osuEZPPPath, false); if (!IsEmpty(outdatedFiles)) { UpdateInstallButton(true); InstallButton.Content = "Update"; linkFoldersCheckbox.IsEnabled = false; } } folderTextbox.Text = osuPath; } else await new ContentDialog() { Title = "Hmmm..", Content = "We failed to locate your osu! Installation path, please define it manually qwq", PrimaryButtonText = "Okay qwq" }.ShowAsync(); } private async Task FetchReleaseStreams() { List releases = await ReleaseStreamUtil.GetAvailableReleaseStreams(); foreach(string release in releases) { ReleaseStreamComboBox.Items.Add(release.UppercaseFirst().Trim()); } string systemRelease = RegistryUtil.GetUsedReleaseStream("Stable"); bool containsRelease = releases.Contains(systemRelease); ReleaseStreamComboBox.SelectedValue = systemRelease; } private async Task CheckForInstallerUpdates() { #if !DEBUG try { using (WebClient client = new WebClient()) { client.Headers.Add("user-agent", "EZPPClientInstaller"); string newVersionString = await client.DownloadStringTaskAsync("https://new.ez-pp.farm/ezppclient?version"); SemVersion newVersion = new SemVersion(9, 9, 9); try { newVersion = SemVersion.Parse(newVersionString); } catch (Exception e){ ShowErrorDialog(e.Message); } int versionsBehind = newVersion.CompareTo(VERSION); if (versionsBehind > 0) return UpdateState.UpdateFound; } } catch (WebException e) { ShowErrorDialog(e.Message); return UpdateState.Error; } #endif return UpdateState.UpToDate; } private async void ShowErrorDialog(string error) { var dialog = new ContentDialog() { Title = "Oops...", Content = "Failed to connect to the EZPPFarm Website.\n" + error, PrimaryButtonText = "Exit" }; await dialog.ShowAsync(); this.Close(); } private async void ShowInstallerUpdateDialog() { UpdateInstallButton(false); var dialog = new ContentDialog() { Title = "New version available.", Content = "Please download the latest version.", PrimaryButtonText = "Download" }; var result = await dialog.ShowAsync(); if (result == ContentDialogResult.Primary) { System.Diagnostics.Process.Start("https://ez-pp.farm/download"); ShowInstallerUpdateDialog(); } } private void UpdateInstallButton(bool enable) { InstallButton.Content = enable ? "Install" : "Reinstall"; linkFoldersCheckbox.IsEnabled = enable; } private async void Install_Click(object sender, RoutedEventArgs e) { string osuFolder = folderTextbox.Text; bool isValid = await isValidOsuFolder(osuFolder); if (!isValid) { SystemSounds.Asterisk.Play(); await new ContentDialog() { Title = "Hmmm..", Content = "It seems like the selected folder is not a osu! installation.", PrimaryButtonText = "Okay qwq" }.ShowAsync(); return; } var type = InstallButton.Content; RegistryUtil.SetUsedReleaseStream(RELEASESTREAM); if (!Directory.Exists(osuFolder + "\\EZPPClient")) { Directory.CreateDirectory(osuFolder + "\\EZPPClient"); } string EZPPFolder = osuFolder + @"\EZPPClient"; if (ProcessUtil.ProgramIsRunning(folderTextbox.Text + @"\EZPPClient.exe")) { _ = new ContentDialog() { Title = "Oops..", Content = "The EZPPClient is currently running.", PrimaryButtonText = "Oh" }.ShowAsync(); return; } if (InstallButton.Content.Equals("Install")) { if (linkFoldersCheckbox.IsChecked.Value) { if (!Directory.Exists(string.Format(@"{0}\Skins\", EZPPFolder)) && Directory.Exists(string.Format(@"{0}\Skins\", osuFolder))) { if (!CreateSymbolicLink(string.Format(@"{0}\Skins\", EZPPFolder), string.Format(@"{0}\Skins\", osuFolder), SymbolicLink.Directory)) { ContentDialog dg = new ContentDialog() { Title = "Hmmm..", Content = "Failed to create Symlink to Skins folder.", PrimaryButtonText = "Okay :/" }; await dg.ShowAsync(); } } else { ContentDialog dg = new ContentDialog() { Title = "Hmmm..", Content = "Failed to locate Skins folder.", PrimaryButtonText = "Okay :/" }; await dg.ShowAsync(); } if (!Directory.Exists(string.Format(@"{0}\Songs\", EZPPFolder)) && Directory.Exists(string.Format(@"{0}\Songs\", osuFolder))) { if (!CreateSymbolicLink(string.Format(@"{0}\Songs\", EZPPFolder), string.Format(@"{0}\Songs\", osuFolder), SymbolicLink.Directory)) { ContentDialog dg = new ContentDialog() { Title = "Hmmm..", Content = "Failed to create Symlink to Songs folder.", PrimaryButtonText = "Okay :/" }; await dg.ShowAsync(); } } else { ContentDialog dg = new ContentDialog() { Title = "Hmmm..", Content = "Failed to locate Songs folder.", PrimaryButtonText = "Okay :/" }; await dg.ShowAsync(); } } string userName = Environment.UserName; if(File.Exists(string.Format(@"{0}\osu!.{1}.cfg", osuFolder, userName)) && !File.Exists(string.Format(@"{0}\osu!.{1}.cfg", EZPPFolder, userName))) { try { if (!CreateSymbolicLink(string.Format(@"{0}\osu!.{1}.cfg", EZPPFolder, userName), string.Format(@"{0}\osu!.{1}.cfg", osuFolder, userName), SymbolicLink.File)) { ContentDialog dg = new ContentDialog() { Title = "Hmmm..", Content = "Failed to create Symlink to cfg file.", PrimaryButtonText = "Okay :/" }; await dg.ShowAsync(); } } catch (Exception) { ContentDialog dg = new ContentDialog() { Title = "Hmmm..", Content = string.Format("Failed to link osu!.{0}.cfg", userName), PrimaryButtonText = "Okay :/" }; await dg.ShowAsync(); } } if (File.Exists(string.Format(@"{0}\scores.db", osuFolder)) && !File.Exists(string.Format(@"{0}\scores.db", EZPPFolder))) { try { if (!CreateSymbolicLink(string.Format(@"{0}\scores.db", EZPPFolder), string.Format(@"{0}\scores.db", osuFolder), SymbolicLink.File)) { ContentDialog dg = new ContentDialog() { Title = "Hmmm..", Content = "Failed to create Symlink to scores.db file.", PrimaryButtonText = "Okay :/" }; await dg.ShowAsync(); } } catch (Exception) { ContentDialog dg = new ContentDialog() { Title = "Hmmm..", Content = "Failed to link scores.db", PrimaryButtonText = "Okay :/" }; await dg.ShowAsync(); } } if (File.Exists(string.Format(@"{0}\collection.db", osuFolder)) && !File.Exists(string.Format(@"{0}\collection.db", EZPPFolder))) { try { if (!CreateSymbolicLink(string.Format(@"{0}\collection.db", EZPPFolder), string.Format(@"{0}\collection.db", osuFolder), SymbolicLink.File)) { ContentDialog dg = new ContentDialog() { Title = "Hmmm..", Content = "Failed to create Symlink to collection.db file.", PrimaryButtonText = "Okay :/" }; await dg.ShowAsync(); } } catch (Exception) { ContentDialog dg = new ContentDialog() { Title = "Hmmm..", Content = "Failed to link collection.db", PrimaryButtonText = "Okay :/" }; await dg.ShowAsync(); } } if (File.Exists(string.Format(@"{0}\osu!.db", osuFolder)) && !File.Exists(string.Format(@"{0}\osu!.db", EZPPFolder))) { try { if (!CreateSymbolicLink(string.Format(@"{0}\osu!.db", EZPPFolder), string.Format(@"{0}\osu!.db", osuFolder), SymbolicLink.File)) { ContentDialog dg = new ContentDialog() { Title = "Hmmm..", Content = "Failed to create Symlink to osu!.db file.", PrimaryButtonText = "Okay :/" }; await dg.ShowAsync(); } } catch (Exception) { ContentDialog dg = new ContentDialog() { Title = "Hmmm..", Content = "Failed to link osu!.db", PrimaryButtonText = "Okay :/" }; await dg.ShowAsync(); } } } List filesToDownload = await DownloadUtil.FilesToDownload(EZPPFolder, InstallButton.Content.Equals("Reinstall")); Visibility prevInstallBtnVis = InstallButton.Visibility; Visibility prevUninstallBtnVis = Uninstall_Button.Visibility; Visibility prevLinkFCVis = linkFoldersCheckbox.Visibility; BrowseButton.IsEnabled = false; InstallButton.Visibility = Visibility.Hidden; Uninstall_Button.Visibility = Visibility.Hidden; linkFoldersCheckbox.Visibility = Visibility.Hidden; ReleaseStreamComboBox.IsEnabled = false; Download_Text.Visibility = Visibility.Visible; Download_Progressbar.Visibility = Visibility.Visible; await DownloadUtil.DownloadFiles(filesToDownload, EZPPFolder); if (type.Equals("Install")) { string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory); string desktopLinkFile = Path.Combine(desktopPath, "Start EZPPClient.lnk"); string startMenuPath = Environment.GetFolderPath(Environment.SpecialFolder.StartMenu); string startMenuPathPrograms = Path.Combine(startMenuPath, "Programs"); string startMenuLinkFile = Path.Combine(startMenuPathPrograms, "Start EZPPClient.lnk"); if (!File.Exists(desktopLinkFile)) { WshShell shell = new WshShell(); IWshShortcut shortcut = (IWshShortcut)shell.CreateShortcut(desktopLinkFile); shortcut.Description = $"Starts the EZPPClient"; shortcut.TargetPath = Path.Combine(EZPPFolder, "EZPPClient.exe"); shortcut.Save(); } if (!File.Exists(startMenuLinkFile)) { WshShell shell = new WshShell(); IWshShortcut shortcut = (IWshShortcut)shell.CreateShortcut(startMenuLinkFile); shortcut.Description = $"Starts the EZPPClient"; shortcut.TargetPath = Path.Combine(EZPPFolder, "EZPPClient.exe"); shortcut.Save(); } } InstallButton.Visibility = prevInstallBtnVis; Uninstall_Button.Visibility = prevUninstallBtnVis; linkFoldersCheckbox.Visibility = prevLinkFCVis; BrowseButton.IsEnabled = true; ReleaseStreamComboBox.IsEnabled = true; Download_Text.Visibility = Visibility.Hidden; Download_Progressbar.Visibility = Visibility.Hidden; Uninstall_Button.Visibility = Visibility.Visible; UpdateInstallButton(false); List outdatedFiles = await DownloadUtil.FilesToDownload(EZPPFolder, false); if (!IsEmpty(outdatedFiles)) { UpdateInstallButton(true); InstallButton.Content = "Update"; linkFoldersCheckbox.IsEnabled = false; string updateText = "It seems like the installation was faulty, please hit Update to repair the EZPPClient."; switch (type) { case "Update": updateText = "It seems like the update was faulty, please hit Update to repair the EZPPClient."; break; case "Reinstall": updateText = "It seems like the reinstallation was faulty, please hit Update to repair the EZPPClient."; break; } _ = new ContentDialog() { Title = "Hmmm..", Content = updateText, PrimaryButtonText = "Okay qwq" }.ShowAsync(); SystemSounds.Asterisk.Play(); this.Focus(); } else { string updateText = "The EZPPClient was successfully installed!"; switch (type) { case "Update": updateText = "The EZPPClient was successfully updated!"; break; case "Reinstall": updateText = "The EZPPClient was successfully reinstalled!"; break; } _ = new ContentDialog() { Title = "Success!", Content = updateText, PrimaryButtonText = "Yaaay :3" }.ShowAsync(); SystemSounds.Asterisk.Play(); this.Focus(); UpdateInstallButton(false); } } private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e) { System.Diagnostics.Process.Start("https://ez-pp.farm/"); e.Handled = true; } private async void Browse_Click(object sender, RoutedEventArgs e) { if (InstallButton.Visibility == Visibility.Hidden) return; using (var fbd = new FolderBrowserDialog()) { DialogResult result = fbd.ShowDialog(); if (result == System.Windows.Forms.DialogResult.OK && !string.IsNullOrWhiteSpace(fbd.SelectedPath)) { bool isValid = await isValidOsuFolder(fbd.SelectedPath); if (!isValid) { SystemSounds.Asterisk.Play(); await new ContentDialog() { Title = "Hmmm..", Content = "It seems like this location is not an osu! installation.", PrimaryButtonText = "Okay qwq" }.ShowAsync(); } else { bool foundInstall = isEZPPClientInstallationFound(fbd.SelectedPath); folderTextbox.Text = fbd.SelectedPath; if (foundInstall) { Uninstall_Button.Visibility = Visibility.Visible; UpdateInstallButton(false); string osuEZPPPath = folderTextbox.Text + @"\EZPPClient"; List outdatedFiles = await DownloadUtil.FilesToDownload(osuEZPPPath, false); if (!IsEmpty(outdatedFiles)) { UpdateInstallButton(true); InstallButton.Content = "Update"; } else { UpdateInstallButton(true); } } else { UpdateInstallButton(true); } } } } } private async void Uninstall_Button_Click(object sender, RoutedEventArgs e) { if(ProcessUtil.ProgramIsRunning(folderTextbox.Text + @"\EZPPClient.exe")) { _ = new ContentDialog() { Title = "Oops..", Content = "The EZPPClient is currently running.", PrimaryButtonText = "Oh" }.ShowAsync(); return; } SystemSounds.Asterisk.Play(); ContentDialog cd = new ContentDialog() { Title = "Confirm uninstall", Content = "Are you sure you want to remove the EZPPClient?", PrimaryButtonText = "Yes", SecondaryButtonText = "No" }; ContentDialogResult result = await cd.ShowAsync(); if (result == ContentDialogResult.Primary) { string osuEZPPPath = folderTextbox.Text + @"\EZPPClient"; if (Directory.Exists(osuEZPPPath)) Directory.Delete(osuEZPPPath, true); string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory); string linkFile = Path.Combine(desktopPath, "Start EZPPClient.lnk"); if (File.Exists(linkFile)) File.Delete(linkFile); string startMenuPath = Environment.GetFolderPath(Environment.SpecialFolder.StartMenu); string startMenuPathPrograms = Path.Combine(startMenuPath, "Programs"); string startMenuLinkFile = Path.Combine(startMenuPathPrograms, "Start EZPPClient.lnk"); if (File.Exists(startMenuLinkFile)) File.Delete(startMenuLinkFile); _ = new ContentDialog() { Title = "Success!", Content = "The EZPPClient was successfully uninstalled!", PrimaryButtonText = "Okay" }.ShowAsync(); SystemSounds.Asterisk.Play(); this.Focus(); Uninstall_Button.Visibility = Visibility.Hidden; UpdateInstallButton(true); } } private bool isEZPPClientInstallationFound(string folder) { return Directory.Exists(folder + @"\EZPPClient"); } private async Task isValidOsuFolder(string folder) { if (string.IsNullOrEmpty(folder) || string.IsNullOrWhiteSpace(folder)) return false; string[] files = Directory.GetFiles(folder, "*"); string[] subDirs = Directory.GetDirectories(folder, "*"); bool hasSongsFolder = false; bool hasSkinsFolder = false; bool hasOsuExecutable = false; foreach (string file in files) { if (file.Contains("osu!.exe")) { hasOsuExecutable = true; break; } } foreach (string dir in subDirs) { if (dir.Contains("Songs") && !hasSongsFolder) hasSongsFolder = true; if (dir.Contains("Skins") && !hasSkinsFolder) hasSkinsFolder = true; if (hasSkinsFolder && hasSongsFolder) break; } return hasOsuExecutable && hasSkinsFolder && hasSongsFolder; } public static bool IsEmpty(List list) { if (list == null) { return true; } return !list.Any(); } private enum SymbolicLink { File, Directory } public static void updateProgress(string file, double progress) { Instance.Download_Progressbar.IsIndeterminate = (progress == 100); Instance.Download_Progressbar.Value = progress; Instance.Download_Text.Content = "Downloading: " + file; } private async void ReleaseStreamComboBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) { bool init = string.IsNullOrEmpty(RELEASESTREAM); RELEASESTREAM = (string)ReleaseStreamComboBox.SelectedValue; if (init) return; ReleaseStreamComboBox.IsEnabled = false; BrowseButton.IsEnabled = false; InstallButton.IsEnabled = false; Uninstall_Button.IsEnabled = false; string osuPath = folderTextbox.Text; bool isValidFolder = await isValidOsuFolder(osuPath); UpdateInstallButton(!string.IsNullOrEmpty(osuPath)); if (!string.IsNullOrEmpty(osuPath) && isValidFolder) { bool foundInstall = isEZPPClientInstallationFound(osuPath); if (foundInstall) { string osuEZPPPath = osuPath + @"\EZPPClient"; Uninstall_Button.Visibility = Visibility.Visible; UpdateInstallButton(false); List outdatedFiles = await DownloadUtil.FilesToDownload(osuEZPPPath, false); if (!IsEmpty(outdatedFiles)) { UpdateInstallButton(true); InstallButton.Content = "Update"; linkFoldersCheckbox.IsEnabled = false; } } folderTextbox.Text = osuPath; } _ = new ContentDialog() { Title = "Success!", Content = "ReleaseStream changed to " + RELEASESTREAM, PrimaryButtonText = "Okay" }.ShowAsync(); ReleaseStreamComboBox.IsEnabled = true; BrowseButton.IsEnabled = true; InstallButton.IsEnabled = true; Uninstall_Button.IsEnabled = true; } } }