Some cleanup

pull/32/head
Miepee 5 years ago
parent 398a44e92d
commit 8f6ef8212c

@ -9,12 +9,12 @@ namespace AM2RLauncher.Gtk;
/// <summary>
/// The main class for the GTK project.
/// </summary>
static class MainClass
internal static class MainClass
{
/// <summary>
/// The logger for <see cref="MainForm"/>, used to write any caught exceptions.
/// </summary>
private static readonly ILog Log = LogManager.GetLogger(typeof(MainForm));
private static readonly ILog log = LogManager.GetLogger(typeof(MainForm));
/// <summary>
/// The main method for the GTK project.
/// </summary>
@ -36,14 +36,14 @@ static class MainClass
try
{
Application GTKLauncher = new Application(Eto.Platforms.Gtk);
Application gtkLauncher = new Application(Eto.Platforms.Gtk);
LauncherUpdater.Main();
GTKLauncher.UnhandledException += GTKLauncher_UnhandledException;
GTKLauncher.Run(new MainForm());
gtkLauncher.UnhandledException += GTKLauncher_UnhandledException;
gtkLauncher.Run(new MainForm());
}
catch (Exception e)
{
Log.Error("An unhandled exception has occurred: \n*****Stack Trace*****\n\n" + e.StackTrace);
log.Error("An unhandled exception has occurred: \n*****Stack Trace*****\n\n" + e.StackTrace);
Console.WriteLine(Language.Text.UnhandledException + "\n" + e.Message + "\n*****Stack Trace*****\n\n" + e.StackTrace);
Console.WriteLine("Check the logs at " + launcherDataPath + " for more info!");
}
@ -54,7 +54,7 @@ static class MainClass
/// </summary>
private static void GTKLauncher_UnhandledException(object sender, Eto.UnhandledExceptionEventArgs e)
{
Log.Error("An unhandled exception has occurred: \n*****Stack Trace*****\n\n" + e.ExceptionObject);
log.Error("An unhandled exception has occurred: \n*****Stack Trace*****\n\n" + e.ExceptionObject);
Application.Instance.Invoke(() =>
{
MessageBox.Show(Language.Text.UnhandledException + "\n*****Stack Trace*****\n\n" + e.ExceptionObject, "GTK", MessageBoxType.Error);
@ -100,5 +100,4 @@ static class MainClass
return Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
}
}
}

@ -2,6 +2,7 @@
using log4net;
using log4net.Config;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
namespace AM2RLauncher.Mac;
@ -9,12 +10,12 @@ namespace AM2RLauncher.Mac;
/// <summary>
/// The main class for the Mac project.
/// </summary>
static class MainClass
internal static class MainClass
{
/// <summary>
/// The logger for <see cref="MainForm"/>, used to write any caught exceptions.
/// </summary>
private static readonly ILog Log = LogManager.GetLogger(typeof(MainForm));
private static readonly ILog log = LogManager.GetLogger(typeof(MainForm));
/// <summary>
/// The main method for the Mac project.
@ -44,7 +45,7 @@ static class MainClass
}
catch (Exception e)
{
Log.Error("An unhandled exception has occurred: \n*****Stack Trace*****\n\n" + e.StackTrace);
log.Error("An unhandled exception has occurred: \n*****Stack Trace*****\n\n" + e.StackTrace);
Console.WriteLine(Language.Text.UnhandledException + "\n" + e.Message + "\n*****Stack Trace*****\n\n" + e.StackTrace);
Console.WriteLine("Check the logs at " + launcherDataPath + " for more info!");
}
@ -55,7 +56,7 @@ static class MainClass
/// </summary>
private static void MacLauncher_UnhandledException(object sender, Eto.UnhandledExceptionEventArgs e)
{
Log.Error("An unhandled exception has occurred: \n*****Stack Trace*****\n\n" + e.ExceptionObject);
log.Error("An unhandled exception has occurred: \n*****Stack Trace*****\n\n" + e.ExceptionObject);
Application.Instance.Invoke(() =>
{
MessageBox.Show(Language.Text.UnhandledException + "\n*****Stack Trace*****\n\n" + e.ExceptionObject, "Mac", MessageBoxType.Error);
@ -63,8 +64,23 @@ static class MainClass
}
// This is a duplicate of CrossPlatformOperations.GenerateCurrentPath, because trying to invoke that would cause a crash due to currentPlatform not being initialized.
[return: NotNull]
private static string GenerateCurrentPath()
{
string am2rLauncherDataEnvVar = Environment.GetEnvironmentVariable("AM2RLAUNCHERDATA");
if (!String.IsNullOrWhiteSpace(am2rLauncherDataEnvVar))
{
try
{
// This will create the directories recursively if they don't exist
Directory.CreateDirectory(am2rLauncherDataEnvVar);
// Our env var is now set and directories exist
return am2rLauncherDataEnvVar;
}
catch { }
}
string nixHome = Environment.GetEnvironmentVariable("HOME");
//Mac has the Path at HOME/Library/AM2RLauncher
string macPath = nixHome + "/Library/AM2RLauncher";

@ -10,12 +10,12 @@ namespace AM2RLauncher.Wpf;
/// <summary>
/// The main class for the WinForms project.
/// </summary>
static class MainClass
internal static class MainClass
{
/// <summary>
/// The logger for <see cref="MainForm"/>, used to write any caught exceptions.
/// </summary>
private static readonly ILog Log = LogManager.GetLogger(typeof(MainForm));
private static readonly ILog log = LogManager.GetLogger(typeof(MainForm));
/// <summary>
/// The main method for the WinForms project.
/// </summary>
@ -45,7 +45,7 @@ static class MainClass
}
catch (Exception e)
{
Log.Error("An unhandled exception has occurred: \n*****Stack Trace*****\n\n" + e.StackTrace);
log.Error("An unhandled exception has occurred: \n*****Stack Trace*****\n\n" + e.StackTrace);
System.Windows.Forms.MessageBox.Show(Language.Text.UnhandledException + "\n" + e.Message + "\n*****Stack Trace*****\n\n" + e.StackTrace, "Microsoft .NET Framework",
System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
}
@ -56,7 +56,7 @@ static class MainClass
/// </summary>
private static void WinLauncher_UnhandledException(object sender, Eto.UnhandledExceptionEventArgs e)
{
Log.Error("An unhandled exception has occurred: \n*****Stack Trace*****\n\n" + e.ExceptionObject);
log.Error("An unhandled exception has occurred: \n*****Stack Trace*****\n\n" + e.ExceptionObject);
MessageBox.Show(Language.Text.UnhandledException + "\n*****Stack Trace*****\n\n" + e.ExceptionObject, "Microsoft .NET Framework", MessageBoxType.Error);
}
@ -65,20 +65,22 @@ static class MainClass
{
// First, we check if the user has a custom AM2RLAUNCHERDATA env var
string am2rLauncherDataEnvVar = Environment.GetEnvironmentVariable("AM2RLAUNCHERDATA");
if (!String.IsNullOrWhiteSpace(am2rLauncherDataEnvVar))
// Windows has the path where the exe is located as default
string defaultPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
if (String.IsNullOrWhiteSpace(am2rLauncherDataEnvVar))
return defaultPath;
try
{
try
{
// This will create the directories recursively if they don't exist
Directory.CreateDirectory(am2rLauncherDataEnvVar);
// This will create the directories recursively if they don't exist
Directory.CreateDirectory(am2rLauncherDataEnvVar);
// Our env var is now set and directories exist
return am2rLauncherDataEnvVar;
}
catch { }
// Our env var is now set and directories exist
return am2rLauncherDataEnvVar;
}
catch
{
return defaultPath;
}
// Windows has the path where the exe is located as default
return Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
}
}
}

@ -1,5 +1,4 @@
using AM2RLauncher.Core;
using Eto;
using Eto.Forms;
using log4net;
using System;
@ -21,41 +20,41 @@ namespace AM2RLauncher
public const string VERSION = Core.Core.VERSION;
/// <summary>The Path of the oldConfig. Only gets used Windows-only</summary>
private static readonly string OldConfigPath = CrossPlatformOperations.CURRENTPATH + "/" + CrossPlatformOperations.LAUNCHERNAME + ".oldCfg";
private static readonly string oldConfigPath = CrossPlatformOperations.CURRENTPATH + "/" + CrossPlatformOperations.LAUNCHERNAME + ".oldCfg";
/// <summary>The actual Path where the executable is stored, only used for updating.</summary>
//TODO: for mac, this reports the path of the mac runner, not the actual .app
private static readonly string UpdatePath = OS.IsWindows ? CrossPlatformOperations.CURRENTPATH : Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
private static readonly string updatePath = OS.IsWindows ? CrossPlatformOperations.CURRENTPATH : Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
//TODO: Mac support for this in general
/// <summary>
/// Our log object, that handles logging the current execution to a file.
/// </summary>
private static readonly ILog Log = LogManager.GetLogger(typeof(MainForm));
private static readonly ILog log = LogManager.GetLogger(typeof(MainForm));
/// <summary>
/// Performs the entire AM2RLauncher update procedure.
/// </summary>
public static void Main()
{
Log.Info("Running update check...");
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.");
log.Info("AM2RLauncher.bak detected. Removing file.");
File.Delete(CrossPlatformOperations.CURRENTPATH + "/AM2RLauncher.bak");
}
if (OS.IsWindows && File.Exists(OldConfigPath))
if (OS.IsWindows && File.Exists(oldConfigPath))
{
Log.Info(CrossPlatformOperations.LAUNCHERNAME + ".oldCfg detected. Removing file.");
File.Delete(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.");
log.Info("Old lib folder detected, removing folder.");
Directory.Delete(CrossPlatformOperations.CURRENTPATH + "/oldLib", true);
}
@ -80,11 +79,11 @@ namespace AM2RLauncher
}
// Check settings if autoUpdateLauncher is set to true
bool autoUpdate = bool.Parse(CrossPlatformOperations.ReadFromConfig("AutoUpdateLauncher"));
bool autoUpdate = Boolean.Parse(CrossPlatformOperations.ReadFromConfig("AutoUpdateLauncher"));
if (autoUpdate)
{
Log.Info("AutoUpdate Launcher set to true!");
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
@ -102,7 +101,7 @@ namespace AM2RLauncher
}
catch (WebException)
{
Log.Error("WebException caught! Displaying MessageBox.");
log.Error("WebException caught! Displaying MessageBox.");
MessageBox.Show(Language.Text.NoInternetConnection);
return;
}
@ -116,24 +115,24 @@ namespace AM2RLauncher
for (int i = 0; i < localVersionArray.Length; i++)
{
int onlineNum = int.Parse(onlineVersionArray[i]);
int localNum = int.Parse(localVersionArray[i]);
int onlineNum = Int32.Parse(onlineVersionArray[i]);
int localNum = Int32.Parse(localVersionArray[i]);
if (onlineNum > localNum)
{
isCurrentVersionOutdated = true;
break;
}
else if (localNum > onlineNum)
if (localNum > onlineNum)
break;
}
Log.Info((isCurrentVersionOutdated ? "Updating" : "Not Updating") + " from " + VERSION + " to " + onlineVersion);
log.Info((isCurrentVersionOutdated ? "Updating" : "Not Updating") + " from " + VERSION + " to " + onlineVersion);
// No new update, exiting
if (!isCurrentVersionOutdated)
return;
Log.Info("Current version (" + VERSION + ") is outdated! Initiating update for version " + onlineVersion + ".");
log.Info("Current version (" + VERSION + ") is outdated! Initiating update for version " + onlineVersion + ".");
string tmpUpdatePath = CrossPlatformOperations.CURRENTPATH + "/tmpupdate/";
string zipPath = CrossPlatformOperations.CURRENTPATH + "/launcher.zip";
@ -152,31 +151,31 @@ namespace AM2RLauncher
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 + ".");
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.");
log.Info("File successfully downloaded.");
}
}
catch (UnauthorizedAccessException)
{
Log.Error("UnauthorizedAccessException caught! Displaying MessageBox.");
log.Error("UnauthorizedAccessException caught! Displaying MessageBox.");
MessageBox.Show(Language.Text.UnauthorizedAccessMessage);
return;
}
ZipFile.ExtractToDirectory(zipPath, tmpUpdatePath);
Log.Info("Updates successfully extracted to " + tmpUpdatePath);
log.Info("Updates successfully extracted to " + tmpUpdatePath);
File.Delete(zipPath);
File.Move(UpdatePath + "/" + CrossPlatformOperations.LAUNCHERNAME, CrossPlatformOperations.CURRENTPATH + "/AM2RLauncher.bak");
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);
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"))
@ -207,17 +206,17 @@ namespace AM2RLauncher
CrossPlatformOperations.CopyOldConfigToNewConfig();
Log.Info("Files extracted. Preparing to restart executable...");
log.Info("Files extracted. Preparing to restart executable...");
if (OS.IsLinux) System.Diagnostics.Process.Start("chmod", "+x ./AM2RLauncher.Gtk");
System.Diagnostics.Process.Start(UpdatePath + "/" + CrossPlatformOperations.LAUNCHERNAME);
System.Diagnostics.Process.Start(updatePath + "/" + CrossPlatformOperations.LAUNCHERNAME);
Environment.Exit(0);
}
else
{
Log.Info("AutoUpdate Launcher set to false. Exiting update check.");
log.Info("AutoUpdate Launcher set to false. Exiting update check.");
}
}
}
}
}

