Beispiel #1
0
        /// <summary>
        /// If the <see cref="customMirrorTextBox"/> has lost focus, we set its text as the new <see cref="currentMirror"/>.
        /// </summary>
        private void CustomMirrorTextBoxLostFocus(object sender, EventArgs e)
        {
            // Check first, if the text is a valid git repo
            Regex  gitURLRegex = new Regex("https://.*\\.git");
            string mirrorText  = customMirrorTextBox.Text;

            if (!gitURLRegex.IsMatch(mirrorText))
            {
                log.Info("User used " + mirrorText + " as a custom Mirror, didn't pass git validation test.");
                MessageBox.Show(this, HelperMethods.GetText(Text.InvalidGitURL, mirrorText), Text.ErrorWindowTitle, MessageBoxType.Error);
                return;
            }

            currentMirror = mirrorText;
            CrossPlatformOperations.WriteToConfig("CustomMirrorText", currentMirror);

            log.Info("Overwriting mirror in gitconfig.");

            // Check if the gitConfig exists, if yes regex the gitURL, and replace it with the new current Mirror.
            string gitConfigPath = CrossPlatformOperations.CURRENTPATH + "/PatchData/.git/config";

            if (!File.Exists(gitConfigPath))
            {
                return;
            }
            string gitConfig = File.ReadAllText(gitConfigPath);
            Match  match     = gitURLRegex.Match(gitConfig);

            gitConfig = gitConfig.Replace(match.Value, currentMirror);
            File.WriteAllText(gitConfigPath, gitConfig);

            log.Info("Custom Mirror has been set to " + currentMirror + ".");
        }
Beispiel #2
0
        /// <summary>Gets called when user selects a different item from <see cref="mirrorDropDown"/>.
        /// It then writes that to the config, and if <see cref="updateState"/> is not <see cref="PlayButtonState.Downloading"/>
        /// it also overwrites the upstream URL in .git/config.</summary>
        private void MirrorDropDownSelectedIndexChanged(object sender, EventArgs e)
        {
            currentMirror = mirrorList[mirrorDropDown.SelectedIndex];

            log.Info("Current mirror has been set to " + currentMirror + ".");

            CrossPlatformOperations.WriteToConfig("MirrorIndex", mirrorDropDown.SelectedIndex);

            // Don't overwrite the git config while we download!!!
            if (updateState == PlayButtonState.Downloading)
            {
                return;
            }

            log.Info("Overwriting mirror in gitconfig.");

            // Check if the gitConfig exists, if yes regex the gitURL, and replace it with the new current Mirror.
            string gitConfigPath = CrossPlatformOperations.CURRENTPATH + "/PatchData/.git/config";

            if (!File.Exists(gitConfigPath))
            {
                return;
            }
            string gitConfig   = File.ReadAllText(gitConfigPath);
            Regex  gitURLRegex = new Regex("https://.*\\.git");
            Match  match       = gitURLRegex.Match(gitConfig);

            gitConfig = gitConfig.Replace(match.Value, currentMirror);
            File.WriteAllText(gitConfigPath, gitConfig);
        }
Beispiel #3
0
        /// <summary>
        /// This opens the save directory for the current profile.
        /// </summary>
        private void SaveButtonClickEvent(object sender, EventArgs e)
        {
            if (!IsProfileIndexValid())
            {
                return;
            }
            ProfileXML profile = profileList[modSettingsProfileDropDown.SelectedIndex];

            log.Info("User opened the save directory for profile " + profile.Name + ", which is " + profile.SaveLocation);
            CrossPlatformOperations.OpenFolder(profile.SaveLocation);
        }
Beispiel #4
0
        /// <summary>
        /// Gets called when user tries to close <see cref="MainForm"/>. This does a few things:<br/>
        /// 1) Writes the Width, Height, the check if <see cref="MainForm"/> is currently maximized and the ProfileIndex to the Config<br/>
        /// 2) Checks if current <see cref="updateState"/> is <see cref="PlayButtonState.Downloading"/>. If yes, it creates a Warning to the end user.
        /// </summary>
        private void MainformClosing(object sender, CancelEventArgs e)
        {
            log.Info("Attempting to close MainForm!");

            CrossPlatformOperations.WriteToConfig("Width", ClientSize.Width);
            CrossPlatformOperations.WriteToConfig("Height", ClientSize.Height);
            CrossPlatformOperations.WriteToConfig("IsMaximized", this.WindowState == WindowState.Maximized);
            CrossPlatformOperations.WriteToConfig("ProfileIndex", profileIndex.ToString());

            switch (updateState)
            {
            // If we're currently still downloading, ask first if user really wants to close and cancel the event if necessary
            case PlayButtonState.Downloading:
            {
                var result = MessageBox.Show(this, Text.CloseOnCloningText, Text.WarningWindowTitle, MessageBoxButtons.YesNo,
                                             MessageBoxType.Warning, MessageBoxDefaultButton.No);

                if (result == DialogResult.No)
                {
                    e.Cancel = true;
                }
                else
                {
                    isGitProcessGettingCancelled = true;
                }
                // We don't need to delete any folders here, the cancelled gitClone will do that automatically for us :)
                break;
            }

            // We can't close during installing, so we cancel the event.
            case PlayButtonState.Installing:
            {
                MessageBox.Show(this, Text.CloseOnInstallingText, Text.WarningWindowTitle, MessageBoxButtons.OK, MessageBoxType.Warning);
                e.Cancel = true;
                break;
            }
            }

            // This needs to be made invisible, otherwise a tray indicator will be visible (on linux?) that clicking crashes the application
            //TODO: this sounds like an eto bug. check if this can get reproduced.
            trayIndicator.Visible = false;

            if (e.Cancel)
            {
                log.Info("Cancelled MainForm closing event during UpdateState." + updateState + ".");
            }
            else
            {
                log.Info("Successfully closed MainForm. Exiting main thread.");
            }
        }
Beispiel #5
0
        /// <summary>
        /// Does stuff, depending on the current state of <see cref="apkButtonState"/>.
        /// </summary>
        private async void ApkButtonClickEvent(object sender, EventArgs e)
        {
            // If we're currently creating something, exit
            if (apkButtonState == ApkButtonState.Creating)
            {
                return;
            }

            // Check for java, exit safely with a warning if not found!
            if (!CrossPlatformOperations.IsJavaInstalled())
            {
                MessageBox.Show(this, Text.JavaNotFound, Text.WarningWindowTitle, MessageBoxButtons.OK);
                SetApkButtonState(ApkButtonState.Create);
                UpdateStateMachine();
                log.Error("Java not found! Aborting Android APK creation.");
                return;
            }

            // Check if xdelta is installed on unix, exit with a warning if not.
            if (OS.IsUnix && !CrossPlatformOperations.CheckIfXdeltaIsInstalled())
            {
                MessageBox.Show(this, Text.XdeltaNotFound, Text.WarningWindowTitle, MessageBoxButtons.OK);
                SetApkButtonState(ApkButtonState.Create);
                UpdateStateMachine();
                log.Error("Xdelta not found. Aborting Android APK creation...");
                return;
            }

            UpdateStateMachine();

            if (apkButtonState == ApkButtonState.Create)
            {
                SetApkButtonState(ApkButtonState.Creating);
                UpdateStateMachine();

                progressBar.Visible = true;
                bool useHqMusic = hqMusicAndroidCheck.Checked.Value;

                var progressIndicator = new Progress <int>(UpdateProgressBar);
                await Task.Run(() => Profile.CreateAPK(profileList[profileIndex.Value], useHqMusic, progressIndicator));

                SetApkButtonState(ApkButtonState.Create);
                progressBar.Visible = false;
                UpdateStateMachine();
            }
        }
Beispiel #6
0
        /// <summary>Gets called when <see cref="customMirrorCheck"/> gets clicked, displays a warning <see cref="MessageBox"/>
        /// and enables <see cref="customMirrorTextBox"/> accordingly.</summary>
        private void CustomMirrorCheckChanged(object sender, EventArgs e)
        {
            log.Info("Use Custom Mirror option has been set to " + customMirrorCheck.Checked + ".");
            CrossPlatformOperations.WriteToConfig("CustomMirrorEnabled", (bool)customMirrorCheck.Checked);

            EnableMirrorControlsAccordingly();

            // Create warning dialog when enabling
            if ((bool)customMirrorCheck.Checked)
            {
                MessageBox.Show(this, Text.WarningWindowText, Text.WarningWindowTitle, MessageBoxType.Warning);
                currentMirror = customMirrorTextBox.Text;
            }
            else
            {
                // Revert mirror to selected index in mirror dropdown
                currentMirror = mirrorList[mirrorDropDown.SelectedIndex];
            }
        }
