From 0b11bc65366f177e9bd4c129dfd00d9c4e4d13bb Mon Sep 17 00:00:00 2001 From: Miepee <38186597+Miepee@users.noreply.github.com> Date: Mon, 3 Jan 2022 15:42:32 +0100 Subject: [PATCH] Initial mac support --- .gitignore | 3 + AM2RLauncher/AM2RLauncher.Gtk/Program.cs | 5 +- .../AM2RLauncher.Mac/AM2RLauncher.Mac.csproj | 43 ++++++ AM2RLauncher/AM2RLauncher.Mac/Icon.icns | Bin 0 -> 29374 bytes AM2RLauncher/AM2RLauncher.Mac/Info.plist | 20 +++ AM2RLauncher/AM2RLauncher.Mac/Program.cs | 83 +++++++++++ .../Properties/Resources.Designer.cs | 56 +++++++ .../Properties/Resources.resx | 139 ++++++++++++++++++ AM2RLauncher/AM2RLauncher.sln | 6 + .../AM2RLauncher/CrossPlatformOperations.cs | 89 +++++++++-- AM2RLauncher/AM2RLauncher/LauncherUpdater.cs | 1 + .../AM2RLauncher/MainForm/HelperMethods.cs | 1 + .../AM2RLauncher/MainForm/MainForm.Events.cs | 13 +- .../AM2RLauncher/MainForm/MainForm.Methods.cs | 122 +++++++++++++-- .../AM2RLauncher/MainForm/MainForm.UI.cs | 34 +++-- .../AM2RLauncher/XML/LauncherConfigXML.cs | 1 + 16 files changed, 577 insertions(+), 39 deletions(-) create mode 100644 AM2RLauncher/AM2RLauncher.Mac/AM2RLauncher.Mac.csproj create mode 100644 AM2RLauncher/AM2RLauncher.Mac/Icon.icns create mode 100644 AM2RLauncher/AM2RLauncher.Mac/Info.plist create mode 100644 AM2RLauncher/AM2RLauncher.Mac/Program.cs create mode 100644 AM2RLauncher/AM2RLauncher.Mac/Properties/Resources.Designer.cs create mode 100644 AM2RLauncher/AM2RLauncher.Mac/Properties/Resources.resx diff --git a/.gitignore b/.gitignore index fb2c22e..c6244ef 100644 --- a/.gitignore +++ b/.gitignore @@ -352,3 +352,6 @@ MigrationBackup/ # Visual Studio Code folder .vscode/ + +# Mac's DS_Stores +.DS_Store diff --git a/AM2RLauncher/AM2RLauncher.Gtk/Program.cs b/AM2RLauncher/AM2RLauncher.Gtk/Program.cs index c3652c7..8417535 100644 --- a/AM2RLauncher/AM2RLauncher.Gtk/Program.cs +++ b/AM2RLauncher/AM2RLauncher.Gtk/Program.cs @@ -56,7 +56,10 @@ namespace AM2RLauncher.Gtk private static void GTKLauncher_UnhandledException(object sender, Eto.UnhandledExceptionEventArgs e) { log.Error("An unhandled exception has occurred: \n*****Stack Trace*****\n\n" + e.ExceptionObject.ToString()); - MessageBox.Show(Language.Text.UnhandledException + "\n*****Stack Trace*****\n\n" + e.ExceptionObject.ToString(), "GTK", MessageBoxType.Error); + Application.Instance.Invoke(new Action(() => + { + MessageBox.Show(Language.Text.UnhandledException + "\n*****Stack Trace*****\n\n" + e.ExceptionObject.ToString(), "GTK", MessageBoxType.Error); + })); } // This is a duplicate of CrossPlatformOperations.GenerateCurrentPath, because trying to invoke that would cause a crash due to currentPlatform not being initialized. diff --git a/AM2RLauncher/AM2RLauncher.Mac/AM2RLauncher.Mac.csproj b/AM2RLauncher/AM2RLauncher.Mac/AM2RLauncher.Mac.csproj new file mode 100644 index 0000000..c95dd8b --- /dev/null +++ b/AM2RLauncher/AM2RLauncher.Mac/AM2RLauncher.Mac.csproj @@ -0,0 +1,43 @@ + + + + WinExe + net6.0 + osx-x64 + LatestMajor + + + + 4 + + + + + + + + + + + + True + True + Resources.resx + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + + + + + diff --git a/AM2RLauncher/AM2RLauncher.Mac/Icon.icns b/AM2RLauncher/AM2RLauncher.Mac/Icon.icns new file mode 100644 index 0000000000000000000000000000000000000000..f84187f0c90c83682e51719b7ad72543a4f5dabc GIT binary patch literal 29374 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 literal 0 HcmV?d00001 diff --git a/AM2RLauncher/AM2RLauncher.Mac/Info.plist b/AM2RLauncher/AM2RLauncher.Mac/Info.plist new file mode 100644 index 0000000..130f17d --- /dev/null +++ b/AM2RLauncher/AM2RLauncher.Mac/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleName + AM2RLauncher + CFBundleIdentifier + com.example.AM2RLauncher + CFBundleShortVersionString + 1.0 + LSMinimumSystemVersion + 10.12 + CFBundleDevelopmentRegion + en + NSHumanReadableCopyright + + CFBundleIconFile + Icon.icns + + diff --git a/AM2RLauncher/AM2RLauncher.Mac/Program.cs b/AM2RLauncher/AM2RLauncher.Mac/Program.cs new file mode 100644 index 0000000..6cce445 --- /dev/null +++ b/AM2RLauncher/AM2RLauncher.Mac/Program.cs @@ -0,0 +1,83 @@ +using Eto.Forms; +using log4net; +using log4net.Config; +using System; +using System.IO; +using System.Reflection; + +namespace AM2RLauncher.Mac +{ + /// + /// The main class for the Mac project. + /// + class MainClass + { + /// + /// The logger for , used to write any caught exceptions. + /// + private static readonly ILog log = LogManager.GetLogger(typeof(MainForm)); + + /// + /// The main method for the Mac project. + /// + [STAThread] + public static void Main(string[] args) + { + string launcherDataPath = GenerateCurrentPath(); + + // Make sure first, ~/.local/share/AM2RLauncher exists + if (!Directory.Exists(launcherDataPath)) + Directory.CreateDirectory(launcherDataPath); + + // Now, see if log4netConfig exists, if not write it again. + if (!File.Exists(launcherDataPath + "/log4net.config")) + File.WriteAllText(launcherDataPath + "/log4net.config", Properties.Resources.log4netContents.Replace("${DATADIR}", launcherDataPath)); + + // Configure logger + XmlConfigurator.Configure(new FileInfo(launcherDataPath + "/log4net.config")); + + try + { + Application MacLauncher = new Application(Eto.Platforms.Mac64); + LauncherUpdater.Main(); + MacLauncher.UnhandledException += MacLauncher_UnhandledException; + MacLauncher.Run(new MainForm()); + } + catch (Exception e) + { + log.Error("An unhandled exception has occurred: \n*****Stack Trace*****\n\n" + e.StackTrace.ToString()); + Console.WriteLine(Language.Text.UnhandledException + "\n" + e.Message + "\n*****Stack Trace*****\n\n" + e.StackTrace.ToString()); + Console.WriteLine("Check the logs at " + launcherDataPath + " for more info!"); + } + //new Application(Eto.Platforms.Mac64).Run(new MainForm()); + } + + /// + /// This method gets fired when an unhandled excpetion occurs in . + /// + private static void MacLauncher_UnhandledException(object sender, Eto.UnhandledExceptionEventArgs e) + { + log.Error("An unhandled exception has occurred: \n*****Stack Trace*****\n\n" + e.ExceptionObject.ToString()); + /*Application.Instance.Invoke(new Action(() => + { + MessageBox.Show(Language.Text.UnhandledException + "\n*****Stack Trace*****\n\n" + e.ExceptionObject.ToString(), "Mac", MessageBoxType.Error); + }));*/ + } + + // This is a duplicate of CrossPlatformOperations.GenerateCurrentPath, because trying to invoke that would cause a crash due to currentPlatform not being initialized. + private static string GenerateCurrentPath() + { + string NIXHOME = Environment.GetEnvironmentVariable("HOME"); + //Mac has the Path at HOME/Library/AM2RLauncher + string macPath = NIXHOME + "/Library/AM2RLauncher"; + try + { + Directory.CreateDirectory(macPath); + return macPath; + } + catch { } + + return Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory); + } + } +} diff --git a/AM2RLauncher/AM2RLauncher.Mac/Properties/Resources.Designer.cs b/AM2RLauncher/AM2RLauncher.Mac/Properties/Resources.Designer.cs new file mode 100644 index 0000000..641e315 --- /dev/null +++ b/AM2RLauncher/AM2RLauncher.Mac/Properties/Resources.Designer.cs @@ -0,0 +1,56 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace AM2RLauncher.Mac.Properties { + using System; + using System.Reflection; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [System.Diagnostics.DebuggerNonUserCodeAttribute()] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static System.Resources.ResourceManager resourceMan; + + private static System.Globalization.CultureInfo resourceCulture; + + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Resources.ResourceManager ResourceManager { + get { + if (object.Equals(null, resourceMan)) { + System.Resources.ResourceManager temp = new System.Resources.ResourceManager("AM2RLauncher.Mac.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static string log4netContents { + get { + return ResourceManager.GetString("log4netContents", resourceCulture); + } + } + } +} diff --git a/AM2RLauncher/AM2RLauncher.Mac/Properties/Resources.resx b/AM2RLauncher/AM2RLauncher.Mac/Properties/Resources.resx new file mode 100644 index 0000000..a2cff8e --- /dev/null +++ b/AM2RLauncher/AM2RLauncher.Mac/Properties/Resources.resx @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + <log4net> + <root> + <level value="ALL" /> + <appender-ref ref="file" /> + </root> + <appender name="file" type="log4net.Appender.RollingFileAppender"> + <file value="${DATADIR}/Logs/AM2RLauncher.log" /> + <appendToFile value="true" /> + <rollingStyle value="Once" /> + <maxSizeRollBackups value="7" /> + <maximumFileSize value="3MB" /> + <staticLogFileName value="true" /> + <layout type="log4net.Layout.PatternLayout"> + <conversionPattern value="%date [%thread] %level %logger - %message%newline" /> + </layout> + </appender> +</log4net> + + \ No newline at end of file diff --git a/AM2RLauncher/AM2RLauncher.sln b/AM2RLauncher/AM2RLauncher.sln index 836a33c..e616f49 100644 --- a/AM2RLauncher/AM2RLauncher.sln +++ b/AM2RLauncher/AM2RLauncher.sln @@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AM2RLauncher.Gtk", "AM2RLau EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AM2RLauncher.Wpf", "AM2RLauncher.Wpf\AM2RLauncher.Wpf.csproj", "{8A162932-BAA3-429A-84C4-B93A1C85DDE3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AM2RLauncher.Mac", "AM2RLauncher.Mac\AM2RLauncher.Mac.csproj", "{6F240F19-144E-4704-A16A-8FDAC3867EC9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +29,10 @@ Global {8A162932-BAA3-429A-84C4-B93A1C85DDE3}.Debug|Any CPU.Build.0 = Debug|Any CPU {8A162932-BAA3-429A-84C4-B93A1C85DDE3}.Release|Any CPU.ActiveCfg = Release|Any CPU {8A162932-BAA3-429A-84C4-B93A1C85DDE3}.Release|Any CPU.Build.0 = Release|Any CPU + {6F240F19-144E-4704-A16A-8FDAC3867EC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6F240F19-144E-4704-A16A-8FDAC3867EC9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6F240F19-144E-4704-A16A-8FDAC3867EC9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6F240F19-144E-4704-A16A-8FDAC3867EC9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/AM2RLauncher/AM2RLauncher/CrossPlatformOperations.cs b/AM2RLauncher/AM2RLauncher/CrossPlatformOperations.cs index 5df6d4e..b26daa5 100644 --- a/AM2RLauncher/AM2RLauncher/CrossPlatformOperations.cs +++ b/AM2RLauncher/AM2RLauncher/CrossPlatformOperations.cs @@ -36,9 +36,9 @@ namespace AM2RLauncher public static readonly string NIXHOME = Environment.GetEnvironmentVariable("HOME"); /// - /// Path to the Config folder on *Nix-based systems. + /// Path to the Config folder on Linux-based systems. /// - public static readonly string NIXXDGCONFIG = Environment.GetEnvironmentVariable("XDG_CONFIG_HOME"); + public static readonly string LINUXXDGCONFIG = Environment.GetEnvironmentVariable("XDG_CONFIG_HOME"); /// /// Current Path where the Launcher is located. For more info, check . @@ -67,8 +67,18 @@ namespace AM2RLauncher "https://gitlab.com/am2r-community-developers/AM2R-Autopatcher-Linux.git" }; } + else if (currentPlatform.IsMac) + { + return new List + { + "https://github.com/Miepee/AM2R-Autopatcher-Mac.git", + "https://github.com/Miepee/AM2R-Autopatcher-Mac.git" //TODO: make mac official, put this on gitlab + }; + + } else // Should never occur, but... { + log.Error(currentPlatform.ID + " has no mirror lists!"); return new List(); } } @@ -78,8 +88,10 @@ namespace AM2RLauncher /// /// The property to get the value from. /// The value from as a string + // TODO: how often is launcherconfigpath / launcherconfigfilepath created? maybe create an extra variable for it so we don't generate it every time public static string ReadFromConfig(string property) { + log.Info($"Reading {property} from config."); if (currentPlatform.IsWinForms) { // We use the configuration manager in order to read `property` from the app.config and then return it @@ -87,11 +99,16 @@ namespace AM2RLauncher if (appConfig == null) throw new ArgumentException("The property " + property + " could not be found."); return appConfig.ConnectionString; } - if (currentPlatform.IsGtk) + else if (currentPlatform.IsGtk || currentPlatform.IsMac) { - // Config for nix systems will be saved in XDG_CONFIG_HOME/AM2RLauncher (or if empty, ~/.config) + // Config for linux systems will be saved in XDG_CONFIG_HOME/AM2RLauncher (or if empty, ~/.config) + // Config for mac systems will be saved in ~/Library/Preferences/AM2RLauncher string homePath = NIXHOME; - string launcherConfigPath = (String.IsNullOrWhiteSpace(NIXXDGCONFIG) ? (homePath + "/.config") : NIXXDGCONFIG) + "/AM2RLauncher"; + string launcherConfigPath = ""; + if (currentPlatform.IsGtk) + launcherConfigPath = (String.IsNullOrWhiteSpace(LINUXXDGCONFIG) ? (homePath + "/.config") : LINUXXDGCONFIG) + "/AM2RLauncher"; + else if (currentPlatform.IsMac) + launcherConfigPath = homePath + "/Library/Preferences/AM2RLauncher"; string launcherConfigFilePath = launcherConfigPath + "/config.xml"; XML.LauncherConfigXML launcherConfig = new XML.LauncherConfigXML(); @@ -111,6 +128,8 @@ namespace AM2RLauncher // 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(currentPlatform.ID + " has no config to read from!"); return null; } @@ -121,6 +140,7 @@ namespace AM2RLauncher /// The value that will be written. public static void WriteToConfig(string property, object value) { + log.Info($"Writing {value} of type {value.GetType()} to {property} to config."); if (currentPlatform.IsWinForms) { // We use the configuration manager in order to read from the app.config, change the value and save it @@ -134,11 +154,16 @@ namespace AM2RLauncher appConfig.Save(); ConfigurationManager.RefreshSection("connectionStrings"); } - else if (currentPlatform.IsGtk) + else if (currentPlatform.IsGtk || currentPlatform.IsMac) { // Config for nix systems will be saved in XDG_CONFIG_HOME/AM2RLauncher (or if empty, ~/.config) + // Config for mac systems will be saved in ~/Library/Preferences/AM2RLauncher string homePath = NIXHOME; - string launcherConfigPath = (String.IsNullOrWhiteSpace(NIXXDGCONFIG) ? (homePath + "/.config") : NIXXDGCONFIG) + "/AM2RLauncher"; + string launcherConfigPath = ""; + if (currentPlatform.IsGtk) + launcherConfigPath = (String.IsNullOrWhiteSpace(LINUXXDGCONFIG) ? (homePath + "/.config") : LINUXXDGCONFIG) + "/AM2RLauncher"; + else if (currentPlatform.IsMac) + launcherConfigPath = homePath + "/Library/Preferences/AM2RLauncher"; string launcherConfigFilePath = launcherConfigPath + "/config.xml"; XML.LauncherConfigXML launcherConfig = new XML.LauncherConfigXML(); @@ -157,6 +182,8 @@ namespace AM2RLauncher // Serialize back into the file File.WriteAllText(launcherConfigFilePath, XML.Serializer.Serialize(launcherConfig)); } + else + log.Error(currentPlatform.ID + " has no config to write to!"); } /// @@ -182,11 +209,16 @@ namespace AM2RLauncher File.WriteAllText(newConfigPath, newConfigText); } - else if (currentPlatform.IsGtk) + else if (currentPlatform.IsGtk || currentPlatform.IsMac) { // Config for nix systems will be saved in XDG_CONFIG_HOME/AM2RLauncher (or if empty, ~/.config) + // Config for mac systems will be saved in ~/Library/Preferences/AM2RLauncher string homePath = NIXHOME; - string launcherConfigPath = (String.IsNullOrWhiteSpace(NIXXDGCONFIG) ? (homePath + "/.config") : NIXXDGCONFIG) + "/AM2RLauncher"; + string launcherConfigPath = ""; + if (currentPlatform.IsGtk) + launcherConfigPath = (String.IsNullOrWhiteSpace(LINUXXDGCONFIG) ? (homePath + "/.config") : LINUXXDGCONFIG) + "/AM2RLauncher"; + else if (currentPlatform.IsMac) + launcherConfigPath = homePath + "/Library/Preferences/AM2RLauncher"; string launcherConfigFilePath = launcherConfigPath + "/config.xml"; XML.LauncherConfigXML launcherConfig = new XML.LauncherConfigXML(); @@ -194,6 +226,8 @@ namespace AM2RLauncher launcherConfig = XML.Serializer.Deserialize(File.ReadAllText(launcherConfigFilePath)); File.WriteAllText(launcherConfigFilePath, XML.Serializer.Serialize(launcherConfig)); } + else + log.Error(currentPlatform.ID + " has no config to transfer over!"); } /// @@ -206,6 +240,10 @@ namespace AM2RLauncher Process.Start(url); else if (currentPlatform.IsGtk) Process.Start("xdg-open", url); + else if (currentPlatform.IsMac) + Process.Start("open", url); + else + log.Error(currentPlatform.ID + " can't open URLs!"); } /// @@ -231,11 +269,15 @@ namespace AM2RLauncher // Linux only opens the directory bc opening and selecting a file is pain else if (currentPlatform.IsGtk) Process.Start("xdg-open", $"\"{realPath}\""); + else if (currentPlatform.IsMac) + Process.Start("open", $"\"{realPath}\""); + else + log.Error(currentPlatform.ID + " can't open folders!"); } /// /// Opens and selects it in a file explorer. - /// Only selects on Windows, on Linux it just opens the folder. Does nothing if file doesn't exist. + /// Only selects on Windows and Mac, on Linux it just opens the folder. Does nothing if file doesn't exist. /// /// Path to open. public static void OpenFolderAndSelectFile(string path) @@ -256,6 +298,10 @@ namespace AM2RLauncher Process.Start("explorer.exe", $"/select, \"{realPath}\""); else if (currentPlatform.IsGtk) Process.Start("xdg-open", $"\"{Path.GetDirectoryName(realPath)}\""); + else if (currentPlatform.IsMac) + Process.Start("open", $"-R \"{realPath}\""); + else + log.Error(currentPlatform.ID + " can't open select files in file explorer!"); } /// @@ -272,7 +318,7 @@ namespace AM2RLauncher process = "cmd.exe"; arguments = "/C java -version"; } - else if (currentPlatform.IsGtk) + else if (currentPlatform.IsGtk || currentPlatform.IsMac) { process = "java"; arguments = "-version"; @@ -373,7 +419,7 @@ namespace AM2RLauncher proc.WaitForExit(); } } - else if (currentPlatform.IsGtk) + else if (currentPlatform.IsGtk || currentPlatform.IsMac) { ProcessStartInfo parameters = new ProcessStartInfo { @@ -405,7 +451,7 @@ namespace AM2RLauncher proc = "cmd"; javaArgs = "/C java -jar"; } - else if (currentPlatform.IsGtk) + else if (currentPlatform.IsGtk || currentPlatform.IsMac) { proc = "java"; javaArgs = "-jar"; @@ -497,6 +543,23 @@ namespace AM2RLauncher log.Error($"There was an error with '{xdgDataHome}'!\n{ex.Message} {ex.StackTrace}. Falling back to defaults."); } } + else if (currentPlatform.IsMac) + { + //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."); + } + } + else + log.Error(currentPlatform.ID + " has no current path!"); log.Info("Something went wrong, falling back to the default CurrentPath."); return Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory); diff --git a/AM2RLauncher/AM2RLauncher/LauncherUpdater.cs b/AM2RLauncher/AM2RLauncher/LauncherUpdater.cs index b927332..cbab196 100644 --- a/AM2RLauncher/AM2RLauncher/LauncherUpdater.cs +++ b/AM2RLauncher/AM2RLauncher/LauncherUpdater.cs @@ -25,6 +25,7 @@ namespace AM2RLauncher static readonly private string oldConfigPath = CrossPlatformOperations.CURRENTPATH + "/" + CrossPlatformOperations.LAUNCHERNAME + ".oldCfg"; /// The actual Path where the executable is stored, only used for updating. + //TODO: for mac, this reports the path of the mac runner, not the actual .app static readonly private string updatePath = currentPlatform.IsWinForms ? CrossPlatformOperations.CURRENTPATH : Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory); // Load reference to logger diff --git a/AM2RLauncher/AM2RLauncher/MainForm/HelperMethods.cs b/AM2RLauncher/AM2RLauncher/MainForm/HelperMethods.cs index b795e60..629ef47 100644 --- a/AM2RLauncher/AM2RLauncher/MainForm/HelperMethods.cs +++ b/AM2RLauncher/AM2RLauncher/MainForm/HelperMethods.cs @@ -247,6 +247,7 @@ namespace AM2RLauncher.Helpers Directory.Delete(tmpPath, true); // If we didn't exit before, everything is fine + log.Info("AM2R_11 check successful!"); return IsZipAM2R11ReturnCodes.Successful; } diff --git a/AM2RLauncher/AM2RLauncher/MainForm/MainForm.Events.cs b/AM2RLauncher/AM2RLauncher/MainForm/MainForm.Events.cs index 13292f3..cc1fe4a 100644 --- a/AM2RLauncher/AM2RLauncher/MainForm/MainForm.Events.cs +++ b/AM2RLauncher/AM2RLauncher/MainForm/MainForm.Events.cs @@ -561,12 +561,12 @@ namespace AM2RLauncher ProfileXML profile = Serializer.Deserialize(File.ReadAllText(modsDir + "/" + extractedName + "/profile.xml")); // Check if the OS versions match - if ((Platform.IsWinForms && profile.OperatingSystem != "Windows") || (Platform.IsGtk && profile.OperatingSystem != "Linux")) + if ((Platform.IsWinForms && profile.OperatingSystem != "Windows") || (Platform.IsGtk && profile.OperatingSystem != "Linux") || (Platform.IsMac && profile.OperatingSystem != "Mac")) { string currentOS = ""; if (Platform.IsWinForms) currentOS = "Windows"; else if (Platform.IsGtk) currentOS = "Linux"; // Teeeeechnically, any OS could run GTK applications as well but it'd break a lot and is thus unsupported. - + else if (Platform.IsMac) currentOS = "Mac"; log.Error("Mod is for " + profile.OperatingSystem + " while current OS is " + Platform + ". Cancelling mod import."); @@ -633,6 +633,8 @@ namespace AM2RLauncher /// private void SettingsProfileDropDownSelectedIndexChanged(object sender, EventArgs e) { + //TODO: for some reason, clearing the dropdown triggers this event on mac. Why!? + if (Platform.IsMac && settingsProfileDropDown.SelectedIndex == -1 && settingsProfileDropDown.Items.Count == 0) return; log.Info("SettingsProfileDropDown.SelectedIndex has been changed to " + settingsProfileDropDown.SelectedIndex + "."); if (settingsProfileDropDown.SelectedIndex <= 0 || settingsProfileDropDown.Items.Count == 0) @@ -661,6 +663,7 @@ namespace AM2RLauncher profileNotesTextArea.TextColor = colGreen; profileNotesTextArea.Text = Language.Text.ProfileNotes + "\n" + profileList[settingsProfileDropDown.SelectedIndex].ProfileNotes; } + } /// @@ -676,6 +679,7 @@ namespace AM2RLauncher addModButton.Enabled = false; settingsProfileLabel.TextColor = colInactive; settingsProfileDropDown.Enabled = false; + profileButton.Enabled = false; saveButton.Enabled = false; updateModButton.Enabled = false; deleteModButton.Enabled = false; @@ -741,6 +745,9 @@ namespace AM2RLauncher /// Gets called when user selects a different item from and changes accordingly. private void ProfileDropDownSelectedIndexChanged(object sender, EventArgs e) { + //TODO: eto bug maybe? for some reason this method shouldnt even get fired when clearing a dropdown... + if (Platform.IsMac && profileDropDown.SelectedIndex == -1 && profileDropDown.Items.Count == 0) return; + profileIndex = profileDropDown.SelectedIndex; log.Info("profileDropDown.SelectedIndex has been changed to " + profileIndex + "."); @@ -753,7 +760,9 @@ namespace AM2RLauncher saveWarningLabel.Visible = true; else saveWarningLabel.Visible = false; + UpdateStateMachine(); + } /// Gets called when user selects a different item from and writes that to the config. diff --git a/AM2RLauncher/AM2RLauncher/MainForm/MainForm.Methods.cs b/AM2RLauncher/AM2RLauncher/MainForm/MainForm.Methods.cs index ff27642..986cc68 100644 --- a/AM2RLauncher/AM2RLauncher/MainForm/MainForm.Methods.cs +++ b/AM2RLauncher/AM2RLauncher/MainForm/MainForm.Methods.cs @@ -25,12 +25,16 @@ namespace AM2RLauncher using (var repo = new Repository(CrossPlatformOperations.CURRENTPATH + "/PatchData")) { // Permanently undo commits not pushed to remote - Branch originMaster = repo.Branches["origin/master"]; + Branch originMaster = repo.Branches.ToList().Where(b => b.FriendlyName.Contains("origin/master") || b.FriendlyName.Contains("origin/main")).FirstOrDefault(); if (originMaster == null) { + log.Info("Neither branch 'master' nor branch 'main' could be found! Corrupted or invalid git repo? Deleting PatchData..."); // Directory exists, but seems corrupted, we delete it and prompt the user to download it again. - MessageBox.Show(Language.Text.CorruptPatchData, Language.Text.ErrorWindowTitle, MessageBoxType.Error); + Application.Instance.Invoke(new Action(() => + { + MessageBox.Show(Language.Text.CorruptPatchData, Language.Text.ErrorWindowTitle, MessageBoxType.Error); + })); HelperMethods.DeleteDirectory(CrossPlatformOperations.CURRENTPATH + "/PatchData"); throw new UserCancelledException(); } @@ -84,7 +88,10 @@ namespace AM2RLauncher private bool IsProfileInstalled(ProfileXML profile) { if (Platform.IsWinForms) return File.Exists(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name + "/AM2R.exe"); - if (Platform.IsGtk) return File.Exists(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name + "/AM2R.AppImage"); + else if (Platform.IsGtk) return File.Exists(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name + "/AM2R.AppImage"); + else if (Platform.IsMac) return Directory.Exists(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name + "/AM2R.app"); + + log.Error(Platform.ID + " can't have profiles installed!"); return false; } @@ -212,7 +219,7 @@ namespace AM2RLauncher settingsProfileDropDown.Items.AddRange(profileDropDown.Items); settingsProfileDropDown.SelectedIndex = profileDropDown.Items.Count != 0 ? 0 : -1; - log.Info("Profiles loaded."); + log.Info("Loaded " + profileList.Count + " profile(s)."); // Refresh the author and version label on the main tab if (profileList.Count > 0) @@ -233,7 +240,7 @@ namespace AM2RLauncher log.Info("Installing profile " + profile.Name + "..."); // Check if xdelta is installed on linux, by searching all folders in PATH - if (Platform.IsGtk && !CrossPlatformOperations.CheckIfXdeltaIsInstalled()) + if ((Platform.IsGtk || Platform.IsMac) && !CrossPlatformOperations.CheckIfXdeltaIsInstalled()) { Application.Instance.Invoke(new Action(() => { @@ -256,20 +263,35 @@ namespace AM2RLauncher // This failsafe should NEVER get triggered, but Miepee's broken this too much for me to trust it otherwise. if (Directory.Exists(profilePath)) Directory.Delete(profilePath, true); - - + // Create profile directory Directory.CreateDirectory(profilePath); // Switch profilePath on Gtk if (Platform.IsGtk) { + //TODO: it was just recursively deleted, is this really necessary? if (Directory.Exists(profilePath + "/assets")) Directory.Delete(profilePath + "/assets", true); profilePath += "/assets"; Directory.CreateDirectory(profilePath); } + else if (Platform.IsMac) + { + // Folder structure for mac is like this: + // am2r.app -> Contents + // -Frameworks (some libs) + // -MacOS (runner) + // -Resources (asset path) + profilePath += "/AM2R.app/Contents"; + Directory.CreateDirectory(profilePath); + Directory.CreateDirectory(profilePath + "/MacOS"); + Directory.CreateDirectory(profilePath + "/Resources"); + profilePath += "/Resources"; + + log.Info("ProfileInstallstion: Created folder structure."); + } // Extract 1.1 ZipFile.ExtractToDirectory(CrossPlatformOperations.CURRENTPATH + "/AM2R_11.zip", profilePath); @@ -298,6 +320,15 @@ namespace AM2RLauncher exe = Regex.Match(desktopContents, @"(?<=Exec=).*").Value; log.Info("According to AppImage desktop file, using \"" + exe + "\" as game name."); } + else if (Platform.IsMac) + { + datawin = "game.ios"; + exe = "Mac_Runner"; + } + else + { + log.Error(Platform.ID + " does not have valid runner / data.win names!"); + } log.Info("Attempting to patch in " + profilePath); @@ -317,24 +348,31 @@ namespace AM2RLauncher CrossPlatformOperations.ApplyXdeltaPatch(profilePath + "/AM2R.exe", dataPath + "/AM2R.xdelta", profilePath + "/" + exe); } } - else if (Platform.IsGtk) // YYC and VM look exactly the same on Linux so we're all good here. + else if (Platform.IsGtk || Platform.IsMac) // YYC and VM look exactly the same on Linux and Mac so we're all good here. { CrossPlatformOperations.ApplyXdeltaPatch(profilePath + "/data.win", dataPath + "/game.xdelta", profilePath + "/" + datawin); CrossPlatformOperations.ApplyXdeltaPatch(profilePath + "/AM2R.exe", dataPath + "/AM2R.xdelta", profilePath + "/" + exe); // Just in case the resulting file isn't chmoddded... Process.Start("chmod", "+x \"" + profilePath + "/" + exe + "\"").WaitForExit(); - // These are not needed by linux at all, so we delete them + // These are not needed by linux or Mac at all, so we delete them File.Delete(profilePath + "/data.win"); File.Delete(profilePath + "/AM2R.exe"); File.Delete(profilePath + "/D3DX9_43.dll"); - // Move exe one directory out - File.Move(profilePath + "/" + exe, profilePath.Substring(0, profilePath.LastIndexOf("/")) + "/" + exe); + // Move exe one directory out on Linux, move to MacOS folder instead on Mac + if (Platform.IsGtk) + File.Move(profilePath + "/" + exe, profilePath.Substring(0, profilePath.LastIndexOf("/")) + "/" + exe); + else + File.Move(profilePath + "/" + exe, profilePath.Replace("Resources", "MacOS") + "/" + exe); + } + else + { + log.Error(Platform.ID + " does not have patching methods!"); } // Applied patch - if (Platform.IsWinForms) UpdateProgressBar(66); + if (Platform.IsWinForms || Platform.IsMac) UpdateProgressBar(66); else if (Platform.IsGtk) UpdateProgressBar(44); // Linux will take a bit longer, due to appimage creation log.Info("xdelta patch(es) applied."); @@ -388,6 +426,22 @@ namespace AM2RLauncher if (File.Exists(profilePath + "/AM2R.AppImage")) File.Delete(profilePath + "/AM2R.AppImage"); File.Move(profilePath + "/" + "AM2R-x86_64.AppImage", profilePath + "/AM2R.AppImage"); } + // Mac post-process + else if (Platform.IsMac) + { + // Rename all songs to lowercase + foreach (var file in new DirectoryInfo(profilePath).GetFiles()) + if (file.Name.EndsWith(".ogg") && !File.Exists(file.DirectoryName + "/" + file.Name.ToLower())) + File.Move(file.FullName, file.DirectoryName + "/" + file.Name.ToLower()); + // Loading custom fonts crashes on Mac, so we delete those + Directory.Delete(profilePath + "/lang/fonts", true); + // Move Frameworks, Info.plist and PkgInfo over + HelperMethods.DirectoryCopy(CrossPlatformOperations.CURRENTPATH + "/PatchData/data/Frameworks", profilePath.Replace("Resources", "Frameworks")); + File.Copy(dataPath + "/Info.plist", profilePath.Replace("Resources", "") + "/Info.plist", true); + File.Copy(CrossPlatformOperations.CURRENTPATH + "/PatchData/data/PkgInfo", profilePath.Replace("Resources", "") + "/PkgInfo", true); + //Put profilePath back to what it was before + profilePath = profilesHomePath + "/" + profile.Name; + } // Copy profile.xml so we can grab data to compare for updates later! // tldr; check if we're in PatchData or not @@ -428,7 +482,7 @@ namespace AM2RLauncher return; } // Check if xdelta is installed on linux - if (Platform.IsGtk && !CrossPlatformOperations.CheckIfXdeltaIsInstalled()) + if ((Platform.IsGtk || Platform.IsMac) && !CrossPlatformOperations.CheckIfXdeltaIsInstalled()) { // Message box show needs to be done on main thread Application.Instance.Invoke(new Action(() => @@ -672,6 +726,48 @@ namespace AM2RLauncher } } + else if (Platform.IsMac) + { + // Sets the arguments to only open the game, or append the profiles save path/logs and create time based logs. Creates the folder if necessary. + string arguments = "AM2R.app -W"; + + // Game logging + if ((bool)profileDebugLogCheck.Checked) + { + log.Info("Performing logging setup for profile " + profile.Name + "."); + + if (!Directory.Exists(logDir.FullName)) + Directory.CreateDirectory(logDir.FullName); + + if (File.Exists(logDir.FullName + "/" + profile.Name + ".txt")) + HelperMethods.RecursiveRollover(logDir.FullName + "/" + profile.Name + ".txt", 5); + + StreamWriter stream = File.AppendText(logDir.FullName + "/" + profile.Name + ".txt"); + + stream.WriteLine("AM2RLauncher " + VERSION + " log generated at " + date); + + stream.Flush(); + + stream.Close(); + + arguments += " --stdout \"" + logDir.FullName + "/" + profile.Name + ".txt\" --stderr \"" + logDir.FullName + "/" + profile.Name + ".txt\""; + } + + ProcessStartInfo proc = new ProcessStartInfo(); + + proc.WorkingDirectory = CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name; + proc.FileName = "open"; + proc.Arguments = arguments; + + log.Info("CWD of Profile is " + proc.WorkingDirectory); + + using (var p = Process.Start(proc)) + { + p.WaitForExit(); + } + } + else + log.Error(Platform.ID + " cannot run games!"); log.Info("Profile " + profile.Name + " process exited."); } diff --git a/AM2RLauncher/AM2RLauncher/MainForm/MainForm.UI.cs b/AM2RLauncher/AM2RLauncher/MainForm/MainForm.UI.cs index f30f4f1..708c616 100644 --- a/AM2RLauncher/AM2RLauncher/MainForm/MainForm.UI.cs +++ b/AM2RLauncher/AM2RLauncher/MainForm/MainForm.UI.cs @@ -127,12 +127,17 @@ namespace AM2RLauncher // Log distro and version (if it exists) if (Platform.IsGtk) { - string osRelease = File.ReadAllText("/etc/os-release"); - Regex lineRegex = new Regex(".*=.*"); - var results = lineRegex.Matches(osRelease).Cast(); - var version = results.FirstOrDefault(x => x.Value.Contains("VERSION")); - log.Info("Current Distro: " + results.FirstOrDefault(x => x.Value.Contains("NAME")).Value.Substring(5).Replace("\"", "") + - (version == null ? "" : " " + version.Value.Substring(8).Replace("\"", ""))); + if (File.Exists("/etc/os-release")) + { + string osRelease = File.ReadAllText("/etc/os-release"); + Regex lineRegex = new Regex(".*=.*"); + var results = lineRegex.Matches(osRelease).Cast(); + var version = results.FirstOrDefault(x => x.Value.Contains("VERSION")); + log.Info("Current Distro: " + results.FirstOrDefault(x => x.Value.Contains("NAME")).Value.Substring(5).Replace("\"", "") + + (version == null ? "" : " " + version.Value.Substring(8).Replace("\"", ""))); + } + else + log.Error("Couldn't determine the currently running distro!"); } // Set the Current Directory to the path the Launcher is located. Fixes some relative path issues. @@ -162,6 +167,10 @@ namespace AM2RLauncher Image = am2rIcon }; + // Create menubar with defaults for mac + if (Platform.IsMac) + Menu = new MenuBar { }; + // Create array from validCount profileList = new List(); @@ -223,6 +232,9 @@ namespace AM2RLauncher // Drawable paint event drawable.Paint += DrawablePaintEvent; + // Some systems don't call the paintEvent by default and only do so after actual resizing + if (Platform.IsMac) + LoadComplete += (sender, e) => { Size = new Size(Size.Width + 1, Size.Height); Size = new Size(Size.Width - 1, Size.Height);}; #region MAIN WINDOW @@ -309,10 +321,11 @@ namespace AM2RLauncher // Yes, we know this looks horrific on GTK. Sorry. // We're not exactly in a position to rewrite the entire DropDown object as a Drawable child, but if you want to, you're more than welcome! + // Mac gets a default BackgroundColor because it looks waaaaaaay better. profileDropDown = new DropDown { TextColor = colGreen, - BackgroundColor = colBGNoAlpha, + BackgroundColor = Platform.IsWinForms ? colBGNoAlpha : new Color(), }; // In order to not have conflicting theming, we just always respect the users theme for dropdown on GTK. if (Platform.IsGtk) @@ -518,7 +531,7 @@ namespace AM2RLauncher languageDropDown = new DropDown { TextColor = colGreen, - BackgroundColor = colBGNoAlpha, + BackgroundColor = Platform.IsWinForms ? colBGNoAlpha : new Color(), }; if (Platform.IsGtk) languageDropDown = new DropDown { }; @@ -608,7 +621,7 @@ namespace AM2RLauncher mirrorDropDown = new DropDown { TextColor = colGreen, - BackgroundColor = colBGNoAlpha, + BackgroundColor = Platform.IsWinForms ? colBGNoAlpha : new Color(), }; if (Platform.IsGtk) mirrorDropDown = new DropDown { }; @@ -679,7 +692,7 @@ namespace AM2RLauncher settingsProfileDropDown = new DropDown { TextColor = colGreen, - BackgroundColor = colBGNoAlpha, + BackgroundColor = Platform.IsWinForms ? colBGNoAlpha : new Color(), }; // In order to not have conflicting theming, we just always respect the users theme for dropdown on GTK. @@ -813,6 +826,7 @@ namespace AM2RLauncher if (Platform.IsGtk) customEnvVarTextBox.LostFocus += CustomEnvVarTextBoxLostFocus; + //TODO: These don't work properly on mac? Maybe on other platforms too? newsWebView.DocumentLoaded += NewsWebViewDocumentLoaded; changelogWebView.DocumentLoaded += ChangelogWebViewDocumentLoaded; diff --git a/AM2RLauncher/AM2RLauncher/XML/LauncherConfigXML.cs b/AM2RLauncher/AM2RLauncher/XML/LauncherConfigXML.cs index 3e6309c..31d4268 100644 --- a/AM2RLauncher/AM2RLauncher/XML/LauncherConfigXML.cs +++ b/AM2RLauncher/AM2RLauncher/XML/LauncherConfigXML.cs @@ -49,6 +49,7 @@ namespace AM2RLauncher.XML { get; set; } /// Indicates whether or not to create debug logs of profile. Used for [XmlAttribute("ProfileDebugLog")] + //TODO: WTF WHY!? public string ProfileDebugLog { get; set; } /// Indicates the custom environment variable(s) as text. Used for