Пример #1
0
    /// <summary>
    /// Deletes a profile from the Mods and Profiles folder.
    /// </summary>
    /// <param name="profile">The profile to delete.</param>
    public static void DeleteProfile(ProfileXML profile)
    {
        log.Info("Attempting to delete profile " + profile.Name + "...");

        // Delete folder in Mods
        if (Directory.Exists(CrossPlatformOperations.CURRENTPATH + profile.DataPath))
        {
            HelperMethods.DeleteDirectory(CrossPlatformOperations.CURRENTPATH + profile.DataPath);
        }

        // Delete the zip file in Mods
        if (File.Exists(CrossPlatformOperations.CURRENTPATH + profile.DataPath + ".zip"))
        {
            // For some reason, it was set at read only, so we undo that here
            File.SetAttributes(CrossPlatformOperations.CURRENTPATH + profile.DataPath + ".zip", FileAttributes.Normal);
            File.Delete(CrossPlatformOperations.CURRENTPATH + profile.DataPath + ".zip");
        }

        // Delete folder in Profiles
        if (Directory.Exists(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name))
        {
            HelperMethods.DeleteDirectory(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name);
        }

        log.Info("Successfully deleted profile " + profile.Name + ".");
    }
Пример #2
0
        internal static ProfileXML ToProfileXML(this Profile profile)
        {
            ProfileXML profileXML = new ProfileXML()
            {
                Description         = profile.Description,
                Email               = profile.Email,
                Fonts               = profile.Fonts,
                Name                = profile.Name,
                OperatingSystem     = profile.OperatingSystem,
                CPU                 = profile.CPU,
                Battery             = profile.Battery,
                EnableAudioApi      = profile.EnableAudioApi,
                EnablePlugins       = profile.EnablePlugins,
                EnableMediaPlugins  = profile.EnableMediaPlugins,
                RandomTimersEnabled = profile.RandomTimersEnabled,
                UserAgent           = profile.UserAgent,
                Screen              = profile.Screen,
                HistoryLength       = profile.HistoryLength,
                WebGL               = profile.WebGL,
                FakeClientRects     = profile.FakeClientRects,
                Canvas              = profile.Canvas,
                EnableNetwork       = profile.EnableNetwork,
                Language            = profile.Language,
                GeoIpEnabled        = profile.GeoIpEnabled,
                ByPassProxySites    = profile.ByPassProxySites,
                Proxies             = profile.Proxies,
            };

            return(profileXML);
        }
Пример #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);
        }
Пример #4
0
    /// <summary>
    /// Archives a given Profile by making a copy with "Name (version)". Does silently nothing if user archives already exist
    /// </summary>
    /// <param name="profile">The profile to archive</param>
    public static void ArchiveProfile(ProfileXML profile)
    {
        // temporarily serialize and deserialize to essentially "clone" the variable as otherwise we'd modify references
        File.WriteAllText(Path.GetTempPath() + "/" + profile.Name, Serializer.Serialize <ProfileXML>(profile));
        profile = Serializer.Deserialize <ProfileXML>(File.ReadAllText(Path.GetTempPath() + "/" + profile.Name));

        string originalName = profile.Name;

        // Change name to include version and be unique
        profile.Name += " (" + profile.Version + ")";
        // if we're archiving community updates, remove the "latest" part
        profile.Name = profile.Name.Replace("Community Updates Latest", "Community Updates");

        log.Info("Archiving " + profile.Name);

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

        // Do NOT overwrite if a path with this name already exists! It is likely an existing user archive.
        if (!Directory.Exists(profileArchivePath))
        {
            // Rename current profile if we have it installed
            if (Directory.Exists(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + originalName))
            {
                Directory.Move(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + originalName, profileArchivePath);
            }

            // Set as non-installable so that it's just treated as a launching reference
            profile.Installable = false;

            string modArchivePath = CrossPlatformOperations.CURRENTPATH + "/Mods/" + profile.Name;

            // Do NOT overwrite if a path with this name already exists! It is likely an existing user archive.
            if (!Directory.Exists(modArchivePath))
            {
                Directory.CreateDirectory(modArchivePath);
                File.WriteAllText(modArchivePath + "/profile.xml", Serializer.Serialize <ProfileXML>(profile));
                log.Info("Finished archival.");
            }
            else
            {
                HelperMethods.DeleteDirectory(profileArchivePath);
                log.Info("Cancelling archival! User-defined archive in Mods already exists.");
            }
        }
        // If our desired rename already exists, it's probably a user archive... so we just delete the original folder and move on with installation of the new version.
        else
        {
            HelperMethods.DeleteDirectory(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + originalName);
            log.Info("Cancelling archival! User-defined archive in Profiles already exists.");
        }
    }