Beispiel #7
0
    /// <summary>
    /// Installs <paramref name="profile"/>.
    /// </summary>
    /// <param name="profile"><see cref="ProfileXML"/> to be installed.</param>
    /// <param name="useHqMusic">Wether to patch this with high quality music or not.</param>
    /// <param name="progress">Provides the current progress of this method.</param>
    public static void InstallProfile(ProfileXML profile, bool useHqMusic, IProgress <int> progress)
    {
        log.Info("Installing profile " + profile.Name + "...");

        string profilesHomePath = CrossPlatformOperations.CURRENTPATH + "/Profiles";
        string profilePath      = profilesHomePath + "/" + profile.Name;

        // Failsafe for Profiles directory
        if (!Directory.Exists(profilesHomePath))
        {
            Directory.CreateDirectory(profilesHomePath);
        }


        // This failsafe should NEVER get triggered, but Miepee's broken this too much for me to trust it otherwise.
        if (Directory.Exists(profilePath))
        {
            Directory.Delete(profilePath, true);
        }

        // Create profile directory
        Directory.CreateDirectory(profilePath);

        // Switch profilePath on Gtk
        if (OS.IsLinux)
        {
            profilePath += "/assets";
            Directory.CreateDirectory(profilePath);
        }
        else if (OS.IsMac)
        {
            // Folder structure for mac is like this:
            // am2r.app -> Contents
            //     -Frameworks (some libs)
            //     -MacOS (runner)
            //     -Resources (asset path)
            profilePath += "/AM2R.app/Contents";
            Directory.CreateDirectory(profilePath);
            Directory.CreateDirectory(profilePath + "/MacOS");
            Directory.CreateDirectory(profilePath + "/Resources");
            profilePath += "/Resources";

            log.Info("ProfileInstallation: Created folder structure.");
        }

        // Extract 1.1
        ZipFile.ExtractToDirectory(CrossPlatformOperations.CURRENTPATH + "/AM2R_11.zip", profilePath);

        // Extracted 1.1
        progress.Report(33);
        log.Info("Profile folder created and AM2R_11.zip extracted.");

        // Set local datapath for installation files
        var dataPath = CrossPlatformOperations.CURRENTPATH + profile.DataPath;

        string datawin = null, exe = null;

        if (OS.IsWindows)
        {
            datawin = "data.win";
            exe     = "AM2R.exe";
        }
        else if (OS.IsLinux)
        {
            datawin = "game.unx";
            // Use the exe name based on the desktop file in the appimage, rather than hardcoding it.
            string desktopContents = File.ReadAllText(CrossPlatformOperations.CURRENTPATH + "/PatchData/data/AM2R.AppDir/AM2R.desktop");
            exe = Regex.Match(desktopContents, @"(?<=Exec=).*").Value;
            log.Info("According to AppImage desktop file, using \"" + exe + "\" as game name.");
        }
        else if (OS.IsMac)
        {
            datawin = "game.ios";
            exe     = "Mac_Runner";
        }
        else
        {
            log.Error(OS.Name + " does not have valid runner / data.win names!");
        }

        log.Info("Attempting to patch in " + profilePath);

        if (OS.IsWindows)
        {
            // Patch game executable
            if (profile.UsesYYC)
            {
                CrossPlatformOperations.ApplyXdeltaPatch(profilePath + "/data.win", dataPath + "/AM2R.xdelta", profilePath + "/" + exe);

                // Delete 1.1's data.win, we don't need it anymore!
                File.Delete(profilePath + "/data.win");
            }
            else
            {
                CrossPlatformOperations.ApplyXdeltaPatch(profilePath + "/data.win", dataPath + "/data.xdelta", profilePath + "/" + datawin);
                CrossPlatformOperations.ApplyXdeltaPatch(profilePath + "/AM2R.exe", dataPath + "/AM2R.xdelta", profilePath + "/" + exe);
            }
        }
        else if (OS.IsUnix) // YYC and VM look exactly the same on Linux and Mac so we're all good here.
        {
            CrossPlatformOperations.ApplyXdeltaPatch(profilePath + "/data.win", dataPath + "/game.xdelta", profilePath + "/" + datawin);
            CrossPlatformOperations.ApplyXdeltaPatch(profilePath + "/AM2R.exe", dataPath + "/AM2R.xdelta", profilePath + "/" + exe);
            // Just in case the resulting file isn't chmoddded...
            Process.Start("chmod", "+x  \"" + profilePath + "/" + exe + "\"")?.WaitForExit();

            // These are not needed by linux or Mac at all, so we delete them
            File.Delete(profilePath + "/data.win");
            File.Delete(profilePath + "/AM2R.exe");
            File.Delete(profilePath + "/D3DX9_43.dll");

            // Move exe one directory out on Linux, move to MacOS folder instead on Mac
            if (OS.IsLinux)
            {
                File.Move(profilePath + "/" + exe, profilePath.Substring(0, profilePath.LastIndexOf("/")) + "/" + exe);
            }
            else
            {
                File.Move(profilePath + "/" + exe, profilePath.Replace("Resources", "MacOS") + "/" + exe);
            }
        }
        else
        {
            log.Error(OS.Name + " does not have patching methods!");
        }

        // Applied patch
        if (OS.IsWindows || OS.IsMac)
        {
            progress.Report(66);
        }
        else if (OS.IsLinux)
        {
            progress.Report(44);                  // Linux will take a bit longer, due to appimage creation
        }
        log.Info("xdelta patch(es) applied.");

        // Install new datafiles
        HelperMethods.DirectoryCopy(dataPath + "/files_to_copy", profilePath);

        // HQ music
        if (!profile.UsesCustomMusic && useHqMusic)
        {
            HelperMethods.DirectoryCopy(CrossPlatformOperations.CURRENTPATH + "/PatchData/data/HDR_HQ_in-game_music", profilePath);
        }


        // Linux post-process
        if (OS.IsLinux)
        {
            string assetsPath = profilePath;
            profilePath = profilePath.Substring(0, profilePath.LastIndexOf("/"));

            // Rename all songs to lowercase
            foreach (var file in new DirectoryInfo(assetsPath).GetFiles())
            {
                if (file.Name.EndsWith(".ogg") && !File.Exists(file.DirectoryName + "/" + file.Name.ToLower()))
                {
                    File.Move(file.FullName, file.DirectoryName + "/" + file.Name.ToLower());
                }
            }

            // Copy AppImage template to here
            HelperMethods.DirectoryCopy(CrossPlatformOperations.CURRENTPATH + "/PatchData/data/AM2R.AppDir", profilePath + "/AM2R.AppDir/");

            // Safety checks, in case the folders don't exist
            Directory.CreateDirectory(profilePath + "/AM2R.AppDir/usr/bin/");
            Directory.CreateDirectory(profilePath + "/AM2R.AppDir/usr/bin/assets/");

            // Copy game assets to the appimageDir
            HelperMethods.DirectoryCopy(assetsPath, profilePath + "/AM2R.AppDir/usr/bin/assets/");
            File.Copy(profilePath + "/" + exe, profilePath + "/AM2R.AppDir/usr/bin/" + exe);

            progress.Report(66);
            log.Info("Gtk-specific formatting finished.");

            // Temp save the currentWorkingDirectory and console.error, change it to profilePath and null, call the script, and change it back.
            string     workingDir = Directory.GetCurrentDirectory();
            TextWriter cliError   = Console.Error;
            Directory.SetCurrentDirectory(profilePath);
            Console.SetError(new StreamWriter(Stream.Null));
            Environment.SetEnvironmentVariable("ARCH", "x86_64");
            Process.Start(CrossPlatformOperations.CURRENTPATH + "/PatchData/utilities/appimagetool-x86_64.AppImage", "-n AM2R.AppDir")?.WaitForExit();
            Directory.SetCurrentDirectory(workingDir);
            Console.SetError(cliError);

            // Clean files
            Directory.Delete(profilePath + "/AM2R.AppDir", true);
            Directory.Delete(assetsPath, true);
            File.Delete(profilePath + "/" + exe);
            if (File.Exists(profilePath + "/AM2R.AppImage"))
            {
                File.Delete(profilePath + "/AM2R.AppImage");
            }
            File.Move(profilePath + "/" + "AM2R-x86_64.AppImage", profilePath + "/AM2R.AppImage");
        }
        // Mac post-process
        else if (OS.IsMac)
        {
            // Rename all songs to lowercase
            foreach (var file in new DirectoryInfo(profilePath).GetFiles())
            {
                if (file.Name.EndsWith(".ogg") && !File.Exists(file.DirectoryName + "/" + file.Name.ToLower()))
                {
                    File.Move(file.FullName, file.DirectoryName + "/" + file.Name.ToLower());
                }
            }
            // Loading custom fonts crashes on Mac, so we delete those if they exist
            if (Directory.Exists(profilePath + "/lang/fonts"))
            {
                Directory.Delete(profilePath + "/lang/fonts", true);
            }
            // Move Frameworks, Info.plist and PkgInfo over
            HelperMethods.DirectoryCopy(CrossPlatformOperations.CURRENTPATH + "/PatchData/data/Frameworks", profilePath.Replace("Resources", "Frameworks"));
            File.Copy(dataPath + "/Info.plist", profilePath.Replace("Resources", "") + "/Info.plist", true);
            File.Copy(CrossPlatformOperations.CURRENTPATH + "/PatchData/data/PkgInfo", profilePath.Replace("Resources", "") + "/PkgInfo", true);
            //Put profilePath back to what it was before
            profilePath = profilesHomePath + "/" + profile.Name;
        }

        // Copy profile.xml so we can grab data to compare for updates later!
        // tldr; check if we're in PatchData or not
        if (new DirectoryInfo(dataPath).Parent?.Name == "PatchData")
        {
            File.Copy(dataPath + "/../profile.xml", profilePath + "/profile.xml");
        }
        else
        {
            File.Copy(dataPath + "/profile.xml", profilePath + "/profile.xml");
        }

        // Installed datafiles
        progress.Report(100);
        log.Info("Successfully installed profile " + profile.Name + ".");
    }