@ -7,7 +7,6 @@ using System.ComponentModel;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
@ -538,7 +537,7 @@ namespace AM2RLauncher
{
Log.Info("User requested to add mod. Requesting user input for new mod .zip...");
ProfileXML addedProfile = null;
ProfileXML addedProfile;
OpenFileDialog fileFinder = new OpenFileDialog
{
@ -721,8 +720,8 @@ namespace AM2RLauncher
private void DrawablePaintEvent(object sender, PaintEventArgs e)
{
// Get drawing variables
int height = drawable.Height;
int width = drawable.Width;
float height = drawable.Height;
float width = drawable.Width;
float scaleDivisor = OS.IsWindows ? 955f : 715f; // Magic brute-forced values. Don't ask questions, because we don't have answers >_>
// Also, seems like nix systems share the same scaleDivisor. Again, don't ask.
float scale = height / scaleDivisor;

@ -1,19 +1,9 @@
using Eto.Forms;
using LibGit2Sharp;
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
namespace AM2RLauncher
{
public partial class MainForm : Form
{
/// <summary>
/// Method that updates <see cref="progressBar"/>.
/// </summary>
@ -40,7 +30,6 @@ namespace AM2RLauncher
UpdateProgressBar(value, 0, 100);
}
/// <summary>
/// Safety check function before accessing <see cref="profileIndex"/>.
/// </summary>
@ -49,9 +38,5 @@ namespace AM2RLauncher
{
return profileIndex != null;
}
}
}
}

@ -160,13 +160,14 @@ namespace AM2RLauncher
bool enabled = false;
switch (updateState)
{
case UpdateState.Download: enabled = false; break;
case UpdateState.Downloading: enabled = false; break;
case UpdateState.Select11: enabled = false; break;
case UpdateState.Install: enabled = true; break;
case UpdateState.Installing: enabled = false; break;
case UpdateState.Play: enabled = true; break;
case UpdateState.Download:
case UpdateState.Downloading:
case UpdateState.Select11:
case UpdateState.Installing:
case UpdateState.Playing: enabled = false; break;
case UpdateState.Install:
case UpdateState.Play: enabled = true; break;
}
if (apkButtonState == ApkButtonState.Creating) enabled = false;

@ -81,21 +81,20 @@ namespace AM2RLauncher
/// </summary>
private static string currentMirror;
private static bool isInternetThere = Core.Core.isInternetThere;
private static bool isInternetThere = Core.Core.IsInternetThere;
private static bool isThisRunningFromWine = Core.Core.isThisRunningFromWine;
private static bool isThisRunningFromWine = Core.Core.IsThisRunningFromWine;
private static bool singleInstance;
// This mutex needs to CONTINUE existing for the entire application's lifetime, or else the rest of this won't ever work!
// We're basically using it to key a thread and scan for other instances of that tag.
Mutex mutex = new Mutex(true, "AM2RLauncher", out singleInstance);
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
bool singleInstance;
// This mutex needs to CONTINUE existing for the entire application's lifetime, or else the rest of this won't ever work!
// We're basically using it to key a thread and scan for other instances of that tag.
Mutex mutex = new Mutex(true, "AM2RLauncher", out singleInstance);
if (!singleInstance)
{
// If on Windows, set the original app to the foreground window to prevent confusion
@ -149,7 +148,7 @@ namespace AM2RLauncher
Log.Info("Actual Launcher location: " + Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory));
// Set the language to what User wanted or choose local language
string userLanguage = Core.CrossPlatformOperations.ReadFromConfig("Language").ToLower();
string userLanguage = CrossPlatformOperations.ReadFromConfig("Language").ToLower();
if (!userLanguage.Equals("default"))
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultures(CultureTypes.AllCultures).First(c => c.NativeName.ToLower().Contains(userLanguage));
@ -182,7 +181,7 @@ namespace AM2RLauncher
}
// Custom splash texts
string splash = Core.Splash.GetSplash();
string splash = Splash.GetSplash();
Log.Info("Randomly chosen splash: " + splash);
// Load bitmaps
@ -205,7 +204,7 @@ namespace AM2RLauncher
// 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 = Core.CrossPlatformOperations.GenerateMirrorList();
mirrorList = CrossPlatformOperations.GenerateMirrorList();
// Create mirror list
// We do this as a list<listItem> for 1) make this dynamic and 2) make ETO happy
@ -224,13 +223,13 @@ namespace AM2RLauncher
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(Core.CrossPlatformOperations.ReadFromConfig("Width")), Int32.Parse(Core.CrossPlatformOperations.ReadFromConfig("Height")));
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(Core.CrossPlatformOperations.ReadFromConfig("IsMaximized"))) Maximize();
if (Boolean.Parse(CrossPlatformOperations.ReadFromConfig("IsMaximized"))) Maximize();
drawable = new Drawable { BackgroundColor = colBGNoAlpha };
@ -450,7 +449,6 @@ namespace AM2RLauncher
{
changelogWebView
}
}
};
@ -541,7 +539,7 @@ namespace AM2RLauncher
languageDropDown.Items.AddRange(languageList);
var tmpLanguage = Core.CrossPlatformOperations.ReadFromConfig("Language");
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)
@ -553,7 +551,7 @@ namespace AM2RLauncher
// autoUpdateAM2R checkbox
autoUpdateAM2RCheck = new CheckBox
{
Checked = Boolean.Parse(Core.CrossPlatformOperations.ReadFromConfig("AutoUpdateAM2R")),
Checked = Boolean.Parse(CrossPlatformOperations.ReadFromConfig("AutoUpdateAM2R")),
Text = Language.Text.AutoUpdateAM2R,
TextColor = colGreen
};
@ -562,7 +560,7 @@ namespace AM2RLauncher
// autoUpdateLauncher checkbox
autoUpdateLauncherCheck = new CheckBox
{
Checked = Boolean.Parse(Core.CrossPlatformOperations.ReadFromConfig("AutoUpdateLauncher")),
Checked = Boolean.Parse(CrossPlatformOperations.ReadFromConfig("AutoUpdateLauncher")),
Text = Language.Text.AutoUpdateLauncher,
TextColor = colGreen
};
@ -570,7 +568,7 @@ namespace AM2RLauncher
// HQ music, PC
hqMusicPCCheck = new CheckBox
{
Checked = Boolean.Parse(Core.CrossPlatformOperations.ReadFromConfig("MusicHQPC")),
Checked = Boolean.Parse(CrossPlatformOperations.ReadFromConfig("MusicHQPC")),
Text = Language.Text.HighQualityPC,
TextColor = colGreen
};
@ -578,7 +576,7 @@ namespace AM2RLauncher
// HQ music, Android
hqMusicAndroidCheck = new CheckBox
{
Checked = Boolean.Parse(Core.CrossPlatformOperations.ReadFromConfig("MusicHQAndroid")),
Checked = Boolean.Parse(CrossPlatformOperations.ReadFromConfig("MusicHQAndroid")),
Text = Language.Text.HighQualityAndroid,
TextColor = colGreen
};
@ -639,14 +637,14 @@ namespace AM2RLauncher
// Custom mirror
customMirrorCheck = new CheckBox
{
Checked = Boolean.Parse(Core.CrossPlatformOperations.ReadFromConfig("CustomMirrorEnabled")),
Checked = Boolean.Parse(CrossPlatformOperations.ReadFromConfig("CustomMirrorEnabled")),
Text = Language.Text.CustomMirrorCheck,
TextColor = colGreen
};
customMirrorTextBox = new TextBox
{
Text = Core.CrossPlatformOperations.ReadFromConfig("CustomMirrorText"),
Text = CrossPlatformOperations.ReadFromConfig("CustomMirrorText"),
BackgroundColor = colBGNoAlpha,
TextColor = colGreen
};
@ -985,4 +983,4 @@ namespace AM2RLauncher
#endregion
}
}
}

@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
</PropertyGroup>
@ -12,4 +11,4 @@
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
</ItemGroup>
</Project>
</Project>