Пример #5
0
    /// <summary>
    /// Scans the PatchData and Mods folders for valid profile entries, creates and returns a list of them.
    /// </summary>
    /// <returns>A <see cref="List{ProfileXML}"/> containing all valid profile entries.</returns>
    public static List <ProfileXML> LoadProfiles()
    {
        log.Info("Loading profiles...");

        List <ProfileXML> profileList = new List <ProfileXML>();

        // Check for and add the Community Updates profile
        if (File.Exists(CrossPlatformOperations.CURRENTPATH + "/PatchData/profile.xml"))
        {
            ProfileXML profile = Serializer.Deserialize <ProfileXML>(File.ReadAllText(CrossPlatformOperations.CURRENTPATH + "/PatchData/profile.xml"));
            profile.DataPath = "/PatchData/data";
            profileList.Add(profile);
        }

        // Safety check to generate the Mods folder if it does not exist
        if (!Directory.Exists(CrossPlatformOperations.CURRENTPATH + "/Mods"))
        {
            Directory.CreateDirectory(CrossPlatformOperations.CURRENTPATH + "/Mods");
        }

        // Get Mods folder info
        DirectoryInfo modsDir = new DirectoryInfo(CrossPlatformOperations.CURRENTPATH + "/Mods");

        // Add all extracted profiles in Mods to the profileList.
        foreach (DirectoryInfo dir in modsDir.GetDirectories())
        {
            // If no profile.xml exists we don't add anything
            if (!File.Exists(dir.FullName + "/profile.xml"))
            {
                continue;
            }

            ProfileXML prof = Serializer.Deserialize <ProfileXML>(File.ReadAllText(dir.FullName + "/profile.xml"));
            // Safety check for non-installable profiles
            if (prof.Installable || IsProfileInstalled(prof))
            {
                prof.DataPath = "/Mods/" + dir.Name;
                profileList.Add(prof);
            }
            // If not installable and isn't installed, remove it
            else if (!IsProfileInstalled(prof))
            {
                prof.DataPath = "/Mods/" + dir.Name;
                DeleteProfile(prof);
            }
        }

        log.Info("Loaded " + profileList.Count + " profile(s).");
        return(profileList);
    }