Beispiel #8
0
 /// <summary>
 /// If <see cref="customEnvVarTextBox"/> has lost focus, we write its text to the config.
 /// </summary>
 private void CustomEnvVarTextBoxLostFocus(object sender, EventArgs e)
 {
     log.Info("Custom Environment variables have been set to \"" + customEnvVarTextBox.Text + "\".");
     CrossPlatformOperations.WriteToConfig("CustomEnvVar", customEnvVarTextBox.Text);
 }
        /// <summary>
        /// Loads valid profile entries and reloads the necessary UI components.
        /// </summary>
        private void LoadProfilesAndAdjustLists()
        {
            // Reset loaded profiles
            profileDropDown.Items.Clear();
            profileList.Clear();
            profileIndex = null;

            // Load the profileList
            profileList = Profile.LoadProfiles();

            // Add profile names to the profileDropDown
            foreach (ProfileXML profile in profileList)
            {
                // Archive version notes
                if (!profile.Installable)
                {
                    if (profile.Name.Contains("Community Updates"))
                    {
                        profile.ProfileNotes = Text.ArchiveNotesCommunityUpdates;
                    }
                    else
                    {
                        profile.ProfileNotes = Text.ArchiveNotesMods + "\n\n" + profile.ProfileNotes;
                    }
                }

                profileDropDown.Items.Add(profile.Name);
            }

            // Read the value from the config
            string profIndexString = CrossPlatformOperations.ReadFromConfig("ProfileIndex");

            // Check if either no profile was found or the setting says that the last current profile didn't exist
            if (profileDropDown.Items.Count == 0)
            {
                profileIndex = null;
            }
            else
            {
                // We know that profiles exist at this point, so we're going to point it to 0 instead so the following code doesn't fail
                if (profIndexString == "null")
                {
                    profIndexString = "0";
                }

                // We parse from the settings, and check if profiles got deleted from the last time the launcher has been selected. if yes, we revert the last selection to 0;
                int intParseResult = Int32.Parse(profIndexString);
                profileIndex = intParseResult;
                if (profileIndex >= profileDropDown.Items.Count)
                {
                    profileIndex = 0;
                }
                profileDropDown.SelectedIndex = profileIndex.Value;
            }

            // Update stored profiles in the Profile Settings tab
            modSettingsProfileDropDown.Items.Clear();
            modSettingsProfileDropDown.Items.AddRange(profileDropDown.Items);
            modSettingsProfileDropDown.SelectedIndex = profileDropDown.Items.Count != 0 ? 0 : -1;

            // Refresh the author and version label on the main tab
            if (profileList.Count > 0)
            {
                profileAuthorLabel.Text  = Text.Author + " " + profileList[profileDropDown.SelectedIndex].Author;
                profileVersionLabel.Text = Text.VersionLabel + " " + profileList[profileDropDown.SelectedIndex].Version;
            }

            log.Info("Reloading UI components after loading successful.");

            UpdateStateMachine();
        }
Beispiel #10
0
 /// <summary>Gets called when <see cref="hqMusicAndroidCheck"/> gets clicked and writes its new value to the config.</summary>
 private void HqMusicAndroidCheckChanged(object sender, EventArgs e)
 {
     log.Info("Android HQ Music option has been changed to " + hqMusicAndroidCheck.Checked);
     CrossPlatformOperations.WriteToConfig("MusicHQAndroid", hqMusicAndroidCheck.Checked);
 }
Beispiel #11
0
 /// <summary>Gets called when <see cref="autoUpdateLauncherCheck"/> gets clicked and writes its new value to the config.</summary>
 private void AutoUpdateLauncherCheckChanged(object sender, EventArgs e)
 {
     log.Info("Auto Update Launcher has been set to " + autoUpdateAM2RCheck.Checked + ".");
     CrossPlatformOperations.WriteToConfig("AutoUpdateLauncher", (bool)autoUpdateAM2RCheck.Checked);
 }
Beispiel #12
0
 /// <summary>Gets called when user selects a different item from <see cref="languageDropDown"/> and writes that to the config.</summary>
 private void LanguageDropDownSelectedIndexChanged(object sender, EventArgs e)
 {
     log.Info("languageDropDown.SelectedIndex has been changed to " + languageDropDown.SelectedIndex + ".");
     CrossPlatformOperations.WriteToConfig("Language", languageDropDown.SelectedIndex == 0 ? "Default" : languageDropDown.Items[languageDropDown.SelectedIndex].Text);
 }