@ -1,53 +1,49 @@
using AM2RLauncher.Core.XML;
using log4net;
using log4net;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace AM2RLauncher.Core
{
namespace AM2RLauncher.Core;
/// <summary>
/// Class that has core stuff that doesn't fit anywhere else
/// </summary>
public static class Core
{
/// <summary>
/// Class that has core stuff that doesn't fit anywhere else
/// Our log object, that handles logging the current execution to a file.
/// </summary>
public static class Core
{
/// <summary>
/// Our log object, that handles logging the current execution to a file.
/// </summary>
public static readonly ILog Log = LogManager.GetLogger(typeof(Core));
public static readonly ILog Log = LogManager.GetLogger(typeof(Core));
/// <summary>The Version that identifies this current release.</summary>
public const string VERSION = "2.2.0";
/// <summary>The Version that identifies this current release.</summary>
public const string VERSION = "2.2.0";
/// <summary>
/// Checks if this is run via WINE.
/// </summary>
public static bool isThisRunningFromWine = CheckIfRunFromWINE();
/// <summary>
/// Checks if this is run via WINE.
/// </summary>
public static readonly bool IsThisRunningFromWine = CheckIfRunFromWINE();
/// <summary>
/// Indicates whether or not we have established an internet connection.
/// </summary>
public static readonly bool isInternetThere = HelperMethods.IsConnectedToInternet();
/// <summary>
/// Indicates whether or not we have established an internet connection.
/// </summary>
public static readonly bool IsInternetThere = HelperMethods.IsConnectedToInternet();
/// <summary>
/// Checks if this is ran from WINE
/// </summary>
/// <returns><see langword="true"/> if run from WINE, <see langword="false"/> if not.</returns>
private static bool CheckIfRunFromWINE()
{
if (OS.IsWindows && Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\\Wine") != null)
return true;
/// <summary>
/// Checks if this is ran from WINE
/// </summary>
/// <returns><see langword="true"/> if run from WINE, <see langword="false"/> if not.</returns>
private static bool CheckIfRunFromWINE()
{
if (OS.IsWindows && Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\\Wine") != null)
return true;
return false;
}
/// <summary>
/// This is used on Windows only. This sets a window to be in foreground, is used i.e. to fix am2r just being hidden.
/// </summary>
/// <param name="hWnd">Pointer to the process you want to have in the foreground.</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
return false;
}
}
/// <summary>
/// This is used on Windows only. This sets a window to be in foreground, is used i.e. to fix am2r just being hidden.
/// </summary>
/// <param name="hWnd">Pointer to the process you want to have in the foreground.</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
}

@ -7,550 +7,539 @@ using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
namespace AM2RLauncher.Core
namespace AM2RLauncher.Core;
/// <summary>
/// Class that does operations that work cross-platform.
/// </summary>
public static class CrossPlatformOperations
{
/// <summary>
/// Class that does operations that work cross-platform.
/// The logger for <see cref="Core"/>, used to write any caught exceptions.
/// </summary>
private static readonly ILog log = Core.Log;
/// <summary>
/// Name of the Launcher executable.
/// </summary>
public static readonly string LAUNCHERNAME = AppDomain.CurrentDomain.FriendlyName;
/// <summary>
/// Path to the Home Folder on *Nix-based systems.
/// </summary>
public static readonly string NIXHOME = Environment.GetEnvironmentVariable("HOME");
/// <summary>
/// Path to the Config folder on Linux-based systems.
/// </summary>
public static class CrossPlatformOperations
private static readonly string LINUXXDGCONFIG = Environment.GetEnvironmentVariable("XDG_CONFIG_HOME");
/// <summary>
/// Path to the Config file folder on *nix based systems. <br/>
/// Linux: Will point to XDG_CONFIG_HOME/AM2RLauncher <br/>
/// Mac: Will point to ~/Library/Preferences/AM2RLauncher
/// </summary>
private static readonly string NIXLAUNCHERCONFIGPATH = OS.IsLinux ? (String.IsNullOrWhiteSpace(LINUXXDGCONFIG) ? NIXHOME + "/.config"
: LINUXXDGCONFIG) + "/AM2RLauncher"
: NIXHOME + "/Library/Preferences/AM2RLauncher";
/// <summary>
/// Config file path for *nix based systems. Will be <see cref="NIXLAUNCHERCONFIGPATH"/> + "/config.xml".
/// </summary>
private static readonly string NIXLAUNCHERCONFIGFILEPATH = NIXLAUNCHERCONFIGPATH + "/config.xml";
/// <summary>
/// Current Path where the Launcher is located. For more info, check <see cref="GenerateCurrentPath"/>.
/// </summary>
public static readonly string CURRENTPATH = GenerateCurrentPath();
/// <summary>
/// Generates the mirror list, depending on the current Platform.
/// </summary>
/// <returns>A <see cref="List{String}"/> containing the mirror links.</returns>
public static List<string> GenerateMirrorList()
{
/// <summary>
/// The logger for <see cref="MainForm"/>, used to write any caught exceptions.
/// </summary>
private static readonly ILog Log = Core.Log;
/// <summary>
/// Name of the Launcher executable.
/// </summary>
public static readonly string LAUNCHERNAME = AppDomain.CurrentDomain.FriendlyName;
/// <summary>
/// Path to the Home Folder on *Nix-based systems.
/// </summary>
public static readonly string NIXHOME = Environment.GetEnvironmentVariable("HOME");
/// <summary>
/// Path to the Config folder on Linux-based systems.
/// </summary>
private static readonly string LINUXXDGCONFIG = Environment.GetEnvironmentVariable("XDG_CONFIG_HOME");
/// <summary>
/// Path to the Config file folder on *nix based systems. <br/>
/// Linux: Will point to XDG_CONFIG_HOME/AM2RLauncher <br/>
/// Mac: Will point to ~/Library/Preferences/AM2RLauncher
/// </summary>
private static readonly string NIXLAUNCHERCONFIGPATH = OS.IsLinux ? (String.IsNullOrWhiteSpace(LINUXXDGCONFIG) ? NIXHOME + "/.config"
: LINUXXDGCONFIG) + "/AM2RLauncher"
: NIXHOME + "/Library/Preferences/AM2RLauncher";
/// <summary>
/// Config file path for *nix based systems. Will be <see cref="NIXLAUNCHERCONFIGPATH"/> + "/config.xml".
/// </summary>
private static readonly string NIXLAUNCHERCONFIGFILEPATH = NIXLAUNCHERCONFIGPATH + "/config.xml";
/// <summary>
/// Current Path where the Launcher is located. For more info, check <see cref="GenerateCurrentPath"/>.
/// </summary>
public static readonly string CURRENTPATH = GenerateCurrentPath();
/// <summary>
/// Generates the mirror list, depending on the current Platform.
/// </summary>
/// <returns>A <see cref="List{String}"/> containing the mirror links.</returns>
public static List<string> GenerateMirrorList()
if (OS.IsWindows)
{
if (OS.IsWindows)
return new List<string>
{
return new List<string>
{
"https://github.com/AM2R-Community-Developers/AM2R-Autopatcher-Windows.git",
"https://gitlab.com/am2r-community-developers/AM2R-Autopatcher-Windows.git"
};
}
else if (OS.IsLinux)
"https://github.com/AM2R-Community-Developers/AM2R-Autopatcher-Windows.git",
"https://gitlab.com/am2r-community-developers/AM2R-Autopatcher-Windows.git"
};
}
if (OS.IsLinux)
{
return new List<string>
{
return new List<string>
{
"https://github.com/AM2R-Community-Developers/AM2R-Autopatcher-Linux.git",
"https://gitlab.com/am2r-community-developers/AM2R-Autopatcher-Linux.git"
};
}
else if (OS.IsMac)
"https://github.com/AM2R-Community-Developers/AM2R-Autopatcher-Linux.git",
"https://gitlab.com/am2r-community-developers/AM2R-Autopatcher-Linux.git"
};
}
if (OS.IsMac)
{
return new List<string>
{
return new List<string>
{
"https://github.com/Miepee/AM2R-Autopatcher-Mac.git"
//TODO: make mac official at some point:tm:, put this on gitlab
};
"https://github.com/Miepee/AM2R-Autopatcher-Mac.git"
//TODO: make mac official at some point:tm:, put this on gitlab
};
}
else // Should never occur, but...
{
Log.Error(OS.Name + " has no mirror lists!");
return new List<string>();
}
}
/// <summary>
/// Reads the Launcher config file on the current Platform and returns the value for <paramref name="property"/>.
/// </summary>
/// <param name="property">The property to get the value from.</param>
/// <returns>The value from <paramref name="property"/> as a string</returns>
public static string ReadFromConfig(string property)
// Should never occur, but...
log.Error(OS.Name + " has no mirror lists!");
return new List<string>();
}
/// <summary>
/// Reads the Launcher config file on the current Platform and returns the value for <paramref name="property"/>.
/// </summary>
/// <param name="property">The property to get the value from.</param>
/// <returns>The value from <paramref name="property"/> as a string</returns>
public static string ReadFromConfig(string property)
{
log.Info($"Reading {property} from config.");
if (OS.IsWindows)
{
// We use the configuration manager in order to read `property` from the app.config and then return it
ConnectionStringSettings appConfig = ConfigurationManager.ConnectionStrings[property];
if (appConfig == null) throw new ArgumentException("The property " + property + " could not be found.");
return appConfig.ConnectionString;
}
if (OS.IsUnix)
{
Log.Info($"Reading {property} from config.");
if (OS.IsWindows)
string launcherConfigPath = NIXLAUNCHERCONFIGPATH;
string launcherConfigFilePath = launcherConfigPath + "/config.xml";
XML.LauncherConfigXML launcherConfig = new XML.LauncherConfigXML();
// If folder doesn't exist, create it and the config file
if (!Directory.Exists(launcherConfigPath) || !File.Exists(launcherConfigFilePath))
{
// We use the configuration manager in order to read `property` from the app.config and then return it
ConnectionStringSettings appConfig = ConfigurationManager.ConnectionStrings[property];
if (appConfig == null) throw new ArgumentException("The property " + property + " could not be found.");
return appConfig.ConnectionString;
Directory.CreateDirectory(launcherConfigPath);
File.WriteAllText(launcherConfigFilePath, XML.Serializer.Serialize<XML.LauncherConfigXML>(launcherConfig));
}
else if (OS.IsUnix)
{
string launcherConfigPath = NIXLAUNCHERCONFIGPATH;
string launcherConfigFilePath = launcherConfigPath + "/config.xml";
XML.LauncherConfigXML launcherConfig = new XML.LauncherConfigXML();
// If folder doesn't exist, create it and the config file
if (!Directory.Exists(launcherConfigPath) || !File.Exists(launcherConfigFilePath))
{
Directory.CreateDirectory(launcherConfigPath);
File.WriteAllText(launcherConfigFilePath, XML.Serializer.Serialize<XML.LauncherConfigXML>(launcherConfig));
}
// Deserialize the config xml into launcherConfig
launcherConfig = XML.Serializer.Deserialize<XML.LauncherConfigXML>(File.ReadAllText(launcherConfigFilePath));
// Deserialize the config xml into launcherConfig
launcherConfig = XML.Serializer.Deserialize<XML.LauncherConfigXML>(File.ReadAllText(launcherConfigFilePath));
// This uses the indexer, which means, we can use the variable in order to get the property. Look at LauncherConfigXML for more info
return launcherConfig[property]?.ToString();
}
if (launcherConfig[property] == null)
return null;
log.Error(OS.Name + " has no config to read from!");
return null;
}
// This uses the indexer, which means, we can use the variable in order to get the property. Look at LauncherConfigXML for more info
return launcherConfig[property]?.ToString();
}
else
Log.Error(OS.Name + " has no config to read from!");
return null;
/// <summary>
/// Writes <paramref name="value"/> to <paramref name="property"/> in the Launcher Config file.
/// </summary>
/// <param name="property">The property whose value you want to change.</param>
/// <param name="value">The value that will be written.</param>
public static void WriteToConfig(string property, object value)
{
log.Info($"Writing {value} of type {value.GetType()} to {property} to config.");
if (OS.IsWindows)
{
// We use the configuration manager in order to read from the app.config, change the value and save it
Configuration appConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
if (appConfig == null)
throw new NullReferenceException("Could not find the Config file! Please make sure it exists!");
ConnectionStringsSection connectionStringsSection = (ConnectionStringsSection)appConfig.GetSection("connectionStrings");
if (connectionStringsSection?.ConnectionStrings[property]?.ConnectionString == null)
throw new ArgumentException("The property " + property + " could not be found.");
connectionStringsSection.ConnectionStrings[property].ConnectionString = value.ToString();
appConfig.Save();
ConfigurationManager.RefreshSection("connectionStrings");
}
/// <summary>
/// Writes <paramref name="value"/> to <paramref name="property"/> in the Launcher Config file.
/// </summary>
/// <param name="property">The property whose value you want to change.</param>
/// <param name="value">The value that will be written.</param>
public static void WriteToConfig(string property, object value)
else if (OS.IsUnix)
{
Log.Info($"Writing {value} of type {value.GetType()} to {property} to config.");
if (OS.IsWindows)
{
// We use the configuration manager in order to read from the app.config, change the value and save it
Configuration appConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
if (appConfig == null)
throw new NullReferenceException("Could not find the Config file! Please make sure it exists!");
ConnectionStringsSection connectionStringsSection = (ConnectionStringsSection)appConfig.GetSection("connectionStrings");
if (connectionStringsSection?.ConnectionStrings[property]?.ConnectionString == null)
throw new ArgumentException("The property " + property + " could not be found.");
connectionStringsSection.ConnectionStrings[property].ConnectionString = value.ToString();
appConfig.Save();
ConfigurationManager.RefreshSection("connectionStrings");
}
else if (OS.IsUnix)
string launcherConfigPath = NIXLAUNCHERCONFIGPATH;
string launcherConfigFilePath = NIXLAUNCHERCONFIGFILEPATH;
XML.LauncherConfigXML launcherConfig = new XML.LauncherConfigXML();
// If folder doesn't exist, create it and the config file
if (!Directory.Exists(launcherConfigPath) || !File.Exists(launcherConfigFilePath))
{
string launcherConfigPath = NIXLAUNCHERCONFIGPATH;
string launcherConfigFilePath = NIXLAUNCHERCONFIGFILEPATH;
XML.LauncherConfigXML launcherConfig = new XML.LauncherConfigXML();
// If folder doesn't exist, create it and the config file
if (!Directory.Exists(launcherConfigPath) || !File.Exists(launcherConfigFilePath))
{
Directory.CreateDirectory(launcherConfigPath);
File.WriteAllText(launcherConfigFilePath, XML.Serializer.Serialize<XML.LauncherConfigXML>(launcherConfig));
}
// Deserialize the config xml into launcherConfig
launcherConfig = XML.Serializer.Deserialize<XML.LauncherConfigXML>(File.ReadAllText(launcherConfigFilePath));
// Uses indexer. Look at LauncherConfigXML for more info
launcherConfig[property] = value;
// Serialize back into the file
Directory.CreateDirectory(launcherConfigPath);
File.WriteAllText(launcherConfigFilePath, XML.Serializer.Serialize<XML.LauncherConfigXML>(launcherConfig));
}
else
Log.Error(OS.Name + " has no config to write to!");
// Deserialize the config xml into launcherConfig
launcherConfig = XML.Serializer.Deserialize<XML.LauncherConfigXML>(File.ReadAllText(launcherConfigFilePath));
// Uses indexer. Look at LauncherConfigXML for more info
launcherConfig[property] = value;
// Serialize back into the file
File.WriteAllText(launcherConfigFilePath, XML.Serializer.Serialize<XML.LauncherConfigXML>(launcherConfig));
}
else
log.Error(OS.Name + " has no config to write to!");
}
/// <summary>
/// When a Launcher update occurs that introduces new config properties, this method ensures that the old user config is copied over as much as possible.
/// </summary>
public static void CopyOldConfigToNewConfig()
/// <summary>
/// When a Launcher update occurs that introduces new config properties, this method ensures that the old user config is copied over as much as possible.
/// </summary>
public static void CopyOldConfigToNewConfig()
{
if (OS.IsWindows)
{
if (OS.IsWindows)
{
string oldConfigPath = LAUNCHERNAME + ".oldCfg";
string newConfigPath = LAUNCHERNAME + ".config";
string oldConfigText = File.ReadAllText(oldConfigPath);
string newConfigText = File.ReadAllText(newConfigPath);
string oldConfigPath = LAUNCHERNAME + ".oldCfg";
string newConfigPath = LAUNCHERNAME + ".config";
string oldConfigText = File.ReadAllText(oldConfigPath);
string newConfigText = File.ReadAllText(newConfigPath);
Regex settingRegex = new Regex("<add name=\".*\" />");
Regex settingRegex = new Regex("<add name=\".*\" />");
MatchCollection oldMatch = settingRegex.Matches(oldConfigText);
MatchCollection newMatch = settingRegex.Matches(newConfigText);
MatchCollection oldMatch = settingRegex.Matches(oldConfigText);
MatchCollection newMatch = settingRegex.Matches(newConfigText);
for (int i = 0; i < oldMatch.Count; i++)
newConfigText = newConfigText.Replace(newMatch[i].Value, oldMatch[i].Value);
for (int i = 0; i < oldMatch.Count; i++)
newConfigText = newConfigText.Replace(newMatch[i].Value, oldMatch[i].Value);
File.WriteAllText(newConfigPath, newConfigText);
File.WriteAllText(newConfigPath, newConfigText);
}
else if (OS.IsUnix)
{
string launcherConfigFilePath = NIXLAUNCHERCONFIGFILEPATH;
}
else if (OS.IsUnix)
{
string launcherConfigFilePath = NIXLAUNCHERCONFIGFILEPATH;
// For some reason deserializing and saving back again works, not exactly sure why, but I'll take it
XML.LauncherConfigXML launcherConfig = XML.Serializer.Deserialize<XML.LauncherConfigXML>(File.ReadAllText(launcherConfigFilePath));
File.WriteAllText(launcherConfigFilePath, XML.Serializer.Serialize<XML.LauncherConfigXML>(launcherConfig));
}
else
Log.Error(OS.Name + " has no config to transfer over!");
// For some reason deserializing and saving back again works, not exactly sure why, but I'll take it
XML.LauncherConfigXML launcherConfig = XML.Serializer.Deserialize<XML.LauncherConfigXML>(File.ReadAllText(launcherConfigFilePath));
File.WriteAllText(launcherConfigFilePath, XML.Serializer.Serialize<XML.LauncherConfigXML>(launcherConfig));
}
else
log.Error(OS.Name + " has no config to transfer over!");
}
/// <summary>
/// This open a website cross-platform.
/// </summary>
/// <param name="url">The URL of the website to be opened.</param>
public static void OpenURL(string url)
/// <summary>
/// This open a website cross-platform.
/// </summary>
/// <param name="url">The URL of the website to be opened.</param>
public static void OpenURL(string url)
{
if (OS.IsWindows)
Process.Start(url);
else if (OS.IsLinux)
Process.Start("xdg-open", url);
else if (OS.IsMac)
Process.Start("open", url);
else
log.Error(OS.Name + " can't open URLs!");
}
/// <summary>
/// Opens <paramref name="path"/> in a file explorer. Creates the directory if it doesn't exist.
/// </summary>
/// <param name="path">Path to open.</param>
public static void OpenFolder(string path)
{
// We have to replace forward slashes with backslashes here on windows because explorer.exe is picky...
// And on Nix systems, we want to replace ~ with its corresponding env var
string realPath = OS.IsWindows ? Environment.ExpandEnvironmentVariables(path).Replace("/", "\\")
: path.Replace("~", NIXHOME);
if (!Directory.Exists(realPath))
{
if (OS.IsWindows)
Process.Start(url);
else if (OS.IsLinux)
Process.Start("xdg-open", url);
else if (OS.IsMac)
Process.Start("open", url);
else
Log.Error(OS.Name + " can't open URLs!");
log.Info(realPath + " did not exist and was created");
Directory.CreateDirectory(realPath);
}
/// <summary>
/// Opens <paramref name="path"/> in a file explorer. Creates the directory if it doesn't exist.
/// </summary>
/// <param name="path">Path to open.</param>
public static void OpenFolder(string path)
// Needs quotes otherwise paths with space wont open
if (OS.IsWindows)
// And we're using explorer.exe to prevent people from stuffing system commands in here wholesale. That would be bad.
Process.Start("explorer.exe", $"\"{realPath}\"");
// Linux only opens the directory bc opening and selecting a file is pain
else if (OS.IsLinux)
Process.Start("xdg-open", $"\"{realPath}\"");
else if (OS.IsMac)
Process.Start("open", $"\"{realPath}\"");
else
log.Error(OS.Name + " can't open folders!");
}
/// <summary>
/// Opens <paramref name="path"/> and selects it in a file explorer.
/// Only selects on Windows and Mac, on Linux it just opens the folder. Does nothing if file doesn't exist.
/// </summary>
/// <param name="path">Path to open.</param>
public static void OpenFolderAndSelectFile(string path)
{
// We have to replace forward slashes with backslashes here on windows because explorer.exe is picky...
// And on nix systems, we want to replace ~ with its corresponding env var
string realPath = OS.IsWindows ? Environment.ExpandEnvironmentVariables(path).Replace("/", "\\")
: path.Replace("~", NIXHOME);
if (!File.Exists(realPath))
{
// We have to replace forward slashes with backslashes here on windows because explorer.exe is picky...
// And on Nix systems, we want to replace ~ with its corresponding env var
string realPath = OS.IsWindows ? Environment.ExpandEnvironmentVariables(path).Replace("/", "\\")
: path.Replace("~", NIXHOME);
if (!Directory.Exists(realPath))
{
Log.Info(realPath + " did not exist and was created");
Directory.CreateDirectory(realPath);
}
log.Error(realPath + "did not exist, operation to open its folder was cancelled!");
return;
}
// Needs quotes otherwise paths with spaces wont open
if (OS.IsWindows)
// And we're using explorer.exe to prevent people from stuffing system commands in here wholesale. That would be bad.
Process.Start("explorer.exe", $"/select, \"{realPath}\"");
else if (OS.IsLinux)
Process.Start("xdg-open", $"\"{Path.GetDirectoryName(realPath)}\"");
else if (OS.IsMac)
Process.Start("open", $"-R \"{realPath}\"");
else
log.Error(OS.Name + " can't open select files in file explorer!");
}
// Needs quotes otherwise paths with space wont open
if (OS.IsWindows)
// And we're using explorer.exe to prevent people from stuffing system commands in here wholesale. That would be bad.
Process.Start("explorer.exe", $"\"{realPath}\"");
// Linux only opens the directory bc opening and selecting a file is pain
else if (OS.IsLinux)
Process.Start("xdg-open", $"\"{realPath}\"");
else if (OS.IsMac)
Process.Start("open", $"\"{realPath}\"");
else
Log.Error(OS.Name + " can't open folders!");
/// <summary>
/// Checks if command-line Java is installed.
/// </summary>
/// <returns><see langword="true"/> if it is installed, <see langword="false"/> if not.</returns>
public static bool IsJavaInstalled()
{
string process = "";
string arguments = "";
if (OS.IsWindows)
{
process = "cmd.exe";
arguments = "/C java -version";
}
else if (OS.IsUnix)
{
process = "java";
arguments = "-version";
}
/// <summary>
/// Opens <paramref name="path"/> and selects it in a file explorer.
/// Only selects on Windows and Mac, on Linux it just opens the folder. Does nothing if file doesn't exist.
/// </summary>
/// <param name="path">Path to open.</param>
public static void OpenFolderAndSelectFile(string path)
ProcessStartInfo javaStart = new ProcessStartInfo
{
// We have to replace forward slashes with backslashes here on windows because explorer.exe is picky...
// And on nix systems, we want to replace ~ with its corresponding env var
string realPath = OS.IsWindows ? Environment.ExpandEnvironmentVariables(path).Replace("/", "\\")
: path.Replace("~", NIXHOME);
if (!File.Exists(realPath))
{
Log.Error(realPath + "did not exist, operation to open its folder was cancelled!");
return;
}
FileName = process,
Arguments = arguments,
UseShellExecute = false,
CreateNoWindow = true
};
// Needs quotes otherwise paths with spaces wont open
if (OS.IsWindows)
// And we're using explorer.exe to prevent people from stuffing system commands in here wholesale. That would be bad.
Process.Start("explorer.exe", $"/select, \"{realPath}\"");
else if (OS.IsLinux)
Process.Start("xdg-open", $"\"{Path.GetDirectoryName(realPath)}\"");
else if (OS.IsMac)
Process.Start("open", $"-R \"{realPath}\"");
else
Log.Error(OS.Name + " can't open select files in file explorer!");
}
/// <summary>
/// Checks if command-line Java is installed.
/// </summary>
/// <returns><see langword="true"/> if it is installed, <see langword="false"/> if not.</returns>
public static bool IsJavaInstalled()
Process java = new Process { StartInfo = javaStart };
// This is primarily for linux, but could be happening on windows as well
try
{
string process = "";
string arguments = "";
java.Start();
if (OS.IsWindows)
{
process = "cmd.exe";
arguments = "/C java -version";
}
else if (OS.IsUnix)
{
process = "java";
arguments = "-version";
}
java.WaitForExit();
}
catch (System.ComponentModel.Win32Exception)
{
return false;
}
ProcessStartInfo javaStart = new ProcessStartInfo
{
FileName = process,
Arguments = arguments,
UseShellExecute = false,
CreateNoWindow = true
};
return java.ExitCode == 0;
}
/// <summary>
/// Checks if command-line xdelta is installed on non-Windows systems.
/// </summary>
/// <returns><see langword="true"/> if it is installed, <see langword="false"/> if not.</returns>
public static bool CheckIfXdeltaIsInstalled()
{
const string process = "xdelta3";
const string arguments = "-V";
Process java = new Process { StartInfo = javaStart };
ProcessStartInfo xdeltaStart = new ProcessStartInfo
{
FileName = process,
Arguments = arguments,
UseShellExecute = false,
CreateNoWindow = true
};
// This is primarily for linux, but could be happening on windows as well
try
{
java.Start();
java.WaitForExit();
}
catch (System.ComponentModel.Win32Exception)
{
return false;
}
Process xdelta = new Process { StartInfo = xdeltaStart };
return java.ExitCode == 0;
}
try
{
xdelta.Start();
/// <summary>
/// Checks if command-line xdelta is installed on non-Windows systems.
/// </summary>
/// <returns><see langword="true"/> if it is installed, <see langword="false"/> if not.</returns>
public static bool CheckIfXdeltaIsInstalled()
xdelta.WaitForExit();
}
catch (System.ComponentModel.Win32Exception)
{
const string process = "xdelta3";
const string arguments = "-V";
return false;
}
return xdelta.ExitCode == 0;
}
ProcessStartInfo xdeltaStart = new ProcessStartInfo
/// <summary>
/// This applies an Xdelta Patch cross-platform.
/// </summary>
/// <param name="original">Full Path to the original file.</param>
/// <param name="patch">Full Path to the Xdelta patch to apply.</param>
/// <param name="output">Full Path to the output file.</param>
public static void ApplyXdeltaPatch(string original, string patch, string output)
{
// For *whatever reason* **sometimes** xdelta patching doesn't work, if output = original. So I'm fixing that here.
string originalOutput = output;
if (original == output)
output += "_";
string arguments = "-f -d -s \"" + original.Replace(CURRENTPATH + "/", "") + "\" \"" + patch.Replace(CURRENTPATH + "/", "")
+ "\" \"" + output.Replace(CURRENTPATH + "/", "") + "\"";
if (OS.IsWindows)
{
// We want some fancy parameters for Windows because the terminal scares end users :(
ProcessStartInfo parameters = new ProcessStartInfo
{
FileName = process,
Arguments = arguments,
FileName = CURRENTPATH + "/PatchData/utilities/xdelta/xdelta3.exe",
WorkingDirectory = CURRENTPATH + "",
UseShellExecute = false,
CreateNoWindow = true
CreateNoWindow = true,
Arguments = arguments
};
using Process proc = new Process { StartInfo = parameters };
proc.Start();
proc.WaitForExit();
}
else if (OS.IsUnix)
{
ProcessStartInfo parameters = new ProcessStartInfo
{
FileName = "xdelta3",
Arguments = arguments,
WorkingDirectory = CURRENTPATH
};
Process xdelta = new Process { StartInfo = xdeltaStart };
using Process proc = Process.Start(parameters);
proc?.WaitForExit();
}
try
{
xdelta.Start();
if (originalOutput == output || !File.Exists(output))
return;
File.Delete(originalOutput);
File.Move(output, originalOutput);
}
xdelta.WaitForExit();
}
catch (System.ComponentModel.Win32Exception)
{
return false;
}
public static void RunJavaJar(string arguments = null, string workingDirectory = null)
{
workingDirectory ??= Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
string proc = "",
javaArgs = "";
return xdelta.ExitCode == 0;
if (OS.IsWindows)
{
proc = "cmd";
javaArgs = "/C java -jar";
}
else if (OS.IsUnix)
{
proc = "java";
javaArgs = "-jar";
}
/// <summary>
/// This applies an Xdelta Patch cross-platform.
/// </summary>
/// <param name="original">Full Path to the original file.</param>
/// <param name="patch">Full Path to the Xdelta patch to apply.</param>
/// <param name="output">Full Path to the output file.</param>
public static void ApplyXdeltaPatch(string original, string patch, string output)
ProcessStartInfo jarStart = new ProcessStartInfo
{
FileName = proc,
Arguments = javaArgs + " " + arguments,
WorkingDirectory = workingDirectory,
UseShellExecute = false,
CreateNoWindow = true
};
Process jarProcess = new Process
{
// For *whatever reason* **sometimes** xdelta patching doesn't work, if output = original. So I'm fixing that here.
string originalOutput = output;
if (original == output)
output += "_";
StartInfo = jarStart
};
string arguments = "-f -d -s \"" + original.Replace(CURRENTPATH + "/", "") + "\" \"" + patch.Replace(CURRENTPATH + "/", "")
+ "\" \"" + output.Replace(CURRENTPATH + "/", "") + "\"";
jarProcess.Start();
if (OS.IsWindows)
{
// We want some fancy parameters for Windows because the terminal scares end users :(
ProcessStartInfo parameters = new ProcessStartInfo
{
FileName = CURRENTPATH + "/PatchData/utilities/xdelta/xdelta3.exe",
WorkingDirectory = CURRENTPATH + "",
UseShellExecute = false,
CreateNoWindow = true,
Arguments = arguments
};
using (Process proc = new Process { StartInfo = parameters })
{
proc.Start();
proc.WaitForExit();
}
}
else if (OS.IsUnix)
jarProcess.WaitForExit();
}
/// <summary>
/// Figures out what the AM2RLauncher's <see cref="CURRENTPATH"/> should be.<br/>
/// Determination is as follows:
/// <list type="number">
/// <item><b>$AM2RLAUNCHERDATA</b> environment variable is read and folders are recursively generated.</item>
/// <item>The current OS is checked. For Windows, the path where the executable is located will be returned.<br/>
/// For Linux, <b>$XDG_DATA_HOME/AM2RLauncher</b> will be returned.
/// Should <b>$XDG_DATA_HOME</b> be empty, it will default to <b>$HOME/.local/share</b>.</item>
/// <item>The path where the executable is located will be returned.</item>
/// </list>
/// Should any errors occur, it falls down to the next step.
/// </summary>
/// <returns></returns>
private static string GenerateCurrentPath()
{
// First, we check if the user has a custom AM2RLAUNCHERDATA env var
string am2rLauncherDataEnvVar = Environment.GetEnvironmentVariable("AM2RLAUNCHERDATA");
if (!String.IsNullOrWhiteSpace(am2rLauncherDataEnvVar))
{
try
{
ProcessStartInfo parameters = new ProcessStartInfo
{
FileName = "xdelta3",
Arguments = arguments,
WorkingDirectory = CURRENTPATH
};
using (Process proc = Process.Start(parameters))
{
proc?.WaitForExit();
}
}
// This will create the directories recursively if they don't exist
Directory.CreateDirectory(am2rLauncherDataEnvVar);
if (originalOutput != output && File.Exists(output))
// Our env var is now set and directories exist
log.Info("CurrentPath is set to " + am2rLauncherDataEnvVar);
return am2rLauncherDataEnvVar;
}
catch (Exception ex)
{
File.Delete(originalOutput);
File.Move(output, originalOutput);
log.Error($"There was an error with '{am2rLauncherDataEnvVar}'!\n{ex.Message} {ex.StackTrace}. Falling back to defaults.");
}
}
public static void RunJavaJar(string arguments = null, string workingDirectory = null)
if (OS.IsWindows)
{
if (workingDirectory == null)
workingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
string proc = "",
javaArgs = "";
if (OS.IsWindows)
{
proc = "cmd";
javaArgs = "/C java -jar";
}
else if (OS.IsUnix)
log.Info("Using default Windows CurrentPath.");
// Windows has the path where the exe is located as default
return Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
}
else if (OS.IsLinux)
{
// First check if XDG_DATA_HOME is set, if not we'll use ~/.local/share
string xdgDataHome = Environment.GetEnvironmentVariable("XDG_DATA_HOME");
if (String.IsNullOrWhiteSpace(xdgDataHome))
{
proc = "java";
javaArgs = "-jar";
log.Info("Using default Linux CurrentPath.");
xdgDataHome = NIXHOME + "/.local/share";
}
ProcessStartInfo jarStart = new ProcessStartInfo
{
FileName = proc,
Arguments = javaArgs + " " + arguments,
WorkingDirectory = workingDirectory,
UseShellExecute = false,
CreateNoWindow = true
};
// Add AM2RLauncher to the end of the dataPath
xdgDataHome += "/AM2RLauncher";
Process jarProcess = new Process
try
{
StartInfo = jarStart
};
jarProcess.Start();
// This will create the directories recursively if they don't exist
Directory.CreateDirectory(xdgDataHome);
jarProcess.WaitForExit();
}
/// <summary>
/// Figures out what the AM2RLauncher's <see cref="CURRENTPATH"/> should be.<br/>
/// Determination is as follows:
/// <list type="number">
/// <item><b>$AM2RLAUNCHERDATA</b> environment variable is read and folders are recursively generated.</item>
/// <item>The current OS is checked. For Windows, the path where the executable is located will be returned.<br/>
/// For Linux, <b>$XDG_DATA_HOME/AM2RLauncher</b> will be returned.
/// Should <b>$XDG_DATA_HOME</b> be empty, it will default to <b>$HOME/.local/share</b>.</item>
/// <item>The path where the executable is located will be returned.</item>
/// </list>
/// Should any errors occur, it falls down to the next step.
/// </summary>
/// <returns></returns>
private static string GenerateCurrentPath()
{
// First, we check if the user has a custom AM2RLAUNCHERDATA env var
string am2rLauncherDataEnvVar = Environment.GetEnvironmentVariable("AM2RLAUNCHERDATA");
if (!String.IsNullOrWhiteSpace(am2rLauncherDataEnvVar))
{
try
{
// This will create the directories recursively if they don't exist
Directory.CreateDirectory(am2rLauncherDataEnvVar);
// Our env var is now set and directories exist
Log.Info("CurrentPath is set to " + am2rLauncherDataEnvVar);
return am2rLauncherDataEnvVar;
}
catch (Exception ex)
{
Log.Error($"There was an error with '{am2rLauncherDataEnvVar}'!\n{ex.Message} {ex.StackTrace}. Falling back to defaults.");
}
// Our env var is now set and directories exist
log.Info("CurrentPath is set to " + xdgDataHome);
return xdgDataHome;
}
if (OS.IsWindows)
catch (Exception ex)
{
Log.Info("Using default Windows CurrentPath.");
// Windows has the path where the exe is located as default
return Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
log.Error($"There was an error with '{xdgDataHome}'!\n{ex.Message} {ex.StackTrace}. Falling back to defaults.");
}
else if (OS.IsLinux)
}
else if (OS.IsMac)
{
//Mac has the Path at HOME/Library/AM2RLauncher
string macPath = NIXHOME + "/Library/AM2RLauncher";
try
{
// First check if XDG_DATA_HOME is set, if not we'll use ~/.local/share
string xdgDataHome = Environment.GetEnvironmentVariable("XDG_DATA_HOME");
if (String.IsNullOrWhiteSpace(xdgDataHome))
{
Log.Info("Using default Linux CurrentPath.");
xdgDataHome = NIXHOME + "/.local/share";
}
// Add AM2RLauncher to the end of the dataPath
xdgDataHome += "/AM2RLauncher";
try
{
// This will create the directories recursively if they don't exist
Directory.CreateDirectory(xdgDataHome);
// Our env var is now set and directories exist
Log.Info("CurrentPath is set to " + xdgDataHome);
return xdgDataHome;
}
catch (Exception ex)
{
Log.Error($"There was an error with '{xdgDataHome}'!\n{ex.Message} {ex.StackTrace}. Falling back to defaults.");
}
Directory.CreateDirectory(macPath);
log.Info("Using default Mac CurrentPath.");
return macPath;
}
else if (OS.IsMac)
catch (Exception ex)
{
//Mac has the Path at HOME/Library/AM2RLauncher
string macPath = NIXHOME + "/Library/AM2RLauncher";
try
{
Directory.CreateDirectory(macPath);
Log.Info("Using default Mac CurrentPath.");
return macPath;
}
catch (Exception ex)
{
Log.Error($"There was an error with '{macPath}'!\n{ex.Message} {ex.StackTrace}. Falling back to defaults.");
}
log.Error($"There was an error with '{macPath}'!\n{ex.Message} {ex.StackTrace}. Falling back to defaults.");
}
else
Log.Error(OS.Name + " has no current path!");
Log.Info("Something went wrong, falling back to the default CurrentPath.");
return Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
}
else
log.Error(OS.Name + " has no current path!");
log.Info("Something went wrong, falling back to the default CurrentPath.");
return Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
}
}
}