Пример #6
0
 private void UpdateAutomationProfile(ProfileXML savedProfile)
 {
     try
     {
         profile.Description             = savedProfile.Description;
         profile.Email                   = savedProfile.Email;
         profile.Fonts                   = savedProfile.Fonts;
         profile.Name                    = savedProfile.Name;
         profile.OperatingSystem         = savedProfile.OperatingSystem;
         profile.CPU.DeviceMemory        = savedProfile.CPU.DeviceMemory;
         profile.CPU.HardwareConcurrency = savedProfile.CPU.HardwareConcurrency;
         profile.Battery.Charging        = savedProfile.Battery.Charging;
         profile.Battery.ChargingTime    = savedProfile.Battery.ChargingTime;
         profile.Battery.DischargingTime = savedProfile.Battery.DischargingTime;
         profile.Battery.Level           = savedProfile.Battery.Level;
         profile.EnableAudioApi          = savedProfile.EnableAudioApi;
         profile.EnablePlugins           = savedProfile.EnablePlugins;
         profile.EnableMediaPlugins      = savedProfile.EnableMediaPlugins;
         profile.RandomTimersEnabled     = savedProfile.RandomTimersEnabled;
         profile.UserAgent               = savedProfile.UserAgent;
         profile.Screen.Color            = savedProfile.Screen.Color;
         profile.Screen.Height           = savedProfile.Screen.Height;
         profile.Screen.Width            = savedProfile.Screen.Width;
         profile.HistoryLength           = savedProfile.HistoryLength;
         profile.WebGL.BrowserplugsR     = savedProfile.WebGL.BrowserplugsR;
         profile.WebGL.Plus1             = savedProfile.WebGL.Plus1;
         profile.WebGL.Plus2             = savedProfile.WebGL.Plus2;
         profile.WebGL.Plus3             = savedProfile.WebGL.Plus3;
         profile.WebGL.Plus4             = savedProfile.WebGL.Plus4;
         profile.WebGL.Plus5             = savedProfile.WebGL.Plus5;
         profile.FakeClientRects         = savedProfile.FakeClientRects;
         profile.Canvas.B                = savedProfile.Canvas.B;
         profile.Canvas.G                = savedProfile.Canvas.G;
         profile.Canvas.R                = savedProfile.Canvas.R;
         profile.EnableNetwork           = savedProfile.EnableNetwork;
         profile.Language                = savedProfile.Language;
         profile.GeoIpEnabled            = savedProfile.GeoIpEnabled;
         profile.ByPassProxySites        = savedProfile.ByPassProxySites;
         profile.Proxies                 = savedProfile.Proxies;
     }
     catch (Exception ex)
     {
         throw new Exception("Update backup profile error");
     }
 }
Пример #7
0
    /// <summary>
    /// Checks if <paramref name="profile"/> is installed.
    /// </summary>
    /// <param name="profile">The <see cref="ProfileXML"/> that should be checked for installation.</param>
    /// <returns><see langword="true"/> if yes, <see langword="false"/> if not.</returns>
    public static bool IsProfileInstalled(ProfileXML profile)
    {
        if (OS.IsWindows)
        {
            return(File.Exists(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name + "/AM2R.exe"));
        }
        if (OS.IsLinux)
        {
            return(File.Exists(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name + "/AM2R.AppImage"));
        }
        if (OS.IsMac)
        {
            return(Directory.Exists(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name + "/AM2R.app"));
        }

        log.Error(OS.Name + " can't have profiles installed!");
        return(false);
    }
Пример #8
0
        /// <summary>
        /// Gets called when <see cref="deleteModButton"/> gets clicked. Deletes the current selected <see cref="ProfileXML"/> in <see cref="modSettingsProfileDropDown"/>.
        /// </summary>
        private void DeleteModButtonClicked(object sender, EventArgs e)
        {
            ProfileXML profile = profileList[modSettingsProfileDropDown.SelectedIndex];

            log.Info("User is attempting to delete profile " + profile.Name + ".");

            DialogResult result = MessageBox.Show(this, HelperMethods.GetText(Text.DeleteModWarning, profile.Name), Text.WarningWindowTitle,
                                                  MessageBoxButtons.OKCancel, MessageBoxType.Warning, MessageBoxDefaultButton.Cancel);

            // if user didn't press ok, cancel
            if (result != DialogResult.Ok)
            {
                log.Info("User has cancelled profile deletion.");
                return;
            }

            log.Info("User did not cancel. Proceeding to delete " + profile);
            DeleteProfileAndAdjustLists(profile);
            log.Info(profile + " has been deleted");
            MessageBox.Show(this, HelperMethods.GetText(Text.DeleteModButtonSuccess, profile.Name), Text.SuccessWindowTitle);
        }
Пример #9
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");
    }