Beispiel #13
0
        /// <summary>
        /// Does a bunch of stuff, depending on the current state of <see cref="updateState"/>.
        /// </summary>
        private async void PlayButtonClickEvent(object sender, EventArgs e)
        {
            // State Check
            UpdateStateMachine();

            // Check if 1.1 is installed by forcing invalidation
            Profile.Is11Installed(true);

            switch (updateState)
            {
                #region Download
            case PlayButtonState.Download:

                log.Info("Attempting to clone repository " + currentMirror + "...");
                bool successful = true;

                // Update playButton states
                SetPlayButtonState(PlayButtonState.Downloading);

                // Enable progressBar
                progressBar.Visible   = true;
                progressLabel.Visible = true;
                progressBar.Value     = 0;

                // Set up progressBar update method
                var c = new CloneOptions
                {
                    OnTransferProgress = TransferProgressHandlerMethod
                };

                // Everything after this is on a different thread, so the rest of the launcher isn't locked up.
                try
                {
                    if (Directory.Exists(CrossPlatformOperations.CURRENTPATH + "/PatchData"))
                    {
                        log.Info("PatchData directory already exists, cleaning up...");
                        HelperMethods.DeleteDirectory(CrossPlatformOperations.CURRENTPATH + "/PatchData");
                    }

                    await Task.Run(() => Repository.Clone(currentMirror, CrossPlatformOperations.CURRENTPATH + "/PatchData", c));
                }
                catch (UserCancelledException)
                {
                    // We deliberately cancelled this!
                    successful = false;
                }
                catch (LibGit2SharpException ex)        // This is for any exceptions from libgit
                {
                    // Libgit2sharp error messages are always in english!
                    if (ex.Message.ToLower().Contains("failed to send request") || ex.Message.ToLower().Contains("connection with the server was terminated") ||
                        ex.Message.ToLower().Contains("failed to resolve address"))
                    {
                        log.Error("Internet connection dropped while attempting to clone repository" + currentMirror + "!");
                        MessageBox.Show(this, Text.InternetConnectionDrop, Text.WarningWindowTitle, MessageBoxType.Warning);
                    }
                    else
                    {
                        log.Error("LibGit2SharpException: " + ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace);
                        MessageBox.Show(this, ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace, Text.ErrorWindowTitle, MessageBoxType.Error);
                        if (Directory.Exists(CrossPlatformOperations.CURRENTPATH + "/PatchData"))
                        {
                            HelperMethods.DeleteDirectory(CrossPlatformOperations.CURRENTPATH + "/PatchData");
                        }
                    }
                    successful = false;
                }
                catch (Exception ex)                 // This is if somehow any other exception might get thrown as well.
                {
                    log.Error(ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace);
                    MessageBox.Show(this, ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace, Text.ErrorWindowTitle, MessageBoxType.Error);

                    if (Directory.Exists(CrossPlatformOperations.CURRENTPATH + " / PatchData"))
                    {
                        HelperMethods.DeleteDirectory(CrossPlatformOperations.CURRENTPATH + "/PatchData");
                    }
                    successful = false;
                }

                log.Info("Repository clone attempt finished " + (successful ? "successfully." : "unsuccessfully."));

                currentGitObject = 0;

                // Reset progressBar after clone is finished
                progressLabel.Visible = false;
                progressLabel.Text    = "";
                progressBar.Visible   = false;
                progressBar.Value     = 0;

                // Just need to switch this to anything that isn't an "active" state so SetUpdateState() actually does something
                SetPlayButtonState(PlayButtonState.Install);

                // This needs to be run BEFORE the state check so that the Mod Settings tab doesn't weird out
                LoadProfilesAndAdjustLists();

                // Do a state check
                UpdateStateMachine();

                break;
                #endregion

                #region Downloading

            case PlayButtonState.Downloading:
                var result = MessageBox.Show(this, Text.CloseOnCloningText, Text.WarningWindowTitle, MessageBoxButtons.YesNo, MessageBoxType.Warning, MessageBoxDefaultButton.No);
                if (result != DialogResult.Yes)
                {
                    return;
                }

                log.Info("User cancelled download!");
                isGitProcessGettingCancelled = true;

                // We don't need to delete any folders here, the cancelled gitClone will do that automatically for us :)
                // But we should probably wait a bit before proceeding, since cleanup can take a while
                Thread.Sleep(1000);
                isGitProcessGettingCancelled = false;
                break;

                #endregion

                #region Select11
            case PlayButtonState.Select11:

                log.Info("Requesting user input for AM2R_11.zip...");

                OpenFileDialog fileFinder = GetSingleZipDialog(Text.Select11FileDialog);

                if (fileFinder.ShowDialog(this) != DialogResult.Ok)
                {
                    log.Info("User cancelled the selection.");
                    return;
                }

                // Default filename is whitespace
                if (String.IsNullOrWhiteSpace(fileFinder.FileName))
                {
                    log.Error("User did not supply valid input. Cancelling import.");
                    return;
                }

                // If either a directory was selected or the file somehow went missing, cancel
                if (!File.Exists(fileFinder.FileName))
                {
                    log.Error("Selected AM2R_11.zip file not found! Cancelling import.");
                    break;
                }

                IsZipAM2R11ReturnCodes errorCode = Profile.CheckIfZipIsAM2R11(fileFinder.FileName);
                if (errorCode != IsZipAM2R11ReturnCodes.Successful)
                {
                    log.Error("User tried to input invalid AM2R_11.zip file (" + errorCode + "). Cancelling import.");
                    MessageBox.Show(this, Text.ZipIsNotAM2R11 + "\n\nError Code: " + errorCode, Text.ErrorWindowTitle, MessageBoxType.Error);
                    return;
                }

                // We check if it exists first, because someone coughDRUIDcough might've copied it into here while on the showDialog
                if (fileFinder.FileName != CrossPlatformOperations.CURRENTPATH + "/AM2R_11.zip")
                {
                    File.Copy(fileFinder.FileName, CrossPlatformOperations.CURRENTPATH + "/AM2R_11.zip");
                }

                log.Info("AM2R_11.zip successfully imported.");

                UpdateStateMachine();
                break;
                #endregion

                #region Install
            case PlayButtonState.Install:
                progressBar.Visible = true;
                progressBar.Value   = 0;
                SetPlayButtonState(PlayButtonState.Installing);

                // Make sure the main interface state machines properly
                UpdateApkState();
                UpdateProfileState();

                // If the file cannot be launched due to anti-virus shenanigans or any other reason, we try catch here
                try
                {
                    // Check if xdelta is installed on unix and exit if not
                    if (OS.IsUnix && !CrossPlatformOperations.CheckIfXdeltaIsInstalled())
                    {
                        MessageBox.Show(this, Text.XdeltaNotFound, Text.WarningWindowTitle, MessageBoxButtons.OK);

                        SetPlayButtonState(PlayButtonState.Install);
                        UpdateStateMachine();
                        log.Error("Xdelta not found. Aborting installing a profile...");
                        return;
                    }
                    var  progressIndicator = new Progress <int>(UpdateProgressBar);
                    bool useHqMusic        = hqMusicPCCheck.Checked.Value;
                    await Task.Run(() => Profile.InstallProfile(profileList[profileIndex.Value], useHqMusic, progressIndicator));

                    // This is just for visuals because the average windows end user will ask why it doesn't go to the end otherwise.
                    if (OS.IsWindows)
                    {
                        Thread.Sleep(500);
                    }
                }
                catch (Exception ex)
                {
                    log.Error(ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace);
                    MessageBox.Show(this, ex.Message + "\n*****Stack Trace*****\n\n" + ex.StackTrace, Text.ErrorWindowTitle, MessageBoxType.Error);
                }
                progressBar.Visible = false;
                progressBar.Value   = 0;

                // Just need to switch this to anything that isn't an "active" state so SetUpdateState() actually does something
                SetPlayButtonState(PlayButtonState.Play);
                UpdateStateMachine();
                break;
                #endregion

                #region Play
            case PlayButtonState.Play:

                if (!IsProfileIndexValid())
                {
                    return;
                }

                ProfileXML profile = profileList[profileIndex.Value];
                Visible = false;
                SetPlayButtonState(PlayButtonState.Playing);

                // Make sure the main interface state machines properly
                UpdateApkState();
                UpdateProfileState();

                this.ShowInTaskbar    = false;
                trayIndicator.Visible = true;
                WindowState windowStateBeforeLaunching = this.WindowState;
                Minimize();

                string envVarText      = customEnvVarTextBox?.Text;
                bool   createDebugLogs = profileDebugLogCheck.Checked.Value;

                await Task.Run(() => Profile.RunGame(profile, createDebugLogs, envVarText));

                this.ShowInTaskbar    = true;
                trayIndicator.Visible = false;
                Show();
                BringToFront();
                Visible     = true;
                WindowState = windowStateBeforeLaunching;

                SetPlayButtonState(PlayButtonState.Play);
                UpdateStateMachine();
                break;

                #endregion

            default: throw new NotImplementedException("Encountered invalid update state: " + updateState + "!");
            }
        }
Beispiel #14
0
    /// <summary>
    /// Creates an APK of the selected <paramref name="profile"/>.
    /// </summary>
    /// <param name="profile"><see cref="ProfileXML"/> to be compiled into an APK.</param>
    /// <param name="useHqMusic">Wether to create the APK with high quality music or not.</param>
    /// <param name="progress">Provides the current progress of this method.</param>
    public static void CreateAPK(ProfileXML profile, bool useHqMusic, IProgress <int> progress)
    {
        // Overall safety check just in case of bad situations
        if (!profile.SupportsAndroid)
        {
            progress.Report(100);
            return;
        }

        log.Info("Creating Android APK for profile " + profile.Name + ".");

        // Create working dir after some cleanup
        string apktoolPath = CrossPlatformOperations.CURRENTPATH + "/PatchData/utilities/android/apktool.jar",
               uberPath    = CrossPlatformOperations.CURRENTPATH + "/PatchData/utilities/android/uber-apk-signer.jar",
               tempDir     = new DirectoryInfo(CrossPlatformOperations.CURRENTPATH + "/temp").FullName,
               dataPath    = CrossPlatformOperations.CURRENTPATH + profile.DataPath;

        if (Directory.Exists(tempDir))
        {
            Directory.Delete(tempDir, true);
        }
        Directory.CreateDirectory(tempDir);

        log.Info("Cleanup, variables, and working directory created.");
        progress.Report(14);

        // Decompile AM2RWrapper.apk
        CrossPlatformOperations.RunJavaJar("\"" + apktoolPath + "\" d \"" + dataPath + "/android/AM2RWrapper.apk\"", tempDir);
        log.Info("AM2RWrapper decompiled.");
        progress.Report(28);

        // Add datafiles: 1.1, new datafiles, hq music, am2r.ini
        string workingDir = tempDir + "/AM2RWrapper/assets";

        ZipFile.ExtractToDirectory(CrossPlatformOperations.CURRENTPATH + "/AM2R_11.zip", workingDir);
        HelperMethods.DirectoryCopy(dataPath + "/files_to_copy", workingDir);

        if (useHqMusic)
        {
            HelperMethods.DirectoryCopy(CrossPlatformOperations.CURRENTPATH + "/PatchData/data/HDR_HQ_in-game_music", workingDir);
        }
        // Yes, I'm aware this is dumb. If you've got any better ideas for how to copy a seemingly randomly named .ini from this folder to the APK, please let me know.
        foreach (FileInfo file in new DirectoryInfo(dataPath).GetFiles().Where(f => f.Name.EndsWith("ini")))
        {
            File.Copy(file.FullName, workingDir + "/" + file.Name);
        }

        log.Info("AM2R_11.zip extracted and datafiles copied into AM2RWrapper.");
        progress.Report(42);

        // Patch data.win to game.droid
        CrossPlatformOperations.ApplyXdeltaPatch(workingDir + "/data.win", dataPath + "/droid.xdelta", workingDir + "/game.droid");
        log.Info("game.droid successfully patched.");
        progress.Report(56);

        // Delete unnecessary files
        File.Delete(workingDir + "/AM2R.exe");
        File.Delete(workingDir + "/D3DX9_43.dll");
        File.Delete(workingDir + "/explanations.txt");
        File.Delete(workingDir + "/modifiers.ini");
        File.Delete(workingDir + "/readme.txt");
        File.Delete(workingDir + "/data.win");
        Directory.Delete(workingDir + "/mods", true);
        Directory.Delete(workingDir + "/lang/headers", true);
        if (OS.IsLinux)
        {
            File.Delete(workingDir + "/icon.png");
        }

        // Modify apktool.yml to NOT compress ogg files
        string apktoolText = File.ReadAllText(workingDir + "/../apktool.yml");

        apktoolText = apktoolText.Replace("doNotCompress:", "doNotCompress:\n- ogg");
        File.WriteAllText(workingDir + "/../apktool.yml", apktoolText);
        log.Info("Unnecessary files removed, apktool.yml modified to prevent ogg compression.");
        progress.Report(70);

        // Rebuild APK
        CrossPlatformOperations.RunJavaJar("\"" + apktoolPath + "\" b AM2RWrapper -o \"" + profile.Name + ".apk\"", tempDir);
        log.Info("AM2RWrapper rebuilt into " + profile.Name + ".apk.");
        progress.Report(84);

        // Debug-sign APK
        CrossPlatformOperations.RunJavaJar("\"" + uberPath + "\" -a \"" + profile.Name + ".apk\"", tempDir);

        // Extra file cleanup
        File.Copy(tempDir + "/" + profile.Name + "-aligned-debugSigned.apk", CrossPlatformOperations.CURRENTPATH + "/" + profile.Name + ".apk", true);
        log.Info(profile.Name + ".apk signed and moved to " + CrossPlatformOperations.CURRENTPATH + "/" + profile.Name + ".apk.");
        HelperMethods.DeleteDirectory(tempDir);

        // Done
        progress.Report(100);
        log.Info("Successfully created Android APK for profile " + profile.Name + ".");
        CrossPlatformOperations.OpenFolderAndSelectFile(CrossPlatformOperations.CURRENTPATH + "/" + profile.Name + ".apk");
    }