@ -1,177 +1,163 @@
using LibGit2Sharp;
using log4net;
using log4net;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
namespace AM2RLauncher.Core
namespace AM2RLauncher.Core;
/// <summary>
/// Class that has various Helper functions. Basically anything that could be used outside the Launcher resides here.
/// </summary>
public static class HelperMethods
{
// Load reference to logger
/// <summary>
/// Class that has various Helper functions. Basically anything that could be used outside the Launcher resides here.
/// Our log object, that handles logging the current execution to a file.
/// </summary>
public static class HelperMethods
private static readonly ILog log = Core.Log;
// Thank you, Microsoft docs: https://docs.microsoft.com/en-us/dotnet/standard/io/how-to-copy-directories
// Slightly modified by adding overwriteFiles bool, as we need to replace readme, music, etc.
/// <summary>
/// This copies the contents of a specified Directory recursively to another Directory.
/// </summary>
/// <param name="sourceDirName">Full Path to the Directory that will be recursively copied.</param>
/// <param name="destDirName">Full Path to the Directory you want to copy to. If the Directory does not exist, it will be created.</param>
/// <param name="overwriteFiles">Specify if Files should be overwritten or not</param>
/// <param name="copySubDirs">Specify if you want to copy Sub-Directories as well.</param>
public static void DirectoryCopy(string sourceDirName, string destDirName, bool overwriteFiles = true, bool copySubDirs = true)
{
// Load reference to logger
/// <summary>
/// Our log object, that handles logging the current execution to a file.
/// </summary>
private static readonly ILog Log = Core.Log;
// Thank you, Microsoft docs: https://docs.microsoft.com/en-us/dotnet/standard/io/how-to-copy-directories
// Slightly modified by adding overwriteFiles bool, as we need to replace readme, music, etc.
/// <summary>
/// This copies the contents of a specified Directory recursively to another Directory.
/// </summary>
/// <param name="sourceDirName">Full Path to the Directory that will be recursively copied.</param>
/// <param name="destDirName">Full Path to the Directory you want to copy to. If the Directory does not exist, it will be created.</param>
/// <param name="overwriteFiles">Specify if Files should be overwritten or not</param>
/// <param name="copySubDirs">Specify if you want to copy Sub-Directories as well.</param>
public static void DirectoryCopy(string sourceDirName, string destDirName, bool overwriteFiles = true, bool copySubDirs = true)
// Get the subdirectories for the specified directory.
DirectoryInfo dir = new DirectoryInfo(sourceDirName);
if (!dir.Exists)
{
// Get the subdirectories for the specified directory.
DirectoryInfo dir = new DirectoryInfo(sourceDirName);
if (!dir.Exists)
{
throw new DirectoryNotFoundException(
"Source directory does not exist or could not be found: "
+ sourceDirName);
}
DirectoryInfo[] dirs = dir.GetDirectories();
// If the destination directory doesn't exist, create it.
Directory.CreateDirectory(destDirName);
// Get the files in the directory and copy them to the new location.
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
string tempPath = Path.Combine(destDirName, file.Name);
file.CopyTo(tempPath, overwriteFiles);
}
// If copying subdirectories, copy them and their contents to new location.
if (!copySubDirs)
return;
foreach (DirectoryInfo subDir in dirs)
{
string tempPath = Path.Combine(destDirName, subDir.Name);
DirectoryCopy(subDir.FullName, tempPath, overwriteFiles);
}
throw new DirectoryNotFoundException(
"Source directory does not exist or could not be found: "
+ sourceDirName);
}
DirectoryInfo[] dirs = dir.GetDirectories();
// If the destination directory doesn't exist, create it.
Directory.CreateDirectory(destDirName);
// Get the files in the directory and copy them to the new location.
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
string tempPath = Path.Combine(destDirName, file.Name);
file.CopyTo(tempPath, overwriteFiles);
}
// If copying subdirectories, copy them and their contents to new location.
if (!copySubDirs)
return;
/// <summary>
/// This is a custom method, that deletes a Directory. The reason this is used, instead of <see cref="Directory.Delete(string)"/>,
/// is because this one sets the attributes of all files to be deletable, while <see cref="Directory.Delete(string)"/> does not do that on it's own.
/// It's needed, because sometimes there are read-only files being generated, that would normally need more code in order to reset the attributes.<br/>
/// Note, that this method acts recursively.
/// </summary>
/// <param name="targetDir">The directory to delete.</param>
public static void DeleteDirectory(string targetDir)
foreach (DirectoryInfo subDir in dirs)
{
if (!Directory.Exists(targetDir)) return;
string tempPath = Path.Combine(destDirName, subDir.Name);
DirectoryCopy(subDir.FullName, tempPath, overwriteFiles);
}
}
File.SetAttributes(targetDir, FileAttributes.Normal);
foreach (string file in Directory.GetFiles(targetDir))
{
File.SetAttributes(file, FileAttributes.Normal);
File.Delete(file);
}
/// <summary>
/// This is a custom method, that deletes a Directory. The reason this is used, instead of <see cref="Directory.Delete(string)"/>,
/// is because this one sets the attributes of all files to be deletable, while <see cref="Directory.Delete(string)"/> does not do that on it's own.
/// It's needed, because sometimes there are read-only files being generated, that would normally need more code in order to reset the attributes.<br/>
/// Note, that this method acts recursively.
/// </summary>
/// <param name="targetDir">The directory to delete.</param>
public static void DeleteDirectory(string targetDir)
{
if (!Directory.Exists(targetDir)) return;
foreach (string dir in Directory.GetDirectories(targetDir))
{
DeleteDirectory(dir);
}
File.SetAttributes(targetDir, FileAttributes.Normal);
Directory.Delete(targetDir, false);
foreach (string file in Directory.GetFiles(targetDir))
{
File.SetAttributes(file, FileAttributes.Normal);
File.Delete(file);
}
/// <summary>
/// Calculates an MD5 hash from a given file.
/// </summary>
/// <param name="filename">Full Path to the file whose MD5 hash is supposed to be calculated.</param>
/// <returns>The MD5 hash as a <see cref="string"/>, empty string if file does not exist.</returns>
public static string CalculateMD5(string filename)
foreach (string dir in Directory.GetDirectories(targetDir))
{
// Check if File exists first
if (!File.Exists(filename))
return "";
using (var stream = File.OpenRead(filename))
{
using (var md5 = MD5.Create())
{
var hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
}
DeleteDirectory(dir);
}
/// <summary>
/// Performs recursive rollover on a set of log files.
/// </summary>
/// <param name="logFile">The log file to begin the rollover from.</param>
/// <param name="max">The maximum amount of log files to retain. Default is 9, as that's the highest digit.</param>
public static void RecursiveRollover(string logFile, int max = 9)
Directory.Delete(targetDir, false);
}
/// <summary>
/// Calculates an MD5 hash from a given file.
/// </summary>
/// <param name="filename">Full Path to the file whose MD5 hash is supposed to be calculated.</param>
/// <returns>The MD5 hash as a <see cref="string"/>, empty string if file does not exist.</returns>
public static string CalculateMD5(string filename)
{
// Check if File exists first
if (!File.Exists(filename))
return "";
using var stream = File.OpenRead(filename);
using var md5 = MD5.Create();
var hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
/// <summary>
/// Performs recursive rollover on a set of log files.
/// </summary>
/// <param name="logFile">The log file to begin the rollover from.</param>
/// <param name="max">The maximum amount of log files to retain. Default is 9, as that's the highest digit.</param>
public static void RecursiveRollover(string logFile, int max = 9)
{
int index = 1;
char endChar = logFile[logFile.Length - 1];
string fileName;
// If not the original file, set the new index and get the new fileName.
if (Char.IsNumber(endChar))
{
int index = 1;
char endChar = logFile[logFile.Length - 1];
string fileName;
// If not the original file, set the new index and get the new fileName.
if (char.IsNumber(endChar))
{
index = int.Parse(endChar.ToString()) + 1;
fileName = logFile.Remove(logFile.Length - 1) + index;
}
else // Otherwise, if the original file, just set fileName to log.txt.1.
fileName = logFile + ".1";
// If new name already exists, run the rollover algorithm on it!
if (File.Exists(fileName))
{
RecursiveRollover(fileName, max);
}
//TODO: this can fail if one doesnt have permissions to move or delete the file
// If index is less than max, rename file.
if (index < max)
{
File.Move(logFile, fileName);
}
else // Otherwise, delete the file.
File.Delete(logFile);
index = Int32.Parse(endChar.ToString()) + 1;
fileName = logFile.Remove(logFile.Length - 1) + index;
}
else // Otherwise, if the original file, just set fileName to log.txt.1.
fileName = logFile + ".1";
// If new name already exists, run the rollover algorithm on it!
if (File.Exists(fileName))
RecursiveRollover(fileName, max);
//TODO: this can fail if one doesnt have permissions to move or delete the file
// If index is less than max, rename file.
if (index < max)
File.Move(logFile, fileName);
else // Otherwise, delete the file.
File.Delete(logFile);
}
/// <summary>
/// Checks if we currently have an internet connection, by pinging github.
/// </summary>
/// <returns><see langword="true"/> if we have internet, <see langword="false"/> if not.</returns>
public static bool IsConnectedToInternet()
/// <summary>
/// Checks if we currently have an internet connection, by pinging github.
/// </summary>
/// <returns><see langword="true"/> if we have internet, <see langword="false"/> if not.</returns>
public static bool IsConnectedToInternet()
{
log.Info("Checking internet connection...");
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://github.com");
try
{
HttpWebResponse _ = (HttpWebResponse)request.GetResponse();
}
catch (WebException)
{
Log.Info("Checking internet connection...");
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://github.com");
try
{
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
}
catch (WebException)
{
Log.Info("Internet connection failed.");
return false;
}
Log.Info("Internet connection established!");
return true;
log.Info("Internet connection failed.");
return false;
}
log.Info("Internet connection established!");
return true;
}
}
}