Пример #10
0
    /// <summary>
    /// Runs the Game, works cross platform.
    /// </summary>
    public static void RunGame(ProfileXML profile, bool useLogging, string envVars = "")
    {
        // These are used on both windows and linux for game logging
        string savePath = OS.IsWindows ? profile.SaveLocation.Replace("%localappdata%", Environment.GetEnvironmentVariable("LOCALAPPDATA"))
            : profile.SaveLocation.Replace("~", CrossPlatformOperations.NIXHOME);
        DirectoryInfo logDir = new DirectoryInfo(savePath + "/logs");
        string        date   = String.Join("-", DateTime.Now.ToString().Split(Path.GetInvalidFileNameChars(), StringSplitOptions.RemoveEmptyEntries));

        log.Info("Launching game profile " + profile.Name + ".");
        if (OS.IsWindows)
        {
            // Sets the arguments to empty, or to the profiles save path/logs and create time based logs. Creates the folder if necessary.
            string arguments = "";

            // Game logging
            if (useLogging)
            {
                log.Info("Performing logging setup for profile " + profile.Name + ".");

                if (!Directory.Exists(logDir.FullName))
                {
                    Directory.CreateDirectory(logDir.FullName);
                }

                if (File.Exists(logDir.FullName + "/" + profile.Name + ".txt"))
                {
                    HelperMethods.RecursiveRollover(logDir.FullName + "/" + profile.Name + ".txt", 5);
                }

                StreamWriter stream = File.AppendText(logDir.FullName + "/" + profile.Name + ".txt");

                stream.WriteLine("AM2RLauncher " + Core.VERSION + " log generated at " + date);

                if (Core.IsThisRunningFromWine)
                {
                    stream.WriteLine("Using WINE!");
                }

                stream.Flush();

                stream.Close();

                arguments = "-debugoutput \"" + logDir.FullName + "/" + profile.Name + ".txt\" -output \"" + logDir.FullName + "/" + profile.Name + ".txt\"";
            }

            ProcessStartInfo proc = new ProcessStartInfo();

            proc.WorkingDirectory = CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name;
            proc.FileName         = proc.WorkingDirectory + "/AM2R.exe";
            proc.Arguments        = arguments;

            log.Info("CWD of Profile is " + proc.WorkingDirectory);

            using var p = Process.Start(proc);
            Core.SetForegroundWindow(p.MainWindowHandle);
            p.WaitForExit();
        }
        else if (OS.IsLinux)
        {
            ProcessStartInfo startInfo = new ProcessStartInfo();
            log.Info("User does " + (String.IsNullOrWhiteSpace(envVars) ? "not" : "") + " have custom environment variables set.");

            //TODO: make this more readable at one day
            if (!String.IsNullOrWhiteSpace(envVars))
            {
                for (int i = 0; i < envVars.Count(f => f == '='); i++)
                {
                    // Env var variable
                    string variable = envVars.Substring(0, envVars.IndexOf('='));
                    envVars = envVars.Replace(variable + "=", "");

                    // This thing here is the value parser. Since values are sometimes in quotes, i need to compensate for them.
                    int valueSubstringLength;
                    if (envVars[0] != '"') // If value is not embedded in "", check if there are spaces left. If yes, get the index of the space, if not that was the last
                    {
                        if (envVars.IndexOf(' ') >= 0)
                        {
                            valueSubstringLength = envVars.IndexOf(' ') + 1;
                        }
                        else
                        {
                            valueSubstringLength = envVars.Length;
                        }
                    }
                    else // If value is embedded in "", check if there are spaces after the "". if yes, get index of that, if not that was the last
                    {
                        int secondQuoteIndex = envVars.IndexOf('"', envVars.IndexOf('"') + 1);
                        if (envVars.IndexOf(' ', secondQuoteIndex) >= 0)
                        {
                            valueSubstringLength = envVars.IndexOf(' ', secondQuoteIndex) + 1;
                        }
                        else
                        {
                            valueSubstringLength = envVars.Length;
                        }
                    }
                    // Env var value
                    string value = envVars.Substring(0, valueSubstringLength);
                    envVars = envVars.Substring(value.Length);

                    log.Info("Adding user variable \"" + variable + "\" with value \"" + value + "\"");
                    startInfo.EnvironmentVariables[variable] = value;
                }
            }

            // If we're supposed to log profiles, add events that track those and append them to this var. otherwise keep it null
            string terminalOutput = null;

            startInfo.UseShellExecute  = false;
            startInfo.WorkingDirectory = CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name;
            startInfo.FileName         = startInfo.WorkingDirectory + "/AM2R.AppImage";

            log.Info("CWD of Profile is " + startInfo.WorkingDirectory);

            log.Debug("Launching game with following variables: ");
            foreach (System.Collections.DictionaryEntry item in startInfo.EnvironmentVariables)
            {
                log.Debug("Key: \"" + item.Key + "\" Value: \"" + item.Value + "\"");
            }

            using (Process p = new Process())
            {
                p.StartInfo = startInfo;
                if (useLogging)
                {
                    p.StartInfo.RedirectStandardOutput = true;
                    p.OutputDataReceived += (_, e) => { terminalOutput += e.Data + "\n"; };

                    p.StartInfo.RedirectStandardError = true;
                    p.ErrorDataReceived += (_, e) => { terminalOutput += e.Data + "\n"; };
                }

                p.Start();

                if (useLogging)
                {
                    p.BeginOutputReadLine();
                    p.BeginErrorReadLine();
                }

                p.WaitForExit();
            }

            if (terminalOutput != null)
            {
                log.Info("Performed logging setup for profile " + profile.Name + ".");

                if (!Directory.Exists(logDir.FullName))
                {
                    Directory.CreateDirectory(logDir.FullName);
                }

                if (File.Exists(logDir.FullName + "/" + profile.Name + ".txt"))
                {
                    HelperMethods.RecursiveRollover(logDir.FullName + "/" + profile.Name + ".txt", 5);
                }

                StreamWriter stream = File.AppendText(logDir.FullName + "/" + profile.Name + ".txt");

                // Write general info
                stream.WriteLine("AM2RLauncher " + Core.VERSION + " log generated at " + date);

                // Write what was in the terminal
                stream.WriteLine(terminalOutput);

                stream.Flush();

                stream.Close();
            }
        }
        else if (OS.IsMac)
        {
            // Sets the arguments to only open the game, or append the profiles save path/logs and create time based logs. Creates the folder if necessary.
            string arguments = "AM2R.app -W";

            // Game logging
            if (useLogging)
            {
                log.Info("Performing logging setup for profile " + profile.Name + ".");

                if (!Directory.Exists(logDir.FullName))
                {
                    Directory.CreateDirectory(logDir.FullName);
                }

                if (File.Exists(logDir.FullName + "/" + profile.Name + ".txt"))
                {
                    HelperMethods.RecursiveRollover(logDir.FullName + "/" + profile.Name + ".txt", 5);
                }

                StreamWriter stream = File.AppendText(logDir.FullName + "/" + profile.Name + ".txt");

                stream.WriteLine("AM2RLauncher " + Core.VERSION + " log generated at " + date);

                stream.Flush();

                stream.Close();

                arguments += " --stdout \"" + logDir.FullName + "/" + profile.Name + ".txt\" --stderr \"" + logDir.FullName + "/" + profile.Name + ".txt\"";
            }

            ProcessStartInfo proc = new ProcessStartInfo();

            proc.WorkingDirectory = CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name;
            proc.FileName         = "open";
            proc.Arguments        = arguments;

            log.Info("CWD of Profile is " + proc.WorkingDirectory);

            using var p = Process.Start(proc);
            p?.WaitForExit();
        }
        else
        {
            log.Error(OS.Name + " cannot run games!");
        }

        log.Info("Profile " + profile.Name + " process exited.");
    }