Beispiel #15
0
 /// <summary>
 /// Gets called when <see cref="profileDebugLogCheck"/> gets clicked, and writes it's new value to the config.
 /// </summary>
 private void ProfileDebugLogCheckedChanged(object sender, EventArgs e)
 {
     log.Info("Create Game Debug Logs option has been set to " + profileDebugLogCheck.Checked + ".");
     CrossPlatformOperations.WriteToConfig("ProfileDebugLog", profileDebugLogCheck.Checked);
 }
Beispiel #16
0
        public MainForm()
        {
            // Exit if we're already running the AM2RLauncher
            // Thanks, StackOverflow! https://stackoverflow.com/questions/184084/how-to-force-c-sharp-net-app-to-run-only-one-instance-in-windows
            if (!singleInstance)
            {
                // If on Windows, set the original app to the foreground window to prevent confusion
                if (OS.IsWindows)
                {
                    Process current = Process.GetCurrentProcess();
                    Process process = Process.GetProcessesByName(current.ProcessName).First(p => p.Id == current.Id);
                    if (process != null)
                    {
                        Core.Core.SetForegroundWindow(process.MainWindowHandle);
                    }
                }
                Environment.Exit(0);
            }

            log.Info("Mutex check passed. Entering main thread.");
            log.Info("Current Launcher Version: " + VERSION);
            log.Info("Current Platform-ID is: " + Platform.ID);
            log.Info("Current OS is: " + OS.Name);

            // Set the Current Directory to the path the Launcher is located. Fixes some relative path issues.
            Environment.CurrentDirectory = CrossPlatformOperations.CURRENTPATH;
            log.Info("Set Launcher CWD to " + Environment.CurrentDirectory);

            // But log actual folder location nonetheless
            log.Info("Actual Launcher location: " + Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory));

            // Set the language to what User wanted or choose local language
            string userLanguage = CrossPlatformOperations.ReadFromConfig("Language").ToLower();

            if (!userLanguage.Equals("default"))
            {
                Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultures(CultureTypes.AllCultures).First(c => c.NativeName.ToLower().Contains(userLanguage));
            }

            log.Info("Language has been set to: " + Thread.CurrentThread.CurrentUICulture.EnglishName);

            #region VARIABLE INITIALIZATION
            log.Info("Beginning UI initialization...");

            Bitmap am2rIcon = new Bitmap(AM2RLauncher.Properties.Resources.AM2RIcon);

            // System tray indicator
            ButtonMenuItem showButton = new ButtonMenuItem {
                Text = Text.TrayButtonShow
            };
            trayIndicator = new TrayIndicator
            {
                Menu    = new ContextMenu(showButton),
                Title   = "AM2RLauncher",
                Visible = false,
                Image   = am2rIcon
            };

            // Create menubar with defaults for mac
            if (OS.IsMac)
            {
                Menu = new MenuBar();
            }

            // Create array from validCount
            profileList = new List <ProfileXML>();

            //TODO: whenever profileDropDown gets rewritten to use a datastore, scrap this
            profileNames = new List <ListItem>();
            foreach (var profile in profileList)
            {
                profileNames.Add(profile.Name);
            }

            // Custom splash texts
            string splash = Splash.GetSplash();
            log.Info("Randomly chosen splash: " + splash);

            Font smallButtonFont = new Font(SystemFont.Default, 10);

            // Create mirror list - eventually this should be platform specific!
            // We do this as a List<Uri> so we can add more dynamically on user input... if necessary.
            mirrorList = CrossPlatformOperations.GenerateMirrorList();

            // Create mirror list
            // We do this as a list<listItem> for 1) make this dynamic and 2) make ETO happy
            mirrorDescriptionList = new List <ListItem>();
            // Add each entry dynamically instead of harcoding it to two. If we have neither a github or gitlab mirror, we use the mirror itself as text
            foreach (var mirror in mirrorList)
            {
                string text = mirror;
                if (text.Contains("github.com"))
                {
                    text = Text.MirrorGithubText;
                }
                else if (text.Contains("gitlab.com"))
                {
                    text = Text.MirrorGitlabText;
                }
                mirrorDescriptionList.Add(new ListItem()
                {
                    Key = mirror, Text = text
                });
            }
            #endregion

            Icon        = new Icon(1f, am2rIcon);
            Title       = "AM2RLauncher " + VERSION + ": " + splash;
            MinimumSize = new Size(500, 400);
            // TODO: for some reason, this sometimes doesn't work on Linux. Was reported at eto, stays here until its fixed
            ClientSize = new Size(Int32.Parse(CrossPlatformOperations.ReadFromConfig("Width")), Int32.Parse(CrossPlatformOperations.ReadFromConfig("Height")));
            if (ClientSize.Width < 500)
            {
                ClientSize = new Size(500, ClientSize.Height);
            }
            if (ClientSize.Height < 400)
            {
                ClientSize = new Size(ClientSize.Width, 400);
            }
            log.Info("Start the launcher with Size: " + ClientSize.Width + ", " + ClientSize.Height);
            if (Boolean.Parse(CrossPlatformOperations.ReadFromConfig("IsMaximized")))
            {
                Maximize();
            }

            drawable = new Drawable {
                BackgroundColor = colBGNoAlpha
            };

            // Drawable paint event
            drawable.Paint += DrawablePaintEvent;
            // Some systems don't call the paintEvent by default and only do so after actual resizing
            if (OS.IsMac)
            {
                LoadComplete += (sender, e) => { Size = new Size(Size.Width + 1, Size.Height); Size = new Size(Size.Width - 1, Size.Height); }
            }
            ;

            #region MAIN WINDOW

            // Center buttons/interface panel
            var centerInterface = new DynamicLayout();

            // PLAY button
            playButton = new ColorButton
            {
                ToolTip = "",
                BackgroundColorHover = colBGHover,
                Height             = 40,
                Width              = 250,
                TextColor          = colGreen,
                TextColorDisabled  = colInactive,
                BackgroundColor    = colBG,
                FrameColor         = colGreen,
                FrameColorDisabled = colInactive
            };

            UpdateStateMachine();

            SetPlayButtonState(updateState);

            centerInterface.AddRow(playButton);

            // 2px spacer between playButton and apkButton (Windows only)
            if (OS.IsWindows)
            {
                centerInterface.AddRow(new Label {
                    BackgroundColor = colBG, Height = 2
                });
            }

            // APK button
            apkButton = new ColorButton
            {
                Text                 = Text.CreateAPK,
                Height               = 40,
                Width                = 250,
                TextColor            = colGreen,
                BackgroundColor      = colBG,
                FrameColor           = colGreen,
                BackgroundColorHover = colBGHover
            };

            centerInterface.AddRow(apkButton);

            progressBar = new ProgressBar
            {
                Visible = false,
                Height  = 15
            };

            // 4px spacer between APK button and progressBar (Windows only)
            if (OS.IsWindows)
            {
                centerInterface.AddRow(new Label {
                    BackgroundColor = colBG, Height = 4
                });
            }

            centerInterface.AddRow(progressBar);

            progressLabel = new Label
            {
                BackgroundColor = colBG,
                Height          = 15,
                Text            = "",
                TextColor       = colGreen,
                Visible         = false
            };

            centerInterface.AddRow(progressLabel);

            // 3px spacer between progressBar and profile label (Windows only)
            if (OS.IsWindows)
            {
                centerInterface.AddRow(new Label {
                    BackgroundColor = colBG, Height = 3
                });
            }

            profileLabel = new Label
            {
                BackgroundColor = colBG,
                Height          = 15,
                Text            = Text.CurrentProfile,
                TextColor       = colGreen
            };

            centerInterface.AddRow(profileLabel);

            // Profiles dropdown

            // Yes, we know this looks horrific on GTK. Sorry.
            // We're not exactly in a position to rewrite the entire DropDown object as a Drawable child, but if you want to, you're more than welcome!
            // Mac gets a default BackgroundColor because it looks waaaaaaay better.
            profileDropDown = new DropDown
            {
                TextColor       = colGreen,
                BackgroundColor = OS.IsWindows ? colBGNoAlpha : new Color()
            };
            // In order to not have conflicting theming, we just always respect the users theme for dropdown on GTK.
            if (OS.IsLinux)
            {
                profileDropDown = new DropDown();
            }

            profileDropDown.Items.AddRange(profileNames);   // It's actually more comfortable if it's outside, because of GTK shenanigans

            centerInterface.AddRow(profileDropDown);

            // Profiles label
            profileAuthorLabel = new Label
            {
                BackgroundColor = colBG,
                Height          = 16,
                Text            = Text.Author + " ",
                TextColor       = colGreen
            };

            centerInterface.AddRow(profileAuthorLabel);

            profileVersionLabel = new Label
            {
                BackgroundColor = colBG,
                Height          = 16,
                Text            = Text.VersionLabel + " ",
                TextColor       = colGreen
            };

            centerInterface.AddRow(profileVersionLabel);

            saveWarningLabel = new Label
            {
                Visible         = false,
                BackgroundColor = colBG,
                Width           = 20,
                Height          = 55,
                Text            = Text.SaveLocationWarning,
                TextColor       = colRed
            };

            centerInterface.AddRow(saveWarningLabel);


            // Social buttons
            Bitmap redditIcon   = new Bitmap(Resources.redditIcon48);
            var    redditButton = new ImageButton {
                ToolTip = Text.RedditToolTip, Image = redditIcon
            };
            redditButton.Click += (sender, e) => CrossPlatformOperations.OpenURL("https://www.reddit.com/r/AM2R");

            Bitmap githubIcon   = new Bitmap(Resources.githubIcon48);
            var    githubButton = new ImageButton {
                ToolTip = Text.GithubToolTip, Image = githubIcon
            };
            githubButton.Click += (sender, e) => CrossPlatformOperations.OpenURL("https://www.github.com/AM2R-Community-Developers");

            Bitmap youtubeIcon   = new Bitmap(Resources.youtubeIcon48);
            var    youtubeButton = new ImageButton {
                ToolTip = Text.YoutubeToolTip, Image = youtubeIcon
            };
            youtubeButton.Click += (sender, e) => CrossPlatformOperations.OpenURL("https://www.youtube.com/c/AM2RCommunityUpdates");

            Bitmap discordIcon   = new Bitmap(Resources.discordIcon48);
            var    discordButton = new ImageButton {
                ToolTip = Text.DiscordToolTip, Image = discordIcon
            };
            discordButton.Click += (sender, e) => CrossPlatformOperations.OpenURL("https://discord.gg/nk7UYPbd5u");


            // Social button panel
            var socialPanel = new DynamicLayout();
            socialPanel.BeginVertical();
            socialPanel.AddRow(redditButton);
            socialPanel.AddRow(githubButton);
            socialPanel.AddRow(youtubeButton);
            socialPanel.AddRow(discordButton);
            socialPanel.EndVertical();


            // Version number label
            Label versionLabel = new Label
            {
                Text  = "v" + VERSION + (isThisRunningFromWine ? "-WINE" : ""),
                Width = 48, TextAlignment = TextAlignment.Right, TextColor = colGreen,
                Font  = new Font(SystemFont.Default, 12)
            };

            // Tie everything together
            var mainLayout = new DynamicLayout();

            mainLayout.BeginHorizontal();
            mainLayout.AddColumn(null, socialPanel);

            mainLayout.AddSpace();
            mainLayout.AddColumn(null, centerInterface, null);
            mainLayout.AddSpace();

            // Yes, I'm hardcoding this string. Linux users can english.
            mainLayout.AddColumn(versionLabel, isThisRunningFromWine ? new Label {
                Text = "Unsupported", TextColor = colRed, TextAlignment = TextAlignment.Right
            } : null);

            drawable.Content = mainLayout;

            #endregion

            #region TABS

            #region MAIN PAGE
            // [MAIN PAGE]
            TabPage mainPage = new TabPage
            {
                BackgroundColor = colBGNoAlpha,
                Text            = Text.PlayTab,
                Content         = drawable
            };
            #endregion

            #region CHANGELOG PAGE
            // [CHANGELOG]
            Uri     changelogUri     = new Uri("https://am2r-community-developers.github.io/DistributionCenter/changelog.html");
            WebView changelogWebView = new WebView {
                Url = changelogUri
            };

            if (OS.IsUnix && !isInternetThere)
            {
                changelogWebView = new WebView();
            }

            Label changelogNoConnectionLabel = new Label
            {
                Text      = Text.NoInternetConnection,
                TextColor = colGreen
            };

            TabPage changelogPage = new TabPage
            {
                BackgroundColor = colBGNoAlpha,
                Text            = Text.ChangelogTab,

                Content = new TableLayout
                {
                    Rows =
                    {
                        changelogWebView
                    }
                }
            };

            #endregion

            #region NEWS PAGE

            // [NEWS]
            Uri     newsUri     = new Uri("https://am2r-community-developers.github.io/DistributionCenter/news.html");
            WebView newsWebView = new WebView {
                Url = newsUri
            };

            //TODO: why exactly is this check necessary?
            if (OS.IsUnix && !isInternetThere)
            {
                newsWebView = new WebView();
            }

            Label newsNoConnectionLabel = new Label
            {
                Text      = Text.NoInternetConnection,
                TextColor = colGreen
            };

            TabPage newsPage = new TabPage
            {
                Text            = Text.NewsTab,
                BackgroundColor = colBGNoAlpha,

                Content = new TableLayout
                {
                    Rows =
                    {
                        newsWebView
                    }
                }
            };

            //TODO: this is hack because on linux / mac the other way doesn't work. eto issue?
            if (OS.IsUnix && !isInternetThere)
            {
                changelogPage.Content = new TableLayout
                {
                    Rows =
                    {
                        null,
                        changelogNoConnectionLabel,
                        null
                    }
                };
                newsPage.Content = new TableLayout
                {
                    Rows =
                    {
                        null,
                        newsNoConnectionLabel,
                        null
                    }
                };
            }

            #endregion

            #region SETTINGS PAGE

            // [LAUNCHER SETTINGS]
            DynamicLayout settingsLayout = new DynamicLayout();

            // LanguageLabel
            Label languageLabel = new Label
            {
                Text      = Text.LanguageNotice,
                TextColor = colGreen
            };

            // Language DropDown menu

            List <ListItem> languageList = new List <ListItem>
            {
                Text.SystemLanguage,
                "Deutsch",
                "English",
                "Español",
                "Français",
                "Italiano",
                "Português",
                "Русский",
                "日本語",
                "中文(简体)"
            };

            languageDropDown = new DropDown
            {
                TextColor       = colGreen,
                BackgroundColor = OS.IsWindows ? colBGNoAlpha : new Color()
            };
            if (OS.IsLinux)
            {
                languageDropDown = new DropDown();
            }

            languageDropDown.Items.AddRange(languageList);

            var tmpLanguage = CrossPlatformOperations.ReadFromConfig("Language");
            languageDropDown.SelectedIndex = tmpLanguage == "Default" ? 0 : languageDropDown.Items.IndexOf(languageDropDown.Items.FirstOrDefault(x => x.Text.Equals(tmpLanguage)));

            if (languageDropDown.SelectedIndex == -1)
            {
                log.Info("User has tried to use " + tmpLanguage + " as a Language, but it was not found. Reverting to System Language");
                languageDropDown.SelectedIndex = 0;
            }

            // autoUpdateAM2R checkbox
            autoUpdateAM2RCheck = new CheckBox
            {
                Checked   = Boolean.Parse(CrossPlatformOperations.ReadFromConfig("AutoUpdateAM2R")),
                Text      = Text.AutoUpdateAM2R,
                TextColor = colGreen
            };


            // autoUpdateLauncher checkbox
            autoUpdateLauncherCheck = new CheckBox
            {
                Checked   = Boolean.Parse(CrossPlatformOperations.ReadFromConfig("AutoUpdateLauncher")),
                Text      = Text.AutoUpdateLauncher,
                TextColor = colGreen
            };

            // HQ music, PC
            hqMusicPCCheck = new CheckBox
            {
                Checked   = Boolean.Parse(CrossPlatformOperations.ReadFromConfig("MusicHQPC")),
                Text      = Text.HighQualityPC,
                TextColor = colGreen
            };

            // HQ music, Android
            hqMusicAndroidCheck = new CheckBox
            {
                Checked   = Boolean.Parse(CrossPlatformOperations.ReadFromConfig("MusicHQAndroid")),
                Text      = Text.HighQualityAndroid,
                TextColor = colGreen
            };

            // Create game debug logs
            profileDebugLogCheck = new CheckBox
            {
                Checked   = bool.Parse(CrossPlatformOperations.ReadFromConfig("ProfileDebugLog")),
                Text      = Text.ProfileDebugCheckBox,
                TextColor = colGreen
            };

            // Custom environment variables label
            Label customEnvVarLabel = new Label();
            if (OS.IsLinux)
            {
                customEnvVarLabel = new Label
                {
                    Text      = Text.CustomEnvVarLabel,
                    TextColor = colGreen
                };
            }

            // Custom environment variables textbox
            customEnvVarTextBox = null;
            if (OS.IsLinux)
            {
                customEnvVarTextBox = new TextBox
                {
                    Text            = CrossPlatformOperations.ReadFromConfig("CustomEnvVar"),
                    BackgroundColor = colBGNoAlpha,
                    TextColor       = colGreen
                };
            }

            // Mirror list
            mirrorLabel = new Label
            {
                Text      = Text.DownloadSource,
                TextColor = colGreen
            };

            mirrorDropDown = new DropDown
            {
                TextColor       = colGreen,
                BackgroundColor = OS.IsWindows ? colBGNoAlpha : new Color()
            };
            if (OS.IsLinux)
            {
                mirrorDropDown = new DropDown();
            }

            mirrorDropDown.Items.AddRange(mirrorDescriptionList);   // As above, find a way to get this inside the dropDown definition
            mirrorIndex = (Int32.Parse(CrossPlatformOperations.ReadFromConfig("MirrorIndex")) < mirrorDropDown.Items.Count) ? Int32.Parse(CrossPlatformOperations.ReadFromConfig("MirrorIndex"))
                                                                                                                            : 0;
            mirrorDropDown.SelectedIndex = mirrorIndex;

            currentMirror = mirrorList[mirrorDropDown.SelectedIndex];

            // Custom mirror
            customMirrorCheck = new CheckBox
            {
                Checked   = Boolean.Parse(CrossPlatformOperations.ReadFromConfig("CustomMirrorEnabled")),
                Text      = Text.CustomMirrorCheck,
                TextColor = colGreen
            };

            customMirrorTextBox = new TextBox
            {
                Text            = CrossPlatformOperations.ReadFromConfig("CustomMirrorText"),
                BackgroundColor = colBGNoAlpha,
                TextColor       = colGreen
            };

            EnableMirrorControlsAccordingly();

            settingsLayout.BeginHorizontal();
            settingsLayout.AddSpace();
            settingsLayout.AddColumn(null, languageLabel, languageDropDown, autoUpdateAM2RCheck, autoUpdateLauncherCheck, hqMusicPCCheck, hqMusicAndroidCheck, profileDebugLogCheck, customEnvVarLabel, (Control)customEnvVarTextBox ?? new Label(), mirrorLabel, mirrorDropDown, customMirrorCheck, customMirrorTextBox, null);
            settingsLayout.AddSpace();

            TabPage settingsPage = new TabPage
            {
                BackgroundColor = colBGNoAlpha,
                Content         = settingsLayout,
                Text            = Text.LauncherSettingsTab
            };

            #endregion

            #region MODSETTINGS PAGE

            // [MOD SETTINGS]
            DynamicLayout modSettingsLayout = new DynamicLayout();


            addModButton = new ColorButton
            {
                ToolTip              = null,
                Text                 = Text.AddNewMod,
                Font                 = smallButtonFont,
                Height               = 30,
                Width                = 275,
                TextColor            = colGreen,
                BackgroundColor      = colBG,
                FrameColor           = colGreen,
                BackgroundColorHover = colBGHover
            };

            Label modSpacer = new Label
            {
                Height = 14
            };

            settingsProfileLabel = new Label
            {
                Text      = Text.CurrentProfile,
                TextColor = colGreen,
                Width     = 275
            };

            modSettingsProfileDropDown = new DropDown
            {
                TextColor       = colGreen,
                BackgroundColor = OS.IsWindows ? colBGNoAlpha : new Color()
            };

            // In order to not have conflicting theming, we just always respect the users theme for dropdown on GTK.
            if (OS.IsLinux)
            {
                modSettingsProfileDropDown = new DropDown();
            }

            modSettingsProfileDropDown.Items.AddRange(profileNames);   // It's actually more comfortable if it's outside, because of GTK shenanigans

            profileButton = new ColorButton
            {
                ToolTip              = null,
                Text                 = Text.OpenProfileFolder,
                Font                 = smallButtonFont,
                Height               = 30,
                Width                = 275,
                TextColor            = colGreen,
                BackgroundColor      = colBG,
                FrameColor           = colGreen,
                BackgroundColorHover = colBGHover
            };

            saveButton = new ColorButton
            {
                ToolTip              = null,
                Text                 = Text.OpenSaveFolder,
                Font                 = smallButtonFont,
                Height               = 30,
                Width                = 275,
                TextColor            = colGreen,
                BackgroundColor      = colBG,
                FrameColor           = colGreen,
                BackgroundColorHover = colBGHover
            };

            updateModButton = new ColorButton
            {
                ToolTip              = null,
                Text                 = Text.UpdateModButtonText,
                Font                 = smallButtonFont,
                Height               = 30,
                Width                = 275,
                TextColor            = colGreen,
                BackgroundColor      = colBG,
                FrameColor           = colGreen,
                BackgroundColorHover = colBGHover
            };

            deleteModButton = new ColorButton
            {
                ToolTip              = null,
                Text                 = Text.DeleteModButtonText,
                Font                 = smallButtonFont,
                Height               = 30,
                Width                = 275,
                TextColor            = colGreen,
                BackgroundColor      = colBG,
                FrameColor           = colGreen,
                BackgroundColorHover = colBGHover
            };

            profileNotesTextArea = new TextArea
            {
                ReadOnly        = true,
                BackgroundColor = colBGNoAlpha,
                TextColor       = colInactive,
                SpellCheck      = false,
                Width           = 275,
                Height          = 150,
                Text            = Text.ProfileNotes
            };

            modSettingsLayout.BeginHorizontal();
            modSettingsLayout.AddSpace();
            modSettingsLayout.AddColumn(null, addModButton, modSpacer, settingsProfileLabel, modSettingsProfileDropDown, profileButton, saveButton, updateModButton, deleteModButton, profileNotesTextArea, null);
            modSettingsLayout.AddSpace();

            TabPage modSettingsPage = new TabPage
            {
                BackgroundColor = colBGNoAlpha,
                Content         = modSettingsLayout,
                Text            = Text.ModSettingsTab
            };

            #endregion

            #endregion

            Content = new TabControl
            {
                Pages =
                {
                    mainPage,

                    changelogPage,

                    newsPage,

                    settingsPage,

                    modSettingsPage
                }
            };

            #region EVENTS
            log.Info("All UI objects have been initialized, UI has been set up.");
            log.Info("Beginning event linkage...");

            Closing          += MainformClosing;
            showButton.Click += ShowButtonClick;
            profileDropDown.SelectedIndexChanged   += ProfileDropDownSelectedIndexChanged;
            languageDropDown.SelectedIndexChanged  += LanguageDropDownSelectedIndexChanged;
            autoUpdateAM2RCheck.CheckedChanged     += AutoUpdateAM2RCheckChanged;
            autoUpdateLauncherCheck.CheckedChanged += AutoUpdateLauncherCheckChanged;
            hqMusicAndroidCheck.CheckedChanged     += HqMusicAndroidCheckChanged;
            hqMusicPCCheck.CheckedChanged          += HqMusicPCCheckChanged;
            customMirrorCheck.CheckedChanged       += CustomMirrorCheckChanged;
            apkButton.Click                                 += ApkButtonClickEvent;
            apkButton.LoadComplete                          += (sender, e) => UpdateApkState();
            profileDropDown.LoadComplete                    += (sender, e) => UpdateProfileState();
            playButton.Click                                += PlayButtonClickEvent;
            playButton.LoadComplete                         += PlayButtonLoadComplete;
            customMirrorTextBox.LostFocus                   += CustomMirrorTextBoxLostFocus;
            mirrorDropDown.SelectedIndexChanged             += MirrorDropDownSelectedIndexChanged;
            modSettingsLayout.LoadComplete                  += ProfileLayoutLoadComplete;
            addModButton.Click                              += AddModButtonClicked;
            profileButton.Click                             += ProfileDataButtonClickEvent;
            saveButton.Click                                += SaveButtonClickEvent;
            modSettingsProfileDropDown.SelectedIndexChanged += ModSettingsProfileDropDownSelectedIndexChanged;
            deleteModButton.Click                           += DeleteModButtonClicked;
            updateModButton.Click                           += UpdateModButtonClicked;
            profileDebugLogCheck.CheckedChanged             += ProfileDebugLogCheckedChanged;
            if (OS.IsLinux)
            {
                customEnvVarTextBox.LostFocus += CustomEnvVarTextBoxLostFocus;
            }

            //TODO: Retest if these now work on mac
            newsWebView.DocumentLoaded      += (sender, e) => ChangeToEmptyPageOnNoInternet(newsPage, newsNoConnectionLabel);
            changelogWebView.DocumentLoaded += (sender, e) => ChangeToEmptyPageOnNoInternet(changelogPage, changelogNoConnectionLabel);

            log.Info("Events linked successfully.");

            #endregion
        }