@ -1,53 +1,49 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Runtime.InteropServices;
namespace AM2RLauncher.Core
namespace AM2RLauncher.Core;
/// <summary>
/// Class that has information about the current running operating system.
/// </summary>
public static class OS
{
/// <summary>
/// Class that has information about the current running operating system.
/// Determines if the current OS is Windows.
/// </summary>
public class OS
{
/// <summary>
/// Determines if the current OS is Windows.
/// </summary>
public static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
public static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
/// <summary>
/// Determines if the current OS is Linux.
/// </summary>
public static readonly bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
/// <summary>
/// Determines if the current OS is Linux.
/// </summary>
public static readonly bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
/// <summary>
/// Determines if the current OS is Mac.
/// </summary>
public static readonly bool IsMac = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
/// <summary>
/// Determines if the current OS is Mac.
/// </summary>
public static readonly bool IsMac = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
/// <summary>
/// Determines if the current OS is a unix based system (Mac or Linux).
/// </summary>
public static readonly bool IsUnix = IsLinux || IsMac;
/// <summary>
/// Determines if the current OS is a unix based system (Mac or Linux).
/// </summary>
public static readonly bool IsUnix = IsLinux || IsMac;
/// <summary>
/// Gets a string representation of the current OS.
/// </summary>
public static readonly string Name = DetermineOSName();
/// <summary>
/// Gets a string representation of the current OS.
/// </summary>
public static readonly string Name = DetermineOsName();
/// <summary>
/// Generates a string representation of the current OS
/// </summary>
private static string DetermineOSName()
{
if (IsWindows)
return "Windows";
else if (IsLinux)
return "Linux";
else if (IsMac)
return "Mac";
else
return "Unknown OS";
}
/// <summary>
/// Generates a string representation of the current OS
/// </summary>
private static string DetermineOsName()
{
if (IsWindows)
return "Windows";
if (IsLinux)
return "Linux";
if (IsMac)
return "Mac";
return "Unknown OS";
}
}
}