Пример #11
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 + ".");
    }
Пример #12
0
 /// <summary>
 /// Deletes a profile and reloads the necessary UI components.
 /// </summary>
 /// <param name="profile">The profile to delete.</param>
 private void DeleteProfileAndAdjustLists(ProfileXML profile)
 {
     Profile.DeleteProfile(profile);
     LoadProfilesAndAdjustLists();
 }
Пример #13
0
 private void ArchiveProfileAndAdjustLists(ProfileXML profile)
 {
     Profile.ArchiveProfile(profile);
     LoadProfilesAndAdjustLists();
 }
Пример #14
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 + "!");
            }
        }
Пример #15
0
        /// <summary>
        /// After the <see cref="playButton"/> has bee loaded, git pull if a repo has been cloned already.
        /// </summary>
        private async void PlayButtonLoadComplete(object sender, EventArgs e)
        {
            LoadProfilesAndAdjustLists();
            if (!Profile.IsPatchDataCloned() || !(bool)autoUpdateAM2RCheck.Checked)
            {
                return;
            }

            SetPlayButtonState(PlayButtonState.Downloading);

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

            // Try to pull first.
            try
            {
                log.Info("Attempting to pull repository " + currentMirror + "...");
                await Task.Run(() => Profile.PullPatchData(TransferProgressHandlerMethod));

                // Thank you druid, for this case that should never happen
                if (!File.Exists(CrossPlatformOperations.CURRENTPATH + "/PatchData/profile.xml"))
                {
                    log.Error("Druid PatchData corruption occurred!");
                    await Application.Instance.InvokeAsync(() =>
                    {
                        MessageBox.Show(this, Text.CorruptPatchData, Text.ErrorWindowTitle, MessageBoxType.Error);
                    });

                    HelperMethods.DeleteDirectory(CrossPlatformOperations.CURRENTPATH + "/PatchData");
                    return;
                }
            }
            catch (UserCancelledException ex)
            {
                log.Info(ex.Message);
                MessageBox.Show(this, Text.CorruptPatchData, Text.ErrorWindowTitle, MessageBoxType.Error);
                HelperMethods.DeleteDirectory(CrossPlatformOperations.CURRENTPATH + "/PatchData");
            }
            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"))
                {
                    if (!(bool)autoUpdateAM2RCheck.Checked)
                    {
                        log.Error("Internet connection failed while attempting to pull repository" + currentMirror + "!");
                        MessageBox.Show(this, Text.InternetConnectionDrop, Text.WarningWindowTitle, MessageBoxType.Warning);
                    }
                }
                else
                {
                    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);
                }
            }
            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);
            }
            finally
            {
                progressBar.Visible   = false;
                progressLabel.Visible = false;
                LoadProfilesAndAdjustLists();
            }

            // Handling for updates - if current version does not match PatchData version, rename folder so that we attempt to install!
            // Also, add a non-installable profile for it so people can access the older version or delete it from the mod manager.
            if (profileList.Count > 0 && Profile.IsProfileInstalled(profileList[0]))
            {
                ProfileXML currentXML = Serializer.Deserialize <ProfileXML>(File.ReadAllText(CrossPlatformOperations.CURRENTPATH + "/Profiles/Community Updates (Latest)/profile.xml"));

                if (currentXML.Version != profileList[0].Version)
                {
                    log.Info("New game version (" + profileList[0].Version + ") detected! Beginning archival of version " + currentXML.Version + "...");
                    Profile.ArchiveProfile(currentXML);
                    profileDropDown.SelectedIndex = 0;
                    LoadProfilesAndAdjustLists();
                }
            }

            SetPlayButtonState(PlayButtonState.Install);
            UpdateStateMachine();
        }