Beispiel #17
0
        /// <summary>
        /// Performs the entire AM2RLauncher update procedure.
        /// </summary>
        public static void Main()
        {
            log.Info("Running update check...");

            // Update section

            // Clean old files that have been left
            if (File.Exists(CrossPlatformOperations.CURRENTPATH + "/AM2RLauncher.bak"))
            {
                log.Info("AM2RLauncher.bak detected. Removing file.");
                File.Delete(CrossPlatformOperations.CURRENTPATH + "/AM2RLauncher.bak");
            }
            if (OS.IsWindows && File.Exists(oldConfigPath))
            {
                log.Info(CrossPlatformOperations.LAUNCHERNAME + ".oldCfg detected. Removing file.");
                File.Delete(oldConfigPath);
            }
            if (OS.IsWindows && Directory.Exists(CrossPlatformOperations.CURRENTPATH + "/oldLib"))
            {
                log.Info("Old lib folder detected, removing folder.");
                Directory.Delete(CrossPlatformOperations.CURRENTPATH + "/oldLib", true);
            }

            // Clean up old update libs
            if (OS.IsWindows && Directory.Exists(CrossPlatformOperations.CURRENTPATH + "/lib"))
            {
                foreach (FileInfo file in new DirectoryInfo(CrossPlatformOperations.CURRENTPATH + "/lib").GetFiles())
                {
                    if (file.Name.EndsWith(".bak"))
                    {
                        file.Delete();
                    }
                }

                // Do the same for each subdir
                foreach (DirectoryInfo dir in new DirectoryInfo(CrossPlatformOperations.CURRENTPATH + "/lib").GetDirectories())
                {
                    foreach (FileInfo file in dir.GetFiles())
                    {
                        if (file.Name.EndsWith(".bak"))
                        {
                            file.Delete();
                        }
                    }
                }
            }

            // Check settings if autoUpdateLauncher is set to true
            bool autoUpdate = Boolean.Parse(CrossPlatformOperations.ReadFromConfig("AutoUpdateLauncher"));

            if (autoUpdate)
            {
                log.Info("AutoUpdate Launcher set to true!");

                // This is supposed to fix the updater throwing an exception on windows 7 and earlier(?)
                // See this for information: https://stackoverflow.com/q/2859790 and https://stackoverflow.com/a/50977774
                if (OS.IsWindows)
                {
                    ServicePointManager.Expect100Continue = true;
                    ServicePointManager.SecurityProtocol  = SecurityProtocolType.Tls12;
                }

                HttpWebRequest  request = (HttpWebRequest)WebRequest.Create("https://github.com/AM2R-Community-Developers/AM2RLauncher/releases/latest");
                HttpWebResponse response;
                try
                {
                    response = (HttpWebResponse)request.GetResponse();
                }
                catch (WebException)
                {
                    log.Error("WebException caught! Displaying MessageBox.");
                    MessageBox.Show(Language.Text.NoInternetConnection);
                    return;
                }

                Uri    realUri                  = response.ResponseUri;
                string onlineVersion            = realUri.AbsoluteUri.Substring(realUri.AbsoluteUri.LastIndexOf('/') + 1);
                bool   isCurrentVersionOutdated = false;

                string[] localVersionArray  = VERSION.Split('.');
                string[] onlineVersionArray = onlineVersion.Split('.');

                for (int i = 0; i < localVersionArray.Length; i++)
                {
                    int onlineNum = Int32.Parse(onlineVersionArray[i]);
                    int localNum  = Int32.Parse(localVersionArray[i]);
                    if (onlineNum > localNum)
                    {
                        isCurrentVersionOutdated = true;
                        break;
                    }
                    if (localNum > onlineNum)
                    {
                        break;
                    }
                }

                log.Info((isCurrentVersionOutdated ? "Updating" : "Not Updating") + " from " + VERSION + " to " + onlineVersion);

                // No new update, exiting
                if (!isCurrentVersionOutdated)
                {
                    return;
                }

                // For mac, we just show a message box that a new version is available, because I don't want to support it yet.
                // hardcoded string, since also temporarily until it gets supported one day.
                if (OS.IsMac)
                {
                    MessageBox.Show("Your current version is outdated! The newest version is " + onlineVersion + "." +
                                    "Please recompile AM2RLauncher again or disable auto-updating");
                    return;
                }

                log.Info("Current version (" + VERSION + ") is outdated! Initiating update for version " + onlineVersion + ".");

                string tmpUpdatePath = CrossPlatformOperations.CURRENTPATH + "/tmpupdate/";
                string zipPath       = CrossPlatformOperations.CURRENTPATH + "/launcher.zip";

                // Clean tmpupdate
                if (Directory.Exists(tmpUpdatePath))
                {
                    Directory.Delete(tmpUpdatePath, true);
                }
                if (!Directory.Exists(tmpUpdatePath))
                {
                    Directory.CreateDirectory(tmpUpdatePath);
                }

                try
                {
                    using (var client = new WebClient())
                    {
                        string platformSuffix = "";
                        if (OS.IsWindows)
                        {
                            platformSuffix = "_win";
                        }
                        else if (OS.IsLinux)
                        {
                            platformSuffix = "_lin";
                        }

                        log.Info("Downloading https://github.com/AM2R-Community-Developers/AM2RLauncher/releases/latest/download/AM2RLauncher_" + onlineVersion + platformSuffix + ".zip to " + zipPath + ".");

                        client.DownloadFile("https://github.com/AM2R-Community-Developers/AM2RLauncher/releases/latest/download/AM2RLauncher_" + onlineVersion + platformSuffix + ".zip", zipPath);

                        log.Info("File successfully downloaded.");
                    }
                }
                catch (UnauthorizedAccessException)
                {
                    log.Error("UnauthorizedAccessException caught! Displaying MessageBox.");
                    MessageBox.Show(Language.Text.UnauthorizedAccessMessage);
                    return;
                }

                ZipFile.ExtractToDirectory(zipPath, tmpUpdatePath);
                log.Info("Updates successfully extracted to " + tmpUpdatePath);

                File.Delete(zipPath);
                File.Move(updatePath + "/" + CrossPlatformOperations.LAUNCHERNAME, CrossPlatformOperations.CURRENTPATH + "/AM2RLauncher.bak");
                if (OS.IsWindows)
                {
                    File.Move(CrossPlatformOperations.LAUNCHERNAME + ".config", CrossPlatformOperations.LAUNCHERNAME + ".oldCfg");
                }

                foreach (var file in new DirectoryInfo(tmpUpdatePath).GetFiles())
                {
                    log.Info("Moving " + file.FullName + " to " + CrossPlatformOperations.CURRENTPATH + "/" + file.Name);
                    File.Copy(file.FullName, updatePath + "/" + file.Name, true);
                }
                // For windows, the actual application is in "AM2RLauncher.dll". Which means, we need to update the lib folder as well.
                if (OS.IsWindows && Directory.Exists(CrossPlatformOperations.CURRENTPATH + "/lib"))
                {
                    // So, because Windows behavior is dumb...

                    // Rename all files in lib to *.bak
                    foreach (FileInfo file in new DirectoryInfo(CrossPlatformOperations.CURRENTPATH + "/lib").GetFiles())
                    {
                        file.CopyTo(file.Directory + "/" + file.Name + ".bak");
                    }

                    // Do the same for each subdir
                    foreach (DirectoryInfo dir in new DirectoryInfo(CrossPlatformOperations.CURRENTPATH + "/lib").GetDirectories())
                    {
                        foreach (FileInfo file in dir.GetFiles())
                        {
                            file.CopyTo(file.Directory + "/" + file.Name + ".bak");
                        }
                    }

                    // Yes, the above calls could be recursive. No, I can't be bothered to make them as such.
                    if (Directory.Exists(tmpUpdatePath + "lib"))
                    {
                        HelperMethods.DirectoryCopy(tmpUpdatePath + "lib", CrossPlatformOperations.CURRENTPATH + "/lib");
                    }
                }

                Directory.Delete(tmpUpdatePath, true);

                CrossPlatformOperations.CopyOldConfigToNewConfig();

                log.Info("Files extracted. Preparing to restart executable...");
                if (OS.IsLinux)
                {
                    System.Diagnostics.Process.Start("chmod", "+x " + updatePath + "./AM2RLauncher.Gtk");
                }

                System.Diagnostics.Process.Start(updatePath + "/" + CrossPlatformOperations.LAUNCHERNAME);
                Environment.Exit(0);
            }
            else
            {
                log.Info("AutoUpdate Launcher set to false. Exiting update check.");
            }
        }