File diff suppressed because it is too large Load Diff

@ -1,131 +1,130 @@
using System;
using System.Linq;
namespace AM2RLauncher.Core
namespace AM2RLauncher.Core;
/// <summary>
/// Class only for providing Splashes
/// </summary>
public static class Splash
{
/// <summary>
/// Class only for providing Splashes
/// Cross-Platform splash strings
/// </summary>
public static class Splash
private static readonly string[] generalSplash =
{
/// <summary>
/// Cross-Platform splash strings
/// </summary>
private static readonly string[] generalSplash =
{
"The real Ridley is the friends we made along the way.",
"Now with 100% more Septoggs!",
"Now with 100% more Blob Throwers!",
"Speedrun THIS, I dare you.",
"The broken pipe is a lie.",
"Overcommitting to April Fool's since 2018.",
"Also try Metroid II: Return of Samus!",
"Also try Metroid: Samus Returns!",
"Also try Prime 2D!",
"Trust me, it's an 'unintentional feature.'",
"Coming soon to a PC near you!",
"This ain't your parents' Metroid 2!",
"Now setting boss pattern to S P E E N",
"You S P E E N me right round, baby!",
"Wait, I thought Metroid was a guy!",
"jellyfish is helping yes",
"Why can't Metroid crawl?",
"When in doubt, blame GameMaker.",
"It just works™",
"Reject C++, return to ABSTRACTION",
"C# is just C++++ with Windows support.",
"Use of the Launcher has now been authorized!",
"Oh? So you're patching me?",
"I, Gawron Giovana, have a dream.",
"Oooh weee ooo, I look just like an Omega",
"AH THE MOLE",
"S P I D E R B A L L",
"Fun is infinite with Community-Developers Inc.",
"You may only proceed if you wear baggy pants.",
"Did you know that games take time to develop?"
};
"The real Ridley is the friends we made along the way.",
"Now with 100% more Septoggs!",
"Now with 100% more Blob Throwers!",
"Speedrun THIS, I dare you.",
"The broken pipe is a lie.",
"Overcommitting to April Fool's since 2018.",
"Also try Metroid II: Return of Samus!",
"Also try Metroid: Samus Returns!",
"Also try Prime 2D!",
"Trust me, it's an 'unintentional feature.'",
"Coming soon to a PC near you!",
"This ain't your parents' Metroid 2!",
"Now setting boss pattern to S P E E N",
"You S P E E N me right round, baby!",
"Wait, I thought Metroid was a guy!",
"jellyfish is helping yes",
"Why can't Metroid crawl?",
"When in doubt, blame GameMaker.",
"It just works™",
"Reject C++, return to ABSTRACTION",
"C# is just C++++ with Windows support.",
"Use of the Launcher has now been authorized!",
"Oh? So you're patching me?",
"I, Gawron Giovana, have a dream.",
"Oooh weee ooo, I look just like an Omega",
"AH THE MOLE",
"S P I D E R B A L L",
"Fun is infinite with Community-Developers Inc.",
"You may only proceed if you wear baggy pants.",
"Did you know that games take time to develop?"
};
/// <summary>
/// Windows only splash strings
/// </summary>
private static readonly string[] windowsSplash =
{
":(",
"All your machine are belong to MS"
};
/// <summary>
/// Windows only splash strings
/// </summary>
private static readonly string[] windowsSplash =
{
":(",
"All your machine are belong to MS"
};
/// <summary>
/// Linux only splash strings
/// </summary>
private static readonly string[] linuxSplash =
{
"Sorry this is ugly, but at least it works.",
"GTK + QT = 💣",
"I hope you use Arch, btw",
"All your Ubuntu are belong to Arch btw.",
"Help, how do I quit vim!?",
"The bloat isn't our fault, YoYo Games forced 1GB dependencies!",
"On second thought, maybe the bloat is our fault...",
"The quieter you are, the more Metroids you can hear.",
"What you are referring to as AM2R, is in fact, GNU/AM2R.",
"GNOME be gone!",
"Go compile your own girlfriend. This one doesn't count, she's out of your league.",
"Year 3115, NVIDIA still doesn't support Wayland.",
"Was a mouse really that expensive, i3 users!?",
"Imagine using non-free software.",
"What if... we used a non-FOSS OS? Haha just kidding... Unless?",
"🐧",
"You are already patched 🐸🕶",
"Yes, do as I say!",
"Did you find the penguin yet?"
};
/// <summary>
/// Linux only splash strings
/// </summary>
private static readonly string[] linuxSplash =
{
"Sorry this is ugly, but at least it works.",
"GTK + QT = 💣",
"I hope you use Arch, btw",
"All your Ubuntu are belong to Arch btw.",
"Help, how do I quit vim!?",
"The bloat isn't our fault, YoYo Games forced 1GB dependencies!",
"On second thought, maybe the bloat is our fault...",
"The quieter you are, the more Metroids you can hear.",
"What you are referring to as AM2R, is in fact, GNU/AM2R.",
"GNOME be gone!",
"Go compile your own girlfriend. This one doesn't count, she's out of your league.",
"Year 3115, NVIDIA still doesn't support Wayland.",
"Was a mouse really that expensive, i3 users!?",
"Imagine using non-free software.",
"What if... we used a non-FOSS OS? Haha just kidding... Unless?",
"🐧",
"You are already patched 🐸🕶",
"Yes, do as I say!",
"Did you find the penguin yet?"
};
/// <summary>
/// Mac only splash strings
/// </summary>
private static readonly string[] macSplash =
{
"Does this even work?",
"You weren't supposed to do that!",
"Another Mac 2 Remake",
"This shouldn't even be possible",
"How long until this breaks?",
"Have fun trying to convince modders to support this!"
};
/// <summary>
/// Mac only splash strings
/// </summary>
private static readonly string[] macSplash =
{
"Does this even work?",
"You weren't supposed to do that!",
"Another Mac 2 Remake",
"This shouldn't even be possible",
"How long until this breaks?",
"Have fun trying to convince modders to support this!"
};
/// <summary>
/// Combined splash strings
/// </summary>
private static readonly string[] combinedSplash = combineSplashes();
/// <summary>
/// Combined splash strings
/// </summary>
private static readonly string[] combinedSplash = CombineSplashes();
/// <summary>
/// Get a random splash string, according to the current OS.
/// </summary>
/// <returns>The randomly chosen splash as a <see cref="string"/>.</returns>
public static string GetSplash()
{
Random rng = new Random();
/// <summary>
/// Get a random splash string, according to the current OS.
/// </summary>
/// <returns>The randomly chosen splash as a <see cref="string"/>.</returns>
public static string GetSplash()
{
Random rng = new Random();
string splashString = combinedSplash[rng.Next(0, combinedSplash.Length)];
return splashString;
}
string splashString = combinedSplash[rng.Next(0, combinedSplash.Length)];
return splashString;
}
/// <summary>
/// Creates a string srray which is <see cref="generalSplash"/> concatonated with the splash array for the current OS
/// </summary>
/// <returns>A string array where <see cref="generalSplash"/> and the splash array for the current OS have been concatonated.</returns>
private static string[] combineSplashes()
{
string[] totalSplashes;
if (OS.IsWindows)
totalSplashes = generalSplash.Concat(windowsSplash).ToArray();
else if (OS.IsLinux)
totalSplashes = generalSplash.Concat(linuxSplash).ToArray();
else if (OS.IsMac)
totalSplashes = generalSplash.Concat(macSplash).ToArray();
else
totalSplashes = generalSplash;
return totalSplashes;
}
/// <summary>
/// Creates a string srray which is <see cref="generalSplash"/> concatonated with the splash array for the current OS
/// </summary>
/// <returns>A string array where <see cref="generalSplash"/> and the splash array for the current OS have been concatonated.</returns>
private static string[] CombineSplashes()
{
string[] totalSplashes;
if (OS.IsWindows)
totalSplashes = generalSplash.Concat(windowsSplash).ToArray();
else if (OS.IsLinux)
totalSplashes = generalSplash.Concat(linuxSplash).ToArray();
else if (OS.IsMac)
totalSplashes = generalSplash.Concat(macSplash).ToArray();
else
totalSplashes = generalSplash;
return totalSplashes;
}
}
}