Пример #16
0
        /// <summary>
        /// Gets called, when <see cref="updateModButton"/> gets clicked. Opens a window, so the user can select a zip, which will be updated over
        /// the current selected <see cref="ProfileXML"/> in <see cref="modSettingsProfileDropDown"/>.
        /// </summary>
        private void UpdateModButtonClicked(object sender, EventArgs e)
        {
            log.Info("User requested to update mod. Requesting user input for new mod .zip...");

            ProfileXML     currentProfile = profileList[modSettingsProfileDropDown.SelectedIndex];
            OpenFileDialog fileFinder     = GetSingleZipDialog(Text.SelectModFileDialog);

            // If user didn't click OK, cancel
            if (fileFinder.ShowDialog(this) != DialogResult.Ok)
            {
                log.Info("User cancelled the Mod selection.");
                return;
            }

            log.Info("User selected \"" + fileFinder.FileName + "\"");

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

            //TODO: move most of this into AM2RLauncher.Profile?

            FileInfo modFile         = new FileInfo(fileFinder.FileName);
            string   modsDir         = new DirectoryInfo(CrossPlatformOperations.CURRENTPATH + "/Mods").FullName;
            string   extractedName   = Path.GetFileNameWithoutExtension(modFile.Name) + "_new";
            string   extractedModDir = modsDir + "/" + extractedName;

            // If for some reason old files remain, delete them so that extraction doesnt throw
            if (Directory.Exists(extractedModDir))
            {
                Directory.Delete(extractedModDir, true);
            }

            // Directory doesn't exist -> extract!
            ZipFile.ExtractToDirectory(fileFinder.FileName, extractedModDir);

            // If mod doesn't have a profile.xml, throw an error and cleanup
            if (!File.Exists(extractedModDir + "/profile.xml"))
            {
                log.Error(fileFinder.FileName + " does not contain profile.xml! Cancelling mod update.");
                MessageBox.Show(this, HelperMethods.GetText(Text.ModIsInvalidMessage, extractedName), Text.ErrorWindowTitle, MessageBoxType.Error);
                Directory.Delete(extractedModDir, true);
                return;
            }

            // Check by *name*, if the mod was installed already
            ProfileXML profile = Serializer.Deserialize <ProfileXML>(File.ReadAllText(extractedModDir + "/profile.xml"));

            // If the selected mod is not installed, tell user that they should add it and cleanup
            if (profileList.FirstOrDefault(p => p.Name == profile.Name) == null)
            {
                log.Error("Mod is not installed! Cancelling mod update.");
                MessageBox.Show(this, HelperMethods.GetText(Text.UpdateModButtonWrongMod, currentProfile.Name).Replace("$SELECT", profile.Name),
                                Text.WarningWindowTitle, MessageBoxButtons.OK);
                HelperMethods.DeleteDirectory(extractedModDir);
                return;
            }

            // If user doesn't want to update, cleanup
            DialogResult updateResult = MessageBox.Show(this, HelperMethods.GetText(Text.UpdateModWarning, currentProfile.Name), Text.WarningWindowTitle,
                                                        MessageBoxButtons.OKCancel, MessageBoxType.Warning, MessageBoxDefaultButton.Cancel);

            if (updateResult != DialogResult.Ok)
            {
                log.Error("User has cancelled mod update!");
                HelperMethods.DeleteDirectory(extractedModDir);
                return;
            }

            // If the profile isn't installed, don't ask about archiving it
            if (Profile.IsProfileInstalled(currentProfile))
            {
                DialogResult archiveResult = MessageBox.Show(this, HelperMethods.GetText(Text.ArchiveMod, currentProfile.Name + " " + Text.VersionLabel + currentProfile.Version), Text.WarningWindowTitle, MessageBoxButtons.YesNo, MessageBoxType.Warning, MessageBoxDefaultButton.No);

                // User wants to archive profile
                if (archiveResult == DialogResult.Yes)
                {
                    ArchiveProfileAndAdjustLists(currentProfile);
                }
            }

            DeleteProfileAndAdjustLists(currentProfile);

            // Rename directory to take the old one's place
            string originalFolder = modsDir + "/" + Path.GetFileNameWithoutExtension(modFile.Name);

            Directory.Move(extractedModDir, originalFolder);

            // Adjust our lists so it gets recognized
            LoadProfilesAndAdjustLists();

            modSettingsProfileDropDown.SelectedIndex = profileList.FindIndex(p => p.Name == currentProfile.Name);
            if (modSettingsProfileDropDown.SelectedIndex == -1)
            {
                modSettingsProfileDropDown.SelectedIndex = 0;
            }

            log.Info("Successfully updated mod profile " + profile.Name + ".");
            MessageBox.Show(this, HelperMethods.GetText(Text.ModSuccessfullyInstalledMessage, currentProfile.Name), Text.SuccessWindowTitle);
        }
Пример #17
0
        /// <summary>
        /// Runs when <see cref="addModButton"/> is clicked. Brings up a file select to select a mod, and adds that to the mod directory.
        /// </summary>
        private void AddModButtonClicked(object sender, EventArgs e)
        {
            log.Info("User requested to add mod. Requesting user input for new mod .zip...");

            OpenFileDialog fileFinder = GetSingleZipDialog(Text.SelectModFileDialog);

            // If user didn't press ok, cancel
            if (fileFinder.ShowDialog(this) != DialogResult.Ok)
            {
                log.Info("User cancelled the Mod selection.");
                return;
            }

            log.Info("User selected \"" + fileFinder.FileName + "\"");

            // If either a directory was selected, user pressed OK without selecting anything or the file somehow went missing, cancel
            if (!File.Exists(fileFinder.FileName))
            {
                log.Error("Selected mod .zip file not found! Cancelling import.");
                return;
            }

            //TODO: move most of this into AM2RLauncher.Profile?

            FileInfo modFile         = new FileInfo(fileFinder.FileName);
            string   modsDir         = new DirectoryInfo(CrossPlatformOperations.CURRENTPATH + "/Mods").FullName;
            string   modFileName     = Path.GetFileNameWithoutExtension(modFile.Name);
            string   extractedModDir = modsDir + "/" + modFileName;

            // Check first, if the directory is already there, if yes, throw error
            if (Directory.Exists(extractedModDir))
            {
                string existingProfileName = Serializer.Deserialize <ProfileXML>(File.ReadAllText(extractedModDir + "/profile.xml")).Name;
                log.Error("Mod is already imported as " + modFileName + "! Cancelling mod import.");
                MessageBox.Show(this, HelperMethods.GetText(Text.ModIsAlreadyInstalledMessage, existingProfileName), Text.WarningWindowTitle, MessageBoxType.Warning);
                return;
            }

            // Directory doesn't exist -> extract!
            ZipFile.ExtractToDirectory(modFile.FullName, extractedModDir);
            log.Info("Imported and extracted mod .zip as " + modFileName);

            // If profile.xml doesn't exist, throw an error and cleanup
            if (!File.Exists(extractedModDir + "/profile.xml"))
            {
                log.Error(modFile.Name + " does not contain profile.xml! Cancelling mod import.");
                MessageBox.Show(this, HelperMethods.GetText(Text.ModIsInvalidMessage, modFileName), Text.ErrorWindowTitle, MessageBoxType.Error);
                Directory.Delete(extractedModDir, true);
                return;
            }

            ProfileXML profile = Serializer.Deserialize <ProfileXML>(File.ReadAllText(extractedModDir + "/profile.xml"));

            // If OS versions mismatch, throw error and cleanup
            if (OS.Name != profile.OperatingSystem)
            {
                log.Error("Mod is for " + profile.OperatingSystem + " while current OS is " + OS.Name + ". Cancelling mod import.");
                MessageBox.Show(this, HelperMethods.GetText(Text.ModIsForWrongOS, profile.Name).Replace("$OS", profile.OperatingSystem).Replace("$CURRENTOS", OS.Name),
                                Text.ErrorWindowTitle, MessageBoxType.Error);
                HelperMethods.DeleteDirectory(extractedModDir);
                return;
            }

            // If mod was installed/added by *name* already, throw error and cleanup
            if (profileList.FirstOrDefault(p => p.Name == profile.Name) != null)
            {
                log.Error(profile.Name + " is already installed.");
                MessageBox.Show(this, HelperMethods.GetText(Text.ModIsAlreadyInstalledMessage, profile.Name), Text.WarningWindowTitle, MessageBoxType.Warning);
                HelperMethods.DeleteDirectory(extractedModDir);
                return;
            }

            // Reload list so mod gets recognized
            LoadProfilesAndAdjustLists();
            // Adjust profileIndex to point to newly added mod. if its not found for whatever reason, we default to first community updates
            modSettingsProfileDropDown.SelectedIndex = profileList.FindIndex(p => p.Name == profile.Name);
            if (modSettingsProfileDropDown.SelectedIndex == -1)
            {
                modSettingsProfileDropDown.SelectedIndex = 0;
            }

            log.Info(profile.Name + " successfully added.");
            MessageBox.Show(this, HelperMethods.GetText(Text.ModSuccessfullyInstalledMessage, profile.Name), Text.SuccessWindowTitle);
        }