@ -2,153 +2,152 @@
using System.Linq;
using System.Xml.Serialization;
namespace AM2RLauncher.Core.XML
namespace AM2RLauncher.Core.XML;
/// <summary>
/// Class that handles how the Launcher settings are saved as XML. Only affects Linux
/// </summary>
[Serializable]
[XmlRoot("settings")]
public class LauncherConfigXML
{
/// <summary>Indicates wether or not to auto-update the Launcher. Used for <see cref="MainForm.autoUpdateAM2RCheck"/></summary>
[XmlAttribute("AutoUpdateAM2R")]
public bool AutoUpdateAM2R
{ get; set; }
/// <summary>Indicates wether or not to auto-update the Launcher. Used for <see cref="MainForm.autoUpdateLauncherCheck"/></summary>
[XmlAttribute("AutoUpdateLauncher")]
public bool AutoUpdateLauncher
{ get; set; }
/// <summary>Indicates the Language of the Launcher. Used for <see cref="MainForm.languageDropDown"/></summary>
[XmlAttribute("Language")]
public string Language
{ get; set; }
/// <summary>Indicates wether or not to use High-quality music when patching to PC. Used for <see cref="MainForm.hqMusicPCCheck"/></summary>
[XmlAttribute("MusicHQPC")]
public bool MusicHQPC
{ get; set; }
/// <summary>Indicates wether or not to use High-quality music when patching to Android. Used for <see cref="MainForm.hqMusicAndroidCheck"/></summary>
[XmlAttribute("MusicHQAndroid")]
public bool MusicHQAndroid
{ get; set; }
/// <summary>Indicates the index for <see cref="MainForm.mirrorDropDown"/>.</summary>
[XmlAttribute("MirrorIndex")]
public int MirrorIndex
{ get; set; }
/// <summary>Indicates the index for <see cref="MainForm.profileDropDown"/>.</summary>
[XmlAttribute("ProfileIndex")]
public string ProfileIndex
{ get; set; }
/// <summary>Indicates wether or not to have custom mirrors enabled. Used for <see cref="MainForm.customMirrorCheck"/></summary>
[XmlAttribute("CustomMirrorEnabled")]
public bool CustomMirrorEnabled
{ get; set; }
/// <summary>Indicates the custom mirror as a text. Used for <see cref="MainForm.customMirrorTextBox"/></summary>
[XmlAttribute("CustomMirrorText")]
public string CustomMirrorText
{ get; set; }
/// <summary>Indicates whether or not to create debug logs of profile. Used for <see cref="MainForm.profileDebugLogCheck"/></summary>
[XmlAttribute("ProfileDebugLog")]
public bool ProfileDebugLog
{ get; set; }
/// <summary>Indicates the custom environment variable(s) as text. Used for <see cref="MainForm.customEnvVarTextBox"/></summary>
[XmlAttribute("CustomEnvVar")]
public string CustomEnvVar
{ get; set; }
/// <summary>Indicates the Width of the Launcher.</summary>
[XmlAttribute("Width")]
public int Width
{ get; set; }
/// <summary>Indicates the Height of the Launcher.</summary>
[XmlAttribute("Height")]
public int Height
{ get; set; }
/// <summary>Indicates wether or not the Launcher is maximized or not.</summary>
[XmlAttribute("IsMaximized")]
public bool IsMaximized
{ get; set; }
// Huge help from James for all of this!!!
// Here's a short explanation of this. Basically, what we do is create an indexer. This makes it possible for this class to be indexed, like this LauncherConfigXML[i]
// The indexer here has a get and set "submethod". And both use Reflection to get the current class as something that can be used with lambda values
// So for get we just lambda the properties and search for something that can be read and has the same name as the input property. After that, we just get the value and return it as a string
// Set is basiaclly the same, but instead of returning it, we set the prtoperty value, which would be followed after the `='.
// So LauncherConfigXML[property] = hellWorld would set the value of `property` to `hellWorld`
/// <summary>
/// Class that handles how the Launcher settings are saved as XML. Only affects Linux
/// An Indexer for <see cref="ProfileXML"/>. Not to be used directly, use <see cref="CrossPlatformOperations.WriteToConfig(string, object)"/>
/// or <see cref="CrossPlatformOperations.ReadFromConfig(string)"/> instead!
/// </summary>
[Serializable]
[XmlRoot("settings")]
public class LauncherConfigXML
/// <param name="property">The property to get or set.</param>
/// <returns>The value of <paramref name="property"/> as an <see cref="object"/> if used as a get, <see cref="Void"/> if used as a set.</returns>
public object this[string property]
{
/// <summary>Indicates wether or not to auto-update the Launcher. Used for <see cref="MainForm.autoUpdateAM2RCheck"/></summary>
[XmlAttribute("AutoUpdateAM2R")]
public bool AutoUpdateAM2R
{ get; set; }
/// <summary>Indicates wether or not to auto-update the Launcher. Used for <see cref="MainForm.autoUpdateLauncherCheck"/></summary>
[XmlAttribute("AutoUpdateLauncher")]
public bool AutoUpdateLauncher
{ get; set; }
/// <summary>Indicates the Language of the Launcher. Used for <see cref="MainForm.languageDropDown"/></summary>
[XmlAttribute("Language")]
public string Language
{ get; set; }
/// <summary>Indicates wether or not to use High-quality music when patching to PC. Used for <see cref="MainForm.hqMusicPCCheck"/></summary>
[XmlAttribute("MusicHQPC")]
public bool MusicHQPC
{ get; set; }
/// <summary>Indicates wether or not to use High-quality music when patching to Android. Used for <see cref="MainForm.hqMusicAndroidCheck"/></summary>
[XmlAttribute("MusicHQAndroid")]
public bool MusicHQAndroid
{ get; set; }
/// <summary>Indicates the index for <see cref="MainForm.mirrorDropDown"/>.</summary>
[XmlAttribute("MirrorIndex")]
public int MirrorIndex
{ get; set; }
/// <summary>Indicates the index for <see cref="MainForm.profileDropDown"/>.</summary>
[XmlAttribute("ProfileIndex")]
public string ProfileIndex
{ get; set; }
/// <summary>Indicates wether or not to have custom mirrors enabled. Used for <see cref="MainForm.customMirrorCheck"/></summary>
[XmlAttribute("CustomMirrorEnabled")]
public bool CustomMirrorEnabled
{ get; set; }
/// <summary>Indicates the custom mirror as a text. Used for <see cref="MainForm.customMirrorTextBox"/></summary>
[XmlAttribute("CustomMirrorText")]
public string CustomMirrorText
{ get; set; }
/// <summary>Indicates whether or not to create debug logs of profile. Used for <see cref="MainForm.profileDebugLogCheck"/></summary>
[XmlAttribute("ProfileDebugLog")]
public bool ProfileDebugLog
{ get; set; }
/// <summary>Indicates the custom environment variable(s) as text. Used for <see cref="MainForm.customEnvVarTextBox"/></summary>
[XmlAttribute("CustomEnvVar")]
public string CustomEnvVar
{ get; set; }
/// <summary>Indicates the Width of the Launcher.</summary>
[XmlAttribute("Width")]
public int Width
{ get; set; }
/// <summary>Indicates the Height of the Launcher.</summary>
[XmlAttribute("Height")]
public int Height
{ get; set; }
/// <summary>Indicates wether or not the Launcher is maximized or not.</summary>
[XmlAttribute("IsMaximized")]
public bool IsMaximized
{ get; set; }
// Huge help from James for all of this!!!
// Here's a short explanation of this. Basically, what we do is create an indexer. This makes it possible for this class to be indexed, like this LauncherConfigXML[i]
// The indexer here has a get and set "submethod". And both use Reflection to get the current class as something that can be used with lambda values
// So for get we just lambda the properties and search for something that can be read and has the same name as the input property. After that, we just get the value and return it as a string
// Set is basiaclly the same, but instead of returning it, we set the prtoperty value, which would be followed after the `='.
// So LauncherConfigXML[property] = hellWorld would set the value of `property` to `hellWorld`
/// <summary>
/// An Indexer for <see cref="ProfileXML"/>. Not to be used directly, use <see cref="CrossPlatformOperations.WriteToConfig(string, object)"/>
/// or <see cref="CrossPlatformOperations.ReadFromConfig(string)"/> instead!
/// </summary>
/// <param name="property">The property to get or set.</param>
/// <returns>The value of <paramref name="property"/> as an <see cref="object"/> if used as a get, <see cref="Void"/> if used as a set.</returns>
public object this[string property]
{
get
{
// This is gonna throw an exception, if the property can't be found. because of null.GetValue(this)
return typeof(LauncherConfigXML).GetProperties().First(p => p.CanRead && p.Name == property).GetValue(this).ToString();
}
set
{
typeof(LauncherConfigXML).GetProperties().First(p => p.CanWrite && p.Name == property).SetValue(this, value);
}
}
get
{
// This is gonna throw an exception, if the property can't be found. because of null.GetValue(this)
return typeof(LauncherConfigXML).GetProperties().First(p => p.CanRead && p.Name == property).GetValue(this).ToString();
}
set
{
typeof(LauncherConfigXML).GetProperties().First(p => p.CanWrite && p.Name == property).SetValue(this, value);
}
}
/// <summary>
/// Creates a <see cref="LauncherConfigXML"/> with a default set of attributes.
/// </summary>
public LauncherConfigXML()
{
AutoUpdateAM2R = true;
AutoUpdateLauncher = true;
Language = "Default";
MusicHQPC = true;
MusicHQAndroid = false;
ProfileIndex = "null";
MirrorIndex = 0;
CustomMirrorEnabled = false;
CustomMirrorText = "";
ProfileDebugLog = true;
CustomEnvVar = "";
Width = 600;
Height = 600;
IsMaximized = false;
}
/// <summary>
/// Creates a <see cref="LauncherConfigXML"/> with a default set of attributes.
/// </summary>
public LauncherConfigXML()
{
AutoUpdateAM2R = true;
AutoUpdateLauncher = true;
Language = "Default";
MusicHQPC = true;
MusicHQAndroid = false;
ProfileIndex = "null";
MirrorIndex = 0;
CustomMirrorEnabled = false;
CustomMirrorText = "";
ProfileDebugLog = true;
CustomEnvVar = "";
Width = 600;
Height = 600;
IsMaximized = false;
}
/// <summary>
/// Creates a <see cref="LauncherConfigXML"/> with custom attributes.
/// </summary>
/// <param name="autoUpdateAM2R">Paramater that indicates if <see cref="MainForm.autoUpdateAM2RCheck"/> is enabled or not.</param>
/// <param name="autoUpdateLauncher">Paramater that indicates if <see cref="MainForm.autoUpdateLauncherCheck"/> is enabled or not.</param>
/// <param name="language">Parameter that indicates the language of the launcher.</param>
/// <param name="musicHQPC">Parameter that indicates if <see cref="MainForm.hqMusicPCCheck"/> is enabled or not.</param>
/// <param name="musicHQAndroid">Parameter that indicates if <see cref="MainForm.hqMusicAndroidCheck"/> is enabled or not.</param>
/// <param name="profileIndex">Parameter that saves the index of the selected profile of <see cref="MainForm.profileDropDown"/>.</param>
/// <param name="mirrorIndex">Parameter that saves the index of the selected mirror in <see cref="MainForm.mirrorDropDown"/>.</param>
/// <param name="customEnvVar">Parameter that saves custom Environment variables that will be used on Linux for launching a game.</param>
/// <param name="customMirrorEnabled">Parameter that indicates if <see cref="MainForm.customMirrorCheck"/> is enabled or not.</param>
/// <param name="profileDebugLog">Parameter that indicates if <see cref="MainForm.profileDebugLogCheck"/> is enabled or not.</param>
/// <param name="customMirrorText">Parameter that's used for <see cref="MainForm.customMirrorTextBox"/>.</param>
/// <param name="width">Parameter that indicates the width of <see cref="MainForm"/>.</param>
/// <param name="height">Parameter that indicates the height of <see cref="MainForm"/>.</param>
/// <param name="isMaximized">Parameter that indicates if <see cref="MainForm"/> has been set to fullscreen or not.</param>
public LauncherConfigXML(bool autoUpdateAM2R, bool autoUpdateLauncher, string language, bool musicHQPC, bool musicHQAndroid,
string profileIndex, int mirrorIndex, bool profileDebugLog, string customEnvVar, bool customMirrorEnabled,
string customMirrorText, int width, int height, bool isMaximized)
{
AutoUpdateAM2R = autoUpdateAM2R;
AutoUpdateLauncher = autoUpdateLauncher;
Language = language;
MusicHQPC = musicHQPC;
MusicHQAndroid = musicHQAndroid;
ProfileIndex = profileIndex;
MirrorIndex = mirrorIndex;
CustomMirrorEnabled = customMirrorEnabled;
CustomMirrorText = customMirrorText;
ProfileDebugLog = profileDebugLog;
CustomEnvVar = customEnvVar;
Width = width;
Height = height;
IsMaximized = isMaximized;
}
/// <summary>
/// Creates a <see cref="LauncherConfigXML"/> with custom attributes.
/// </summary>
/// <param name="autoUpdateAM2R">Paramater that indicates if <see cref="MainForm.autoUpdateAM2RCheck"/> is enabled or not.</param>
/// <param name="autoUpdateLauncher">Paramater that indicates if <see cref="MainForm.autoUpdateLauncherCheck"/> is enabled or not.</param>
/// <param name="language">Parameter that indicates the language of the launcher.</param>
/// <param name="musicHQPC">Parameter that indicates if <see cref="MainForm.hqMusicPCCheck"/> is enabled or not.</param>
/// <param name="musicHQAndroid">Parameter that indicates if <see cref="MainForm.hqMusicAndroidCheck"/> is enabled or not.</param>
/// <param name="profileIndex">Parameter that saves the index of the selected profile of <see cref="MainForm.profileDropDown"/>.</param>
/// <param name="mirrorIndex">Parameter that saves the index of the selected mirror in <see cref="MainForm.mirrorDropDown"/>.</param>
/// <param name="customEnvVar">Parameter that saves custom Environment variables that will be used on Linux for launching a game.</param>
/// <param name="customMirrorEnabled">Parameter that indicates if <see cref="MainForm.customMirrorCheck"/> is enabled or not.</param>
/// <param name="profileDebugLog">Parameter that indicates if <see cref="MainForm.profileDebugLogCheck"/> is enabled or not.</param>
/// <param name="customMirrorText">Parameter that's used for <see cref="MainForm.customMirrorTextBox"/>.</param>
/// <param name="width">Parameter that indicates the width of <see cref="MainForm"/>.</param>
/// <param name="height">Parameter that indicates the height of <see cref="MainForm"/>.</param>
/// <param name="isMaximized">Parameter that indicates if <see cref="MainForm"/> has been set to fullscreen or not.</param>
public LauncherConfigXML(bool autoUpdateAM2R, bool autoUpdateLauncher, string language, bool musicHQPC, bool musicHQAndroid,
string profileIndex, int mirrorIndex, bool profileDebugLog, string customEnvVar, bool customMirrorEnabled,
string customMirrorText, int width, int height, bool isMaximized)
{
AutoUpdateAM2R = autoUpdateAM2R;
AutoUpdateLauncher = autoUpdateLauncher;
Language = language;
MusicHQPC = musicHQPC;
MusicHQAndroid = musicHQAndroid;
ProfileIndex = profileIndex;
MirrorIndex = mirrorIndex;
CustomMirrorEnabled = customMirrorEnabled;
CustomMirrorText = customMirrorText;
ProfileDebugLog = profileDebugLog;
CustomEnvVar = customEnvVar;
Width = width;
Height = height;
IsMaximized = isMaximized;
}
}
}

@ -1,99 +1,98 @@
using System;
using System.Xml.Serialization;
namespace AM2RLauncher.Core.XML
namespace AM2RLauncher.Core.XML;
/// <summary>
/// Class that handles how the mod settings are saved as XML.
/// </summary>
[Serializable]
[XmlRoot("message")]
public class ProfileXML
{
/// <summary>Indicates the Operating system the mod was made for.</summary>
[XmlAttribute("OperatingSystem")]
public string OperatingSystem
{ get; set; }
/// <summary>Indicates the xml version the mod was made in.</summary>
[XmlAttribute("XMLVersion")]
public int XMLVersion
{ get; set; }
/// <summary>Indicates the version of the mod.</summary>
[XmlAttribute("Version")]
public string Version
{ get; set; }
/// <summary>Indicates the mod's name.</summary>
[XmlAttribute("Name")]
public string Name
{ get; set; }
/// <summary>Indicates the mod's author.</summary>
[XmlAttribute("Author")]
public string Author
{ get; set; }
/// <summary>Indicates wether or not the mod uses custom music.</summary>
[XmlAttribute("UsesCustomMusic")]
public bool UsesCustomMusic
{ get; set; }
/// <summary>Indicates the save location of the mod.</summary>
[XmlAttribute("SaveLocation")]
public string SaveLocation
{ get; set; }
/// <summary>Indicates wether or not the mod supports Android.</summary>
[XmlAttribute("SupportsAndroid")]
public bool SupportsAndroid
{ get; set; }
/// <summary>Indicates wether or not the mod was compiled with YYC.</summary>
[XmlAttribute("UsesYYC")]
public bool UsesYYC
{ get; set; }
/// <summary>Indicates if the mod is installable. This is only <see langword="false"/> for archival community updates mods.</summary>
[XmlAttribute("Installable")]
public bool Installable
{ get; set; }
/// <summary>Indicates any notes that the mod author deemed worthy to share about his mod.</summary>
[XmlAttribute("ProfileNotes")]
public string ProfileNotes
{ get; set; }
/// <summary>This gets calculated at runtime, by <see cref="MainForm.LoadProfiles"/>. Indicates where the install data for the mod is stored.</summary>
[XmlIgnore]
public string DataPath
{ get; set; }
/// <summary>Creates a <see cref="ProfileXML"/> with a default set of attributes.</summary>
public ProfileXML()
{
Installable = true;
}
/// <summary>
/// Class that handles how the mod settings are saved as XML.
/// Creates a <see cref="ProfileXML"/> with a custom set of attributes.
/// </summary>
[Serializable]
[XmlRoot("message")]
public class ProfileXML
/// <param name="operatingSystem">The operating system the mod was made on.</param>
/// <param name="xmlVersion">The xml version the mod was created with.</param>
/// <param name="version">The version of the mod.</param>
/// <param name="name">The mod name.</param>
/// <param name="author">The mod author.</param>
/// <param name="usesCustomMusic">Wether or not the mod uses custom music.</param>
/// <param name="saveLocation">The save location of the mod.</param>
/// <param name="android">Wether or not the mod works for android.</param>
/// <param name="usesYYC">Wether or not the mod was made with YYC.</param>
/// <param name="installable">Wether or not the mod is installable.</param>
/// <param name="profileNotes">The notes of the mod.</param>
public ProfileXML(string operatingSystem, int xmlVersion, string version, string name, string author,
bool usesCustomMusic, string saveLocation, bool android, bool usesYYC,
bool installable, string profileNotes)
{
/// <summary>Indicates the Operating system the mod was made for.</summary>
[XmlAttribute("OperatingSystem")]
public string OperatingSystem
{ get; set; }
/// <summary>Indicates the xml version the mod was made in.</summary>
[XmlAttribute("XMLVersion")]
public int XMLVersion
{ get; set; }
/// <summary>Indicates the version of the mod.</summary>
[XmlAttribute("Version")]
public string Version
{ get; set; }
/// <summary>Indicates the mod's name.</summary>
[XmlAttribute("Name")]
public string Name
{ get; set; }
/// <summary>Indicates the mod's author.</summary>
[XmlAttribute("Author")]
public string Author
{ get; set; }
/// <summary>Indicates wether or not the mod uses custom music.</summary>
[XmlAttribute("UsesCustomMusic")]
public bool UsesCustomMusic
{ get; set; }
/// <summary>Indicates the save location of the mod.</summary>
[XmlAttribute("SaveLocation")]
public string SaveLocation
{ get; set; }
/// <summary>Indicates wether or not the mod supports Android.</summary>
[XmlAttribute("SupportsAndroid")]
public bool SupportsAndroid
{ get; set; }
/// <summary>Indicates wether or not the mod was compiled with YYC.</summary>
[XmlAttribute("UsesYYC")]
public bool UsesYYC
{ get; set; }
/// <summary>Indicates if the mod is installable. This is only <see langword="false"/> for archival community updates mods.</summary>
[XmlAttribute("Installable")]
public bool Installable
{ get; set; }
/// <summary>Indicates any notes that the mod author deemed worthy to share about his mod.</summary>
[XmlAttribute("ProfileNotes")]
public string ProfileNotes
{ get; set; }
/// <summary>This gets calculated at runtime, by <see cref="MainForm.LoadProfiles"/>. Indicates where the install data for the mod is stored.</summary>
[XmlIgnore]
public string DataPath
{ get; set; }
/// <summary>Creates a <see cref="ProfileXML"/> with a default set of attributes.</summary>
public ProfileXML()
{
Installable = true;
}
/// <summary>
/// Creates a <see cref="ProfileXML"/> with a custom set of attributes.
/// </summary>
/// <param name="operatingSystem">The operating system the mod was made on.</param>
/// <param name="xmlVersion">The xml version the mod was created with.</param>
/// <param name="version">The version of the mod.</param>
/// <param name="name">The mod name.</param>
/// <param name="author">The mod author.</param>
/// <param name="usesCustomMusic">Wether or not the mod uses custom music.</param>
/// <param name="saveLocation">The save location of the mod.</param>
/// <param name="android">Wether or not the mod works for android.</param>
/// <param name="usesYYC">Wether or not the mod was made with YYC.</param>
/// <param name="installable">Wether or not the mod is installable.</param>
/// <param name="profileNotes">The notes of the mod.</param>
public ProfileXML(string operatingSystem, int xmlVersion, string version, string name, string author,
bool usesCustomMusic, string saveLocation, bool android, bool usesYYC,
bool installable, string profileNotes)
{
OperatingSystem = operatingSystem;
XMLVersion = xmlVersion;
Version = version;
Name = name;
Author = author;
UsesCustomMusic = usesCustomMusic;
SaveLocation = saveLocation;
SupportsAndroid = android;
UsesYYC = usesYYC;
Installable = installable;
ProfileNotes = profileNotes;
}
OperatingSystem = operatingSystem;
XMLVersion = xmlVersion;
Version = version;
Name = name;
Author = author;
UsesCustomMusic = usesCustomMusic;
SaveLocation = saveLocation;
SupportsAndroid = android;
UsesYYC = usesYYC;
Installable = installable;
ProfileNotes = profileNotes;
}
}
}

@ -2,51 +2,48 @@
using System.IO;
using System.Text;
namespace AM2RLauncher.Core.XML
namespace AM2RLauncher.Core.XML;
/// <summary>
/// The <c>Serializer</c> class, that serializes to and deserializes from XML files.
/// </summary>
public static class Serializer
{
/// <summary>
/// The <c>Serializer</c> class, that serializes to and deserializes from XML files.
/// Serializes <paramref name="item"/> as a <typeparamref name="T"/> to XML.
/// </summary>
public static class Serializer
/// <typeparam name="T">The class to serialize to.</typeparam>
/// <param name="item">The object that will be serialized.</param>
/// <returns>The serialized XML as a <see cref="string"/>.</returns>
public static string Serialize<T>(object item)
{
/// <summary>
/// Serializes <paramref name="item"/> as a <typeparamref name="T"/> to XML.
/// </summary>
/// <typeparam name="T">The class to serialize to.</typeparam>
/// <param name="item">The object that will be serialized.</param>
/// <returns>The serialized XML as a <see cref="string"/>.</returns>
public static string Serialize<T>(object item)
{
Type t = typeof(T);
MemoryStream memStream = new MemoryStream();
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(t);
Type t = typeof(T);
MemoryStream memStream = new MemoryStream();
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(t);
serializer.Serialize(memStream, item);
serializer.Serialize(memStream, item);
string xml = Encoding.UTF8.GetString(memStream.ToArray());
string xml = Encoding.UTF8.GetString(memStream.ToArray());
memStream.Flush();
memStream.Close();
memStream.Dispose();
memStream = null;
memStream.Flush();
memStream.Close();
memStream.Dispose();
memStream = null;
return xml;
}
return xml;
}
/// <summary>
/// Deserialize <paramref name="xmlString"/> into an object of class <typeparamref name="T"/> that can be assigned.
/// </summary>
/// <typeparam name="T">The class that <paramref name="xmlString"/> will be deserialized to.</typeparam>
/// <param name="xmlString">An XML <see cref="string"/> that will be deserialized.</param>
/// <returns>A deserialized object of class <typeparamref name="T"/> from <paramref name="xmlString"/>.</returns>
public static T Deserialize<T>(string xmlString)
{
Type t = typeof(T);
using (MemoryStream memStream = new MemoryStream(Encoding.UTF8.GetBytes(xmlString)))
{
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(t);
return (T)serializer.Deserialize(memStream);
}
}
/// <summary>
/// Deserialize <paramref name="xmlString"/> into an object of class <typeparamref name="T"/> that can be assigned.
/// </summary>
/// <typeparam name="T">The class that <paramref name="xmlString"/> will be deserialized to.</typeparam>
/// <param name="xmlString">An XML <see cref="string"/> that will be deserialized.</param>
/// <returns>A deserialized object of class <typeparamref name="T"/> from <paramref name="xmlString"/>.</returns>
public static T Deserialize<T>(string xmlString)
{
Type t = typeof(T);
using MemoryStream memStream = new MemoryStream(Encoding.UTF8.GetBytes(xmlString));
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(t);
return (T)serializer.Deserialize(memStream);
}
}
Loading…
Cancel
Save