more reorga

- add some #regions
- rename some methods to be more clearer
- cleanup add and delete profile
pull/32/head
Miepee 4 years ago
parent 428387256f
commit 1b6d1469f9

@ -117,6 +117,107 @@ namespace AM2RLauncher
UpdateStateMachine(); UpdateStateMachine();
} }
#region Misc events
/// <summary>
/// Fires when the profile layout completes loading. This makes sure that if <see cref="modSettingsProfileDropDown"/> has nothing in it "on boot",
/// that everything is disabled.
/// </summary>
private void ProfileLayoutLoadComplete(object sender, EventArgs e)
{
// Safety check
if ((modSettingsProfileDropDown == null) || (modSettingsProfileDropDown.Items.Count != 0)) return;
addModButton.Enabled = false;
settingsProfileLabel.TextColor = colInactive;
modSettingsProfileDropDown.Enabled = false;
profileButton.Enabled = false;
saveButton.Enabled = false;
updateModButton.Enabled = false;
deleteModButton.Enabled = false;
profileNotesTextArea.TextColor = colInactive;
}
/// <summary>
/// The <see cref="MainForm"/> calls this when you're resizing, in order to resize and scale the application accordingly.
/// </summary>
private void DrawablePaintEvent(object sender, PaintEventArgs e)
{
// Get drawing variables
float height = drawable.Height;
float width = drawable.Width;
//TODO: apparently winforms is the big outlier here. Works normal on wpf, I have *no* idea why, seems related to our image. issue has been submitted at eto
float scaleDivisor = OS.IsWindows ? formBG.Width : formBG.Height;
float scale = height / scaleDivisor;
// Do the actual scaling
e.Graphics.ScaleTransform(scale);
// Draw the image, change x offset with some absurd wizardry written at 5 AM
e.Graphics.DrawImage(formBG, ((width / 2) - (height / 1.4745f)) / scale, 0);
}
/// <summary>
/// Gets called when user tries to close <see cref="MainForm"/>. This does a few things:<br/>
/// 1) Writes the Width, Height, the check if <see cref="MainForm"/> is currently maximized and the ProfileIndex to the Config<br/>
/// 2) Checks if current <see cref="updateState"/> is <see cref="UpdateState.Downloading"/>. If yes, it creates a Warning to the end user.
/// </summary>
private void MainformClosing(object sender, CancelEventArgs e)
{
log.Info("Attempting to close MainForm!");
CrossPlatformOperations.WriteToConfig("Width", ClientSize.Width);
CrossPlatformOperations.WriteToConfig("Height", ClientSize.Height);
CrossPlatformOperations.WriteToConfig("IsMaximized", this.WindowState == WindowState.Maximized);
CrossPlatformOperations.WriteToConfig("ProfileIndex", profileIndex.ToString());
switch (updateState)
{
// If we're currently still downloading, ask first if user really wants to close and cancel the event if necessary
case UpdateState.Downloading:
{
var result = MessageBox.Show(this, Text.CloseOnCloningText, Text.WarningWindowTitle, MessageBoxButtons.YesNo,
MessageBoxType.Warning, MessageBoxDefaultButton.No);
if (result == DialogResult.No)
e.Cancel = true;
else
isGitProcessGettingCancelled = true;
// We don't need to delete any folders here, the cancelled gitClone will do that automatically for us :)
break;
}
// We can't close during installing, so we cancel the event.
case UpdateState.Installing:
{
MessageBox.Show(this, Text.CloseOnInstallingText, Text.WarningWindowTitle, MessageBoxButtons.OK, MessageBoxType.Warning);
e.Cancel = true;
break;
}
}
// This needs to be made invisible, otherwise a tray indicator will be visible (on linux?) that clicking crashes the application
//TODO: this sounds like an eto bug. check if this can get reproduced.
trayIndicator.Visible = false;
if (e.Cancel)
log.Info("Cancelled MainForm closing event during UpdateState." + updateState + ".");
else
log.Info("Successfully closed MainForm. Exiting main thread.");
}
/// <summary>Gets called when <see cref="showButton"/> gets clicked and shows the <see cref="MainForm"/> and brings it to the front again.</summary>
private void ShowButtonClick(object sender, EventArgs e)
{
log.Info("User has opened the launcher from system tray.");
this.Show();
this.BringToFront();
}
#endregion
#region MAIN TAB
/// <summary> /// <summary>
/// Does a bunch of stuff, depending on the current state of <see cref="updateState"/>. /// Does a bunch of stuff, depending on the current state of <see cref="updateState"/>.
/// </summary> /// </summary>
@ -411,6 +512,28 @@ namespace AM2RLauncher
} }
} }
/// <summary>Gets called when user selects a different item from <see cref="profileDropDown"/> and changes <see cref="profileAuthorLabel"/> accordingly.</summary>
private void ProfileDropDownSelectedIndexChanged(object sender, EventArgs e)
{
if (profileDropDown.SelectedIndex == -1 && profileDropDown.Items.Count == 0) return;
profileIndex = profileDropDown.SelectedIndex;
log.Debug("profileDropDown.SelectedIndex has been changed to " + profileIndex + ".");
profileAuthorLabel.Text = Text.Author + " " + profileList[profileDropDown.SelectedIndex].Author;
profileVersionLabel.Text = Text.VersionLabel + " " + profileList[profileDropDown.SelectedIndex].Version;
if (profileDropDown.SelectedIndex != 0 && (profileList[profileDropDown.SelectedIndex].SaveLocation == "%localappdata%/AM2R" ||
profileList[profileDropDown.SelectedIndex].SaveLocation == "default"))
saveWarningLabel.Visible = true;
else
saveWarningLabel.Visible = false;
UpdateStateMachine();
}
#endregion
/// <summary> /// <summary>
/// If no internet access is available, this changes the content of <paramref name="tabPage"/> to an empty page only displaying <paramref name="errorLabel"/>. /// If no internet access is available, this changes the content of <paramref name="tabPage"/> to an empty page only displaying <paramref name="errorLabel"/>.
/// </summary> /// </summary>
@ -432,376 +555,309 @@ namespace AM2RLauncher
}; };
} }
/// <summary> #region SETTINGS
/// Runs when <see cref="addModButton"/> is clicked. Brings up a file select to select a mod, and adds that to the mod directory.
/// </summary> /// <summary>Gets called when user selects a different item from <see cref="languageDropDown"/> and writes that to the config.</summary>
private void AddModButtonClicked(object sender, EventArgs e) private void LanguageDropDownSelectedIndexChanged(object sender, EventArgs e)
{ {
log.Info("User requested to add mod. Requesting user input for new mod .zip..."); log.Info("languageDropDown.SelectedIndex has been changed to " + languageDropDown.SelectedIndex + ".");
CrossPlatformOperations.WriteToConfig("Language", languageDropDown.SelectedIndex == 0 ? "Default" : languageDropDown.Items[languageDropDown.SelectedIndex].Text);
}
OpenFileDialog fileFinder = GetSingleZipDialog(Text.SelectModFileDialog); /// <summary>Gets called when <see cref="autoUpdateAM2RCheck"/> gets clicked and writes its new value to the config.</summary>
private void AutoUpdateAM2RCheckChanged(object sender, EventArgs e)
{
log.Info("Auto Update AM2R has been set to " + autoUpdateAM2RCheck.Checked + ".");
CrossPlatformOperations.WriteToConfig("AutoUpdateAM2R", (bool)autoUpdateAM2RCheck.Checked);
}
if (fileFinder.ShowDialog(this) != DialogResult.Ok) /// <summary>Gets called when <see cref="autoUpdateLauncherCheck"/> gets clicked and writes its new value to the config.</summary>
{ private void AutoUpdateLauncherCheckChanged(object sender, EventArgs e)
log.Info("User cancelled the Mod selection."); {
return; log.Info("Auto Update Launcher has been set to " + autoUpdateAM2RCheck.Checked + ".");
} CrossPlatformOperations.WriteToConfig("AutoUpdateLauncher", (bool)autoUpdateAM2RCheck.Checked);
}
if (String.IsNullOrWhiteSpace(fileFinder.FileName)) /// <summary>Gets called when <see cref="hqMusicPCCheck"/> gets clicked and writes its new value to the config.</summary>
{ private void HqMusicPCCheckChanged(object sender, EventArgs e)
log.Error("User did not supply valid input. Cancelling import."); {
LoadProfilesAndAdjustLists(); log.Info("PC HQ Music option has been changed to " + hqMusicPCCheck.Checked);
return; CrossPlatformOperations.WriteToConfig("MusicHQPC", hqMusicPCCheck.Checked);
} }
log.Info("User selected \"" + fileFinder.FileName + "\""); /// <summary>Gets called when <see cref="hqMusicAndroidCheck"/> gets clicked and writes its new value to the config.</summary>
private void HqMusicAndroidCheckChanged(object sender, EventArgs e)
{
log.Info("Android HQ Music option has been changed to " + hqMusicAndroidCheck.Checked);
CrossPlatformOperations.WriteToConfig("MusicHQAndroid", hqMusicAndroidCheck.Checked);
}
// If either a directory was selected or the file somehow went missing, cancel /// <summary>
if (!File.Exists(fileFinder.FileName)) /// Gets called when <see cref="profileDebugLogCheck"/> gets clicked, and writes it's new value to the config.
{ /// </summary>
log.Error("Selected mod .zip file not found! Cancelling import."); private void ProfileDebugLogCheckedChanged(object sender, EventArgs e)
return; {
} log.Info("Create Game Debug Logs option has been set to " + profileDebugLogCheck.Checked + ".");
CrossPlatformOperations.WriteToConfig("ProfileDebugLog", profileDebugLogCheck.Checked);
}
FileInfo modFile = new FileInfo(fileFinder.FileName); /// <summary>Gets called when user selects a different item from <see cref="mirrorDropDown"/>.
/// It then writes that to the config, and if <see cref="updateState"/> is not <see cref="UpdateState.Downloading"/>
/// it also overwrites the upstream URL in .git/config.</summary>
private void MirrorDropDownSelectedIndexChanged(object sender, EventArgs e)
{
currentMirror = mirrorList[mirrorDropDown.SelectedIndex];
string modsDir = new DirectoryInfo(CrossPlatformOperations.CURRENTPATH + "/Mods").FullName; log.Info("Current mirror has been set to " + currentMirror + ".");
string extractedName = modFile.Name.Replace(".zip", "");
// Extract it and see if it contains a profile.xml. If not, this is invalid CrossPlatformOperations.WriteToConfig("MirrorIndex", mirrorDropDown.SelectedIndex);
// Check first, if the directory is already there, if yes, throw a message // Don't overwrite the git config while we download!!!
if (Directory.Exists(modsDir + "/" + extractedName)) if (updateState == UpdateState.Downloading) return;
{
ProfileXML profile2 = Serializer.Deserialize<ProfileXML>(File.ReadAllText(modsDir + "/" + extractedName + "/profile.xml"));
log.Error("Mod is already imported as " + extractedName + "! Cancelling mod import.");
MessageBox.Show(this, HelperMethods.GetText(Text.ModIsAlreadyInstalledMessage, profile2.Name), Text.WarningWindowTitle, MessageBoxType.Warning); log.Info("Overwriting mirror in gitconfig.");
return;
}
// Directory doesn't exist -> extract!
ZipFile.ExtractToDirectory(fileFinder.FileName, modsDir + "/" + extractedName);
log.Info("Imported and extracted mod .zip as " + extractedName);
// Let's check if profile.xml exists in there! If it doesn't throw an error and cleanup // Check if the gitConfig exists, if yes regex the gitURL, and replace it with the new current Mirror.
if (!File.Exists(modsDir + "/" + extractedName + "/profile.xml")) string gitConfigPath = CrossPlatformOperations.CURRENTPATH + "/PatchData/.git/config";
{ if (!File.Exists(gitConfigPath)) return;
log.Error(fileFinder.FileName + " does not contain profile.xml! Cancelling mod import."); string gitConfig = File.ReadAllText(gitConfigPath);
Regex gitURLRegex = new Regex("https://.*\\.git");
Match match = gitURLRegex.Match(gitConfig);
gitConfig = gitConfig.Replace(match.Value, currentMirror);
File.WriteAllText(gitConfigPath, gitConfig);
}
MessageBox.Show(this, HelperMethods.GetText(Text.ModIsInvalidMessage, extractedName), Text.ErrorWindowTitle, MessageBoxType.Error); /// <summary>Gets called when <see cref="customMirrorCheck"/> gets clicked, displays a warning <see cref="MessageBox"/>
Directory.Delete(modsDir + "/" + extractedName, true); /// and enables <see cref="customMirrorTextBox"/> accordingly.</summary>
File.Delete(CrossPlatformOperations.CURRENTPATH + "/Mods/" + modFile.Name); private void CustomMirrorCheckChanged(object sender, EventArgs e)
return; {
} log.Info("Use Custom Mirror option has been set to " + customMirrorCheck.Checked + ".");
CrossPlatformOperations.WriteToConfig("CustomMirrorEnabled", (bool)customMirrorCheck.Checked);
ProfileXML profile = Serializer.Deserialize<ProfileXML>(File.ReadAllText(modsDir + "/" + extractedName + "/profile.xml")); bool enabled = (bool)customMirrorCheck.Checked;
customMirrorTextBox.Enabled = enabled;
mirrorDropDown.Enabled = !enabled;
// Not sure why the dropdown menu needs this hack, but the textBox does not.
if (OS.IsWindows)
mirrorDropDown.TextColor = mirrorDropDown.Enabled ? colGreen : colInactive;
mirrorLabel.TextColor = !enabled ? colGreen : colInactive;
// Check if the OS versions match // Create warning dialog when enabling
if (OS.Name != profile.OperatingSystem) if (enabled)
{ {
log.Error("Mod is for " + profile.OperatingSystem + " while current OS is " + OS.Name + ". Cancelling mod import."); MessageBox.Show(this, Text.WarningWindowText, Text.WarningWindowTitle, MessageBoxType.Warning);
currentMirror = customMirrorTextBox.Text;
MessageBox.Show(this, HelperMethods.GetText(Text.ModIsForWrongOS, profile.Name).Replace("$OS", profile.OperatingSystem).Replace("$CURRENTOS", OS.Name),
Text.ErrorWindowTitle, MessageBoxType.Error);
HelperMethods.DeleteDirectory(modsDir + "/" + extractedName);
return;
} }
else
// Check by *name*, if the mod was installed already
if (profileList.FirstOrDefault(p => p.Name == profile.Name) != null || Directory.Exists(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name))
{ {
log.Error(profile.Name + " is already installed."); // Revert mirror to selected index in mirror dropdown
MessageBox.Show(this, HelperMethods.GetText(Text.ModIsAlreadyInstalledMessage, profile.Name), Text.WarningWindowTitle, MessageBoxType.Warning); currentMirror = mirrorList[mirrorDropDown.SelectedIndex];
HelperMethods.DeleteDirectory(modsDir + "/" + extractedName);
return;
} }
log.Info(profile.Name + " successfully installed.");
MessageBox.Show(this, HelperMethods.GetText(Text.ModSuccessfullyInstalledMessage, profile.Name), Text.SuccessWindowTitle);
LoadProfilesAndAdjustLists();
// Adjust profileIndex to point to newly added mod. if its not found for whatever reason, we default to first community updates
modSettingsProfileDropDown.SelectedIndex = profileList.FindIndex(p => p.Name == profile.Name);
if (modSettingsProfileDropDown.SelectedIndex == -1)
modSettingsProfileDropDown.SelectedIndex = 0;
} }
/// <summary> //TODO: why exactly do we need this?
/// This opens the game files directory for the current profile. /// <summary>Gets called when <see cref="customMirrorCheck"/> gets loaded.
/// </summary> /// Enables and changes colors for <see cref="customMirrorTextBox"/> and <see cref="mirrorDropDown"/> accordingly.</summary>
private void ProfilesButtonClickEvent(object sender, EventArgs e) private void CustomMirrorCheckLoadComplete(object sender, EventArgs e)
{ {
if (!IsProfileIndexValid()) bool enabled = (bool)customMirrorCheck.Checked;
return; customMirrorTextBox.Enabled = enabled;
log.Info("User opened the profile directory for profile " + profileList[modSettingsProfileDropDown.SelectedIndex].Name + mirrorDropDown.Enabled = !enabled;
", which is " + profileList[modSettingsProfileDropDown.SelectedIndex].SaveLocation); if (OS.IsWindows)
CrossPlatformOperations.OpenFolder(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profileList[modSettingsProfileDropDown.SelectedIndex].Name); mirrorDropDown.TextColor = mirrorDropDown.Enabled ? colGreen : colInactive;
} }
/// <summary> /// <summary>
/// This opens the save directory for the current profile. /// If the <see cref="customMirrorTextBox"/> has lost focus, we set its text as the new <see cref="currentMirror"/>.
/// </summary> /// </summary>
private void SaveButtonClickEvent(object sender, EventArgs e) private void CustomMirrorTextBoxLostFocus(object sender, EventArgs e)
{ {
if (!IsProfileIndexValid()) // Check first, if the text is a valid git repo
return; Regex gitURLRegex = new Regex("https://.*\\.git");
log.Info("User opened the save directory for profile " + profileList[modSettingsProfileDropDown.SelectedIndex].Name + ", which is " + profileList[modSettingsProfileDropDown.SelectedIndex].SaveLocation); string mirrorText = customMirrorTextBox.Text;
CrossPlatformOperations.OpenFolder(profileList[modSettingsProfileDropDown.SelectedIndex].SaveLocation); if (!gitURLRegex.IsMatch(mirrorText))
}
/// <summary>
/// Enabled / disables <see cref="updateModButton"/> and <see cref="deleteModButton"/> accordingly.
/// </summary>
private void SettingsProfileDropDownSelectedIndexChanged(object sender, EventArgs e)
{
if (modSettingsProfileDropDown.SelectedIndex == -1 && modSettingsProfileDropDown.Items.Count == 0) return;
string profileName = modSettingsProfileDropDown.Items[modSettingsProfileDropDown.SelectedIndex].Text;
log.Info("SettingsProfileDropDown.SelectedIndex has been changed to " + modSettingsProfileDropDown.SelectedIndex + ".");
if (modSettingsProfileDropDown.SelectedIndex <= 0 || modSettingsProfileDropDown.Items.Count == 0)
{
deleteModButton.Enabled = false;
deleteModButton.ToolTip = null;
updateModButton.Enabled = false;
updateModButton.ToolTip = null;
profileNotesTextArea.TextColor = colInactive;
}
else
{ {
deleteModButton.Enabled = true; log.Info("User used " + mirrorText + " as a custom Mirror, didn't pass git validation test.");
deleteModButton.ToolTip = HelperMethods.GetText(Text.DeleteModButtonToolTip, profileName); MessageBox.Show(this, HelperMethods.GetText(Text.InvalidGitURL, mirrorText), Text.ErrorWindowTitle, MessageBoxType.Error);
// On non-installable profiles we want to disable updating return;
updateModButton.Enabled = profileList[modSettingsProfileDropDown.SelectedIndex].Installable;
updateModButton.ToolTip = HelperMethods.GetText(Text.UpdateModButtonToolTip, profileName);
} }
profileButton.Enabled = Directory.Exists(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profileName); currentMirror = mirrorText;
profileButton.ToolTip = HelperMethods.GetText(Text.OpenProfileFolderToolTip, profileName); CrossPlatformOperations.WriteToConfig("CustomMirrorText", currentMirror);
saveButton.Enabled = true;
saveButton.ToolTip = HelperMethods.GetText(Text.OpenSaveFolderToolTip, profileName);
if (modSettingsProfileDropDown.SelectedIndex < 0 || modSettingsProfileDropDown.Items.Count == 0) log.Info("Overwriting mirror in gitconfig.");
return;
profileNotesTextArea.TextColor = colGreen; // Check if the gitConfig exists, if yes regex the gitURL, and replace it with the new current Mirror.
profileNotesTextArea.Text = Text.ProfileNotes + "\n" + profileList[modSettingsProfileDropDown.SelectedIndex].ProfileNotes; string gitConfigPath = CrossPlatformOperations.CURRENTPATH + "/PatchData/.git/config";
if (!File.Exists(gitConfigPath)) return;
string gitConfig = File.ReadAllText(gitConfigPath);
Match match = gitURLRegex.Match(gitConfig);
gitConfig = gitConfig.Replace(match.Value, currentMirror);
File.WriteAllText(gitConfigPath, gitConfig);
log.Info("Custom Mirror has been set to " + currentMirror + ".");
} }
/// <summary> /// <summary>
/// Fires when the profile layout completes loading. This makes sure that if <see cref="modSettingsProfileDropDown"/> has nothing in it "on boot", /// If <see cref="customEnvVarTextBox"/> has lost focus, we write its text to the config.
/// that everything is disabled.
/// </summary> /// </summary>
private void ProfileLayoutLoadComplete(object sender, EventArgs e) private void CustomEnvVarTextBoxLostFocus(object sender, EventArgs e)
{ {
// Safety check log.Info("Custom Environment variables have been set to \"" + customEnvVarTextBox.Text + "\".");
if ((modSettingsProfileDropDown == null) || (modSettingsProfileDropDown.Items.Count != 0)) return; CrossPlatformOperations.WriteToConfig("CustomEnvVar", customEnvVarTextBox.Text);
addModButton.Enabled = false;
settingsProfileLabel.TextColor = colInactive;
modSettingsProfileDropDown.Enabled = false;
profileButton.Enabled = false;
saveButton.Enabled = false;
updateModButton.Enabled = false;
deleteModButton.Enabled = false;
profileNotesTextArea.TextColor = colInactive;
} }
#endregion
#region MOD SETTINGS
/// <summary> /// <summary>
/// The <see cref="MainForm"/> calls this when you're resizing, in order to resize and scale the application accordingly. /// Runs when <see cref="addModButton"/> is clicked. Brings up a file select to select a mod, and adds that to the mod directory.
/// </summary> /// </summary>
private void DrawablePaintEvent(object sender, PaintEventArgs e) private void AddModButtonClicked(object sender, EventArgs e)
{ {
// Get drawing variables log.Info("User requested to add mod. Requesting user input for new mod .zip...");
float height = drawable.Height;
float width = drawable.Width;
//TODO: apparently winforms is the big outlier here. Works normal on wpf, I have *no* idea why, seems related to our image. issue has been submitted at eto
float scaleDivisor = OS.IsWindows ? formBG.Width : formBG.Height;
float scale = height / scaleDivisor;
// Do the actual scaling OpenFileDialog fileFinder = GetSingleZipDialog(Text.SelectModFileDialog);
e.Graphics.ScaleTransform(scale);
// Draw the image, change x offset with some absurd wizardry written at 5 AM // If user didn't press ok, cancel
e.Graphics.DrawImage(formBG, ((width / 2) - (height / 1.4745f)) / scale, 0); if (fileFinder.ShowDialog(this) != DialogResult.Ok)
} {
log.Info("User cancelled the Mod selection.");
return;
}
/// <summary>Gets called when <see cref="showButton"/> gets clicked and shows the <see cref="MainForm"/> and brings it to the front again.</summary> log.Info("User selected \"" + fileFinder.FileName + "\"");
private void ShowButtonClick(object sender, EventArgs e)
{
log.Info("User has opened the launcher from system tray.");
this.Show(); // If either a directory was selected, user pressed OK without selecting anything or the file somehow went missing, cancel
this.BringToFront(); if (!File.Exists(fileFinder.FileName))
} {
log.Error("Selected mod .zip file not found! Cancelling import.");
return;
}
/// <summary>Gets called when <see cref="hqMusicPCCheck"/> gets clicked and writes its new value to the config.</summary> //TODO: move most of this into AM2RLauncher.Profile?
private void HqMusicPCCheckChanged(object sender, EventArgs e)
{
log.Info("PC HQ Music option has been changed to " + hqMusicPCCheck.Checked);
CrossPlatformOperations.WriteToConfig("MusicHQPC", hqMusicPCCheck.Checked);
}
/// <summary>Gets called when <see cref="hqMusicAndroidCheck"/> gets clicked and writes its new value to the config.</summary> FileInfo modFile = new FileInfo(fileFinder.FileName);
private void HqMusicAndroidCheckChanged(object sender, EventArgs e) string modsDir = new DirectoryInfo(CrossPlatformOperations.CURRENTPATH + "/Mods").FullName;
{ string modFileName = Path.GetFileNameWithoutExtension(modFile.Name);
log.Info("Android HQ Music option has been changed to " + hqMusicAndroidCheck.Checked); string extractedModDir = modsDir + "/" + modFileName;
CrossPlatformOperations.WriteToConfig("MusicHQAndroid", hqMusicAndroidCheck.Checked);
}
/// <summary>Gets called when user selects a different item from <see cref="profileDropDown"/> and changes <see cref="profileAuthorLabel"/> accordingly.</summary> // Check first, if the directory is already there, if yes, throw error
private void ProfileDropDownSelectedIndexChanged(object sender, EventArgs e) if (Directory.Exists(extractedModDir))
{ {
if (profileDropDown.SelectedIndex == -1 && profileDropDown.Items.Count == 0) return; string existingProfileName = Serializer.Deserialize<ProfileXML>(File.ReadAllText(extractedModDir + "/profile.xml")).Name;
log.Error("Mod is already imported as " + modFileName + "! Cancelling mod import.");
MessageBox.Show(this, HelperMethods.GetText(Text.ModIsAlreadyInstalledMessage, existingProfileName), Text.WarningWindowTitle, MessageBoxType.Warning);
return;
}
profileIndex = profileDropDown.SelectedIndex; // Directory doesn't exist -> extract!
log.Debug("profileDropDown.SelectedIndex has been changed to " + profileIndex + "."); ZipFile.ExtractToDirectory(modFile.FullName, extractedModDir);
log.Info("Imported and extracted mod .zip as " + modFileName);
profileAuthorLabel.Text = Text.Author + " " + profileList[profileDropDown.SelectedIndex].Author; // If profile.xml doesn't exist, throw an error and cleanup
profileVersionLabel.Text = Text.VersionLabel + " " + profileList[profileDropDown.SelectedIndex].Version; if (!File.Exists(extractedModDir + "/profile.xml"))
{
log.Error(modFile.Name + " does not contain profile.xml! Cancelling mod import.");
MessageBox.Show(this, HelperMethods.GetText(Text.ModIsInvalidMessage, modFileName), Text.ErrorWindowTitle, MessageBoxType.Error);
Directory.Delete(extractedModDir, true);
return;
}
if (profileDropDown.SelectedIndex != 0 && (profileList[profileDropDown.SelectedIndex].SaveLocation == "%localappdata%/AM2R" || ProfileXML profile = Serializer.Deserialize<ProfileXML>(File.ReadAllText(extractedModDir + "/profile.xml"));
profileList[profileDropDown.SelectedIndex].SaveLocation == "default"))
saveWarningLabel.Visible = true;
else
saveWarningLabel.Visible = false;
UpdateStateMachine(); // If OS versions mismatch, throw error and cleanup
} if (OS.Name != profile.OperatingSystem)
{
log.Error("Mod is for " + profile.OperatingSystem + " while current OS is " + OS.Name + ". Cancelling mod import.");
MessageBox.Show(this, HelperMethods.GetText(Text.ModIsForWrongOS, profile.Name).Replace("$OS", profile.OperatingSystem).Replace("$CURRENTOS", OS.Name),
Text.ErrorWindowTitle, MessageBoxType.Error);
HelperMethods.DeleteDirectory(extractedModDir);
return;
}
/// <summary>Gets called when user selects a different item from <see cref="languageDropDown"/> and writes that to the config.</summary> // If mod was installed/added by *name* already, throw error and cleanup
private void LanguageDropDownSelectedIndexChanged(object sender, EventArgs e) if (profileList.FirstOrDefault(p => p.Name == profile.Name) != null)
{ {
log.Info("languageDropDown.SelectedIndex has been changed to " + languageDropDown.SelectedIndex + "."); log.Error(profile.Name + " is already installed.");
CrossPlatformOperations.WriteToConfig("Language", languageDropDown.SelectedIndex == 0 ? "Default" : languageDropDown.Items[languageDropDown.SelectedIndex].Text); MessageBox.Show(this, HelperMethods.GetText(Text.ModIsAlreadyInstalledMessage, profile.Name), Text.WarningWindowTitle, MessageBoxType.Warning);
} HelperMethods.DeleteDirectory(extractedModDir);
return;
}
/// <summary>Gets called when <see cref="autoUpdateAM2RCheck"/> gets clicked and writes its new value to the config.</summary> // Reload list so mod gets recognized
private void AutoUpdateAM2RCheckChanged(object sender, EventArgs e) LoadProfilesAndAdjustLists();
{ // Adjust profileIndex to point to newly added mod. if its not found for whatever reason, we default to first community updates
log.Info("Auto Update AM2R has been set to " + autoUpdateAM2RCheck.Checked + "."); modSettingsProfileDropDown.SelectedIndex = profileList.FindIndex(p => p.Name == profile.Name);
CrossPlatformOperations.WriteToConfig("AutoUpdateAM2R", (bool)autoUpdateAM2RCheck.Checked); if (modSettingsProfileDropDown.SelectedIndex == -1)
} modSettingsProfileDropDown.SelectedIndex = 0;
/// <summary>Gets called when <see cref="autoUpdateLauncherCheck"/> gets clicked and writes its new value to the config.</summary> log.Info(profile.Name + " successfully added.");
private void AutoUpdateLauncherCheckChanged(object sender, EventArgs e) MessageBox.Show(this, HelperMethods.GetText(Text.ModSuccessfullyInstalledMessage, profile.Name), Text.SuccessWindowTitle);
{
log.Info("Auto Update Launcher has been set to " + autoUpdateAM2RCheck.Checked + ".");
CrossPlatformOperations.WriteToConfig("AutoUpdateLauncher", (bool)autoUpdateAM2RCheck.Checked);
} }
/// <summary>Gets called when <see cref="customMirrorCheck"/> gets clicked, displays a warning <see cref="MessageBox"/> /// <summary>
/// and enables <see cref="customMirrorTextBox"/> accordingly.</summary> /// Enabled / disables <see cref="updateModButton"/> and <see cref="deleteModButton"/> accordingly.
private void CustomMirrorCheckChanged(object sender, EventArgs e) /// </summary>
private void ModSettingsProfileDropDownSelectedIndexChanged(object sender, EventArgs e)
{ {
log.Info("Use Custom Mirror option has been set to " + customMirrorCheck.Checked + "."); if (modSettingsProfileDropDown.SelectedIndex == -1 && modSettingsProfileDropDown.Items.Count == 0) return;
CrossPlatformOperations.WriteToConfig("CustomMirrorEnabled", (bool)customMirrorCheck.Checked);
bool enabled = (bool)customMirrorCheck.Checked; string profileName = modSettingsProfileDropDown.Items[modSettingsProfileDropDown.SelectedIndex].Text;
customMirrorTextBox.Enabled = enabled;
mirrorDropDown.Enabled = !enabled;
// Not sure why the dropdown menu needs this hack, but the textBox does not.
if (OS.IsWindows)
mirrorDropDown.TextColor = mirrorDropDown.Enabled ? colGreen : colInactive;
mirrorLabel.TextColor = !enabled ? colGreen : colInactive;
// Create warning dialog when enabling log.Info("SettingsProfileDropDown.SelectedIndex has been changed to " + modSettingsProfileDropDown.SelectedIndex + ".");
if (enabled) if (modSettingsProfileDropDown.SelectedIndex <= 0 || modSettingsProfileDropDown.Items.Count == 0)
{ {
MessageBox.Show(this, Text.WarningWindowText, Text.WarningWindowTitle, MessageBoxType.Warning); deleteModButton.Enabled = false;
currentMirror = customMirrorTextBox.Text; deleteModButton.ToolTip = null;
updateModButton.Enabled = false;
updateModButton.ToolTip = null;
profileNotesTextArea.TextColor = colInactive;
} }
else else
{ {
// Revert mirror to selected index in mirror dropdown deleteModButton.Enabled = true;
currentMirror = mirrorList[mirrorDropDown.SelectedIndex]; deleteModButton.ToolTip = HelperMethods.GetText(Text.DeleteModButtonToolTip, profileName);
// On non-installable profiles we want to disable updating
updateModButton.Enabled = profileList[modSettingsProfileDropDown.SelectedIndex].Installable;
updateModButton.ToolTip = HelperMethods.GetText(Text.UpdateModButtonToolTip, profileName);
} }
}
/// <summary>Gets called when user selects a different item from <see cref="mirrorDropDown"/>.
/// It then writes that to the config, and if <see cref="updateState"/> is not <see cref="UpdateState.Downloading"/>
/// it also overwrites the upstream URL in .git/config.</summary>
private void MirrorDropDownSelectedIndexChanged(object sender, EventArgs e)
{
currentMirror = mirrorList[mirrorDropDown.SelectedIndex];
log.Info("Current mirror has been set to " + currentMirror + "."); profileButton.Enabled = Directory.Exists(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profileName);
profileButton.ToolTip = HelperMethods.GetText(Text.OpenProfileFolderToolTip, profileName);
CrossPlatformOperations.WriteToConfig("MirrorIndex", mirrorDropDown.SelectedIndex); saveButton.Enabled = true;
saveButton.ToolTip = HelperMethods.GetText(Text.OpenSaveFolderToolTip, profileName);
// Don't overwrite the git config while we download!!!
if (updateState == UpdateState.Downloading) return;
log.Info("Overwriting mirror in gitconfig.");
// Check if the gitConfig exists, if yes regex the gitURL, and replace it with the new current Mirror. if (modSettingsProfileDropDown.SelectedIndex < 0 || modSettingsProfileDropDown.Items.Count == 0)
string gitConfigPath = CrossPlatformOperations.CURRENTPATH + "/PatchData/.git/config"; return;
if (!File.Exists(gitConfigPath)) return; profileNotesTextArea.TextColor = colGreen;
string gitConfig = File.ReadAllText(gitConfigPath); profileNotesTextArea.Text = Text.ProfileNotes + "\n" + profileList[modSettingsProfileDropDown.SelectedIndex].ProfileNotes;
Regex gitURLRegex = new Regex("https://.*\\.git");
Match match = gitURLRegex.Match(gitConfig);
gitConfig = gitConfig.Replace(match.Value, currentMirror);
File.WriteAllText(gitConfigPath, gitConfig);
}
/// <summary>
/// Gets called when <see cref="profileDebugLogCheck"/> gets clicked, and writes it's new value to the config.
/// </summary>
private void ProfileDebugLogCheckedChanged(object sender, EventArgs e)
{
log.Info("Create Game Debug Logs option has been set to " + profileDebugLogCheck.Checked + ".");
CrossPlatformOperations.WriteToConfig("ProfileDebugLog", profileDebugLogCheck.Checked);
} }
/// <summary> /// <summary>
/// If the <see cref="customMirrorTextBox"/> has lost focus, we set its text as the new <see cref="currentMirror"/>. /// This opens the game files directory for the current profile.
/// </summary> /// </summary>
private void CustomMirrorTextBoxLostFocus(object sender, EventArgs e) private void ProfileDataButtonClickEvent(object sender, EventArgs e)
{ {
// Check first, if the text is a valid git repo if (!IsProfileIndexValid())
Regex gitURLRegex = new Regex("https://.*\\.git");
string mirrorText = customMirrorTextBox.Text;
if (!gitURLRegex.IsMatch(mirrorText))
{
log.Info("User used " + mirrorText + " as a custom Mirror, didn't pass git validation test.");
MessageBox.Show(this, HelperMethods.GetText(Text.InvalidGitURL, mirrorText), Text.ErrorWindowTitle, MessageBoxType.Error);
return; return;
} ProfileXML profile = profileList[modSettingsProfileDropDown.SelectedIndex];
log.Info("User opened the profile directory for profile " + profile.Name + ", which is " + profile.SaveLocation);
currentMirror = mirrorText; CrossPlatformOperations.OpenFolder(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name);
CrossPlatformOperations.WriteToConfig("CustomMirrorText", currentMirror);
log.Info("Overwriting mirror in gitconfig.");
// Check if the gitConfig exists, if yes regex the gitURL, and replace it with the new current Mirror.
string gitConfigPath = CrossPlatformOperations.CURRENTPATH + "/PatchData/.git/config";
if (!File.Exists(gitConfigPath)) return;
string gitConfig = File.ReadAllText(gitConfigPath);
Match match = gitURLRegex.Match(gitConfig);
gitConfig = gitConfig.Replace(match.Value, currentMirror);
File.WriteAllText(gitConfigPath, gitConfig);
log.Info("Custom Mirror has been set to " + currentMirror + ".");
} }
/// <summary> /// <summary>
/// If <see cref="customEnvVarTextBox"/> has lost focus, we write its text to the config. /// This opens the save directory for the current profile.
/// </summary> /// </summary>
private void CustomEnvVarTextBoxLostFocus(object sender, EventArgs e) private void SaveButtonClickEvent(object sender, EventArgs e)
{
log.Info("Custom Environment variables have been set to \"" + customEnvVarTextBox.Text + "\".");
CrossPlatformOperations.WriteToConfig("CustomEnvVar", customEnvVarTextBox.Text);
}
/// <summary>Gets called when <see cref="customMirrorCheck"/> gets loaded.
/// Enables and changes colors for <see cref="customMirrorTextBox"/> and <see cref="mirrorDropDown"/> accordingly.</summary>
private void CustomMirrorCheckLoadComplete(object sender, EventArgs e)
{ {
bool enabled = (bool)customMirrorCheck.Checked; if (!IsProfileIndexValid())
customMirrorTextBox.Enabled = enabled; return;
mirrorDropDown.Enabled = !enabled; ProfileXML profile = profileList[modSettingsProfileDropDown.SelectedIndex];
if (OS.IsWindows) log.Info("User opened the save directory for profile " + profile.Name + ", which is " + profile.SaveLocation);
mirrorDropDown.TextColor = mirrorDropDown.Enabled ? colGreen : colInactive; CrossPlatformOperations.OpenFolder(profile.SaveLocation);
} }
/// <summary> /// <summary>
@ -815,17 +871,17 @@ namespace AM2RLauncher
DialogResult result = MessageBox.Show(this, HelperMethods.GetText(Text.DeleteModWarning, profile.Name), Text.WarningWindowTitle, DialogResult result = MessageBox.Show(this, HelperMethods.GetText(Text.DeleteModWarning, profile.Name), Text.WarningWindowTitle,
MessageBoxButtons.OKCancel, MessageBoxType.Warning, MessageBoxDefaultButton.Cancel); MessageBoxButtons.OKCancel, MessageBoxType.Warning, MessageBoxDefaultButton.Cancel);
if (result == DialogResult.Ok) // if user didn't press ok, cancel
{ if (result != DialogResult.Ok)
log.Info("User did not cancel. Proceeding to delete " + profile);
DeleteProfileAndAdjustLists(profile);
log.Info(profile + " has been deleted");
MessageBox.Show(this, HelperMethods.GetText(Text.DeleteModButtonSuccess, profile.Name), Text.SuccessWindowTitle);
}
else
{ {
log.Info("User has cancelled profile deletion."); log.Info("User has cancelled profile deletion.");
return;
} }
log.Info("User did not cancel. Proceeding to delete " + profile);
DeleteProfileAndAdjustLists(profile);
log.Info(profile + " has been deleted");
MessageBox.Show(this, HelperMethods.GetText(Text.DeleteModButtonSuccess, profile.Name), Text.SuccessWindowTitle);
} }
/// <summary> /// <summary>
@ -836,165 +892,98 @@ namespace AM2RLauncher
{ {
log.Info("User requested to update mod. Requesting user input for new mod .zip..."); log.Info("User requested to update mod. Requesting user input for new mod .zip...");
bool abort = false;
ProfileXML currentProfile = profileList[modSettingsProfileDropDown.SelectedIndex]; ProfileXML currentProfile = profileList[modSettingsProfileDropDown.SelectedIndex];
OpenFileDialog fileFinder = GetSingleZipDialog(Text.SelectModFileDialog); OpenFileDialog fileFinder = GetSingleZipDialog(Text.SelectModFileDialog);
// If user didn't click OK, cancel
if (fileFinder.ShowDialog(this) != DialogResult.Ok) if (fileFinder.ShowDialog(this) != DialogResult.Ok)
{ {
log.Info("User cancelled the Mod selection."); log.Info("User cancelled the Mod selection.");
return; return;
} }
// Exit if nothing was selected
if (String.IsNullOrWhiteSpace(fileFinder.FileName))
{
log.Info("Nothing was selected, cancelling mod update.");
LoadProfilesAndAdjustLists();
return;
}
log.Info("User selected \"" + fileFinder.FileName + "\""); log.Info("User selected \"" + fileFinder.FileName + "\"");
// If either a directory was selected or the file somehow went missing, cancel // If either a directory was selected, no file was selected or the file somehow went missing, cancel
if (!File.Exists(fileFinder.FileName)) if (!File.Exists(fileFinder.FileName))
{ {
log.Error("Selected mod .zip file not found! Cancelling mod update."); log.Error("Selected mod .zip file not found! Cancelling mod update.");
return; return;
} }
FileInfo modFile = new FileInfo(fileFinder.FileName); //TODO: move most of this into AM2RLauncher.Profile?
FileInfo modFile = new FileInfo(fileFinder.FileName);
string modsDir = new DirectoryInfo(CrossPlatformOperations.CURRENTPATH + "/Mods").FullName; string modsDir = new DirectoryInfo(CrossPlatformOperations.CURRENTPATH + "/Mods").FullName;
string extractedName = modFile.Name.Replace(".zip", "_new"); string extractedName = Path.GetFileNameWithoutExtension(modFile.Name) + "_new";
string extractedFolder = modsDir + "/" + extractedName; string extractedModDir = modsDir + "/" + extractedName;
// Extract it and see if it contains a profile.xml. If not, this is invalid // If for some reason old files remain, delete them so that extraction doesnt throw
if (Directory.Exists(extractedModDir))
// If for some reason old files remain, delete them Directory.Delete(extractedModDir, true);
if (Directory.Exists(extractedFolder))
Directory.Delete(extractedFolder, true);
// Directory doesn't exist -> extract! // Directory doesn't exist -> extract!
ZipFile.ExtractToDirectory(fileFinder.FileName, extractedFolder); ZipFile.ExtractToDirectory(fileFinder.FileName, extractedModDir);
// Let's check if profile.xml exists in there! If it doesn't throw an error and cleanup // If mod doesn't have a profile.xml, throw an error and cleanup
if (!File.Exists(extractedFolder + "/profile.xml")) if (!File.Exists(extractedModDir + "/profile.xml"))
{ {
log.Error(fileFinder.FileName + " does not contain profile.xml! Cancelling mod update."); log.Error(fileFinder.FileName + " does not contain profile.xml! Cancelling mod update.");
MessageBox.Show(this, HelperMethods.GetText(Text.ModIsInvalidMessage, extractedName), Text.ErrorWindowTitle, MessageBoxType.Error); MessageBox.Show(this, HelperMethods.GetText(Text.ModIsInvalidMessage, extractedName), Text.ErrorWindowTitle, MessageBoxType.Error);
Directory.Delete(extractedFolder, true); Directory.Delete(extractedModDir, true);
File.Delete(CrossPlatformOperations.CURRENTPATH + "/Mods/" + modFile.Name);
return; return;
} }
// Check by *name*, if the mod was installed already // Check by *name*, if the mod was installed already
ProfileXML profile = Serializer.Deserialize<ProfileXML>(File.ReadAllText(extractedFolder + "/profile.xml")); ProfileXML profile = Serializer.Deserialize<ProfileXML>(File.ReadAllText(extractedModDir + "/profile.xml"));
if (profileList.FirstOrDefault(p => p.Name == profile.Name) != null || Directory.Exists(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name)) // If the selected mod is not installed, tell user that they should add it and cleanup
if (profileList.FirstOrDefault(p => p.Name == profile.Name) == null)
{ {
// Mod is already installed, so we can update!
DialogResult updateResult = MessageBox.Show(this, HelperMethods.GetText(Text.UpdateModWarning, currentProfile.Name), Text.WarningWindowTitle,
MessageBoxButtons.OKCancel, MessageBoxType.Warning, MessageBoxDefaultButton.Cancel);
if (updateResult == DialogResult.Ok)
{
// If the profile isn't installed, don't ask about archiving it
if (Profile.IsProfileInstalled(currentProfile))
{
DialogResult archiveResult = MessageBox.Show(this, HelperMethods.GetText(Text.ArchiveMod, currentProfile.Name + " " + Text.VersionLabel + currentProfile.Version), Text.WarningWindowTitle, MessageBoxButtons.YesNo, MessageBoxType.Warning, MessageBoxDefaultButton.No);
// User wants to archive profile
if (archiveResult == DialogResult.Yes)
ArchiveProfileAndAdjustLists(currentProfile);
}
// Now we delete the profile
DeleteProfileAndAdjustLists(currentProfile);
// Rename directory to take the old one's place
string originalFolder = modsDir + "/" + extractedName.Replace("_new", "");
Directory.Move(extractedFolder, originalFolder);
}
else // Cancel the operation!
{
log.Error("User has cancelled mod update!");
abort = true;
}
}
else
{
// Cancel the operation!
// Show message to tell user that mod could not be found, install this separately
log.Error("Mod is not installed! Cancelling mod update."); log.Error("Mod is not installed! Cancelling mod update.");
MessageBox.Show(this, HelperMethods.GetText(Text.UpdateModButtonWrongMod, currentProfile.Name).Replace("$SELECT", profile.Name), MessageBox.Show(this, HelperMethods.GetText(Text.UpdateModButtonWrongMod, currentProfile.Name).Replace("$SELECT", profile.Name),
Text.WarningWindowTitle, MessageBoxButtons.OK); Text.WarningWindowTitle, MessageBoxButtons.OK);
abort = true; HelperMethods.DeleteDirectory(extractedModDir);
return;
} }
if (abort) // If user doesn't want to update, cleanup
DialogResult updateResult = MessageBox.Show(this, HelperMethods.GetText(Text.UpdateModWarning, currentProfile.Name), Text.WarningWindowTitle,
MessageBoxButtons.OKCancel, MessageBoxType.Warning, MessageBoxDefaultButton.Cancel);
if (updateResult != DialogResult.Ok)
{ {
// File cleanup log.Error("User has cancelled mod update!");
HelperMethods.DeleteDirectory(extractedFolder); HelperMethods.DeleteDirectory(extractedModDir);
LoadProfilesAndAdjustLists();
return; return;
} }
log.Info("Successfully updated mod profile " + profile.Name + "."); // If the profile isn't installed, don't ask about archiving it
MessageBox.Show(this, HelperMethods.GetText(Text.ModSuccessfullyInstalledMessage, currentProfile.Name), Text.SuccessWindowTitle); if (Profile.IsProfileInstalled(currentProfile))
UpdateStateMachine(); {
DialogResult archiveResult = MessageBox.Show(this, HelperMethods.GetText(Text.ArchiveMod, currentProfile.Name + " " + Text.VersionLabel + currentProfile.Version), Text.WarningWindowTitle, MessageBoxButtons.YesNo, MessageBoxType.Warning, MessageBoxDefaultButton.No);
// User wants to archive profile
if (archiveResult == DialogResult.Yes)
ArchiveProfileAndAdjustLists(currentProfile);
}
DeleteProfileAndAdjustLists(currentProfile);
// Rename directory to take the old one's place
string originalFolder = modsDir + "/" + Path.GetFileNameWithoutExtension(modFile.Name);
Directory.Move(extractedModDir, originalFolder);
// Adjust our lists so it gets recognized
LoadProfilesAndAdjustLists(); LoadProfilesAndAdjustLists();
modSettingsProfileDropDown.SelectedIndex = profileList.FindIndex(p => p.Name == currentProfile.Name); modSettingsProfileDropDown.SelectedIndex = profileList.FindIndex(p => p.Name == currentProfile.Name);
if (modSettingsProfileDropDown.SelectedIndex == -1) if (modSettingsProfileDropDown.SelectedIndex == -1)
modSettingsProfileDropDown.SelectedIndex = 0; modSettingsProfileDropDown.SelectedIndex = 0;
}
/// <summary>
/// Gets called when user tries to close <see cref="MainForm"/>. This does a few things:<br/>
/// 1) Writes the Width, Height and the check if <see cref="MainForm"/> is currently maximized to the Config<br/>
/// 2) Checks if current <see cref="updateState"/> is <see cref="UpdateState.Downloading"/>. If yes, it creates a Warning to the end user.
/// </summary>
private void MainformClosing(object sender, CancelEventArgs e)
{
log.Info("Attempting to close MainForm!");
CrossPlatformOperations.WriteToConfig("Width", ClientSize.Width);
CrossPlatformOperations.WriteToConfig("Height", ClientSize.Height);
CrossPlatformOperations.WriteToConfig("IsMaximized", this.WindowState == WindowState.Maximized);
CrossPlatformOperations.WriteToConfig("ProfileIndex", profileIndex.ToString());
switch (updateState) log.Info("Successfully updated mod profile " + profile.Name + ".");
{ MessageBox.Show(this, HelperMethods.GetText(Text.ModSuccessfullyInstalledMessage, currentProfile.Name), Text.SuccessWindowTitle);
case UpdateState.Downloading:
{
var result = MessageBox.Show(this, Text.CloseOnCloningText, Text.WarningWindowTitle, MessageBoxButtons.YesNo,
MessageBoxType.Warning, MessageBoxDefaultButton.No);
if (result == DialogResult.No)
{
e.Cancel = true;
}
else
isGitProcessGettingCancelled = true;
// We don't need to delete any folders here, the cancelled gitClone will do that automatically for us :)
break;
}
case UpdateState.Installing:
MessageBox.Show(this, Text.CloseOnInstallingText, Text.WarningWindowTitle, MessageBoxButtons.OK, MessageBoxType.Warning);
e.Cancel = true;
break;
}
// This needs to be made invisible, otherwise a tray indicator will be visible (on linux?) that clicking crashes the application
trayIndicator.Visible = false;
if (e.Cancel)
log.Info("Cancelled MainForm closing event during UpdateState." + updateState + ".");
else
log.Info("Successfully closed MainForm. Exiting main thread.");
} }
#endregion
} }
} }

@ -175,6 +175,7 @@ namespace AM2RLauncher
colBGNoAlpha = Color.FromArgb(10, 10, 10); colBGNoAlpha = Color.FromArgb(10, 10, 10);
colBG = Color.FromArgb(10, 10, 10, 80); colBG = Color.FromArgb(10, 10, 10, 80);
if (OS.IsLinux) colBG = colBGNoAlpha; // XORG can't display alpha anyway, and Wayland breaks with it. if (OS.IsLinux) colBG = colBGNoAlpha; // XORG can't display alpha anyway, and Wayland breaks with it.
// TODO: that sounds like an Eto bug. investigate, try to open eto issue.
colBGHover = Color.FromArgb(17, 28, 13); colBGHover = Color.FromArgb(17, 28, 13);
Font smallButtonFont = new Font(SystemFont.Default, 10); Font smallButtonFont = new Font(SystemFont.Default, 10);
@ -808,9 +809,9 @@ namespace AM2RLauncher
mirrorDropDown.SelectedIndexChanged += MirrorDropDownSelectedIndexChanged; mirrorDropDown.SelectedIndexChanged += MirrorDropDownSelectedIndexChanged;
profileLayout.LoadComplete += ProfileLayoutLoadComplete; profileLayout.LoadComplete += ProfileLayoutLoadComplete;
addModButton.Click += AddModButtonClicked; addModButton.Click += AddModButtonClicked;
profileButton.Click += ProfilesButtonClickEvent; profileButton.Click += ProfileDataButtonClickEvent;
saveButton.Click += SaveButtonClickEvent; saveButton.Click += SaveButtonClickEvent;
modSettingsProfileDropDown.SelectedIndexChanged += SettingsProfileDropDownSelectedIndexChanged; modSettingsProfileDropDown.SelectedIndexChanged += ModSettingsProfileDropDownSelectedIndexChanged;
deleteModButton.Click += DeleteModButtonClicked; deleteModButton.Click += DeleteModButtonClicked;
updateModButton.Click += UpdateModButtonClicked; updateModButton.Click += UpdateModButtonClicked;
profileDebugLogCheck.CheckedChanged += ProfileDebugLogCheckedChanged; profileDebugLogCheck.CheckedChanged += ProfileDebugLogCheckedChanged;
@ -836,6 +837,7 @@ namespace AM2RLauncher
ButtonMenuItem showButton; ButtonMenuItem showButton;
/// <summary><see cref="List{T}"/> of <see cref="ProfileXML"/>s, used for actually working with profile data.</summary> /// <summary><see cref="List{T}"/> of <see cref="ProfileXML"/>s, used for actually working with profile data.</summary>
//TODO: this should be moved into AM2RLauncher.Core
List<ProfileXML> profileList; List<ProfileXML> profileList;
/// <summary><see cref="List{T}"/> of <see cref="ListItem"/>s so that Eto's annoying <see cref="IListItem"/> interface is appeased. Used for profile name display in DropDowns.</summary> /// <summary><see cref="List{T}"/> of <see cref="ListItem"/>s so that Eto's annoying <see cref="IListItem"/> interface is appeased. Used for profile name display in DropDowns.</summary>
List<ListItem> profileNames; List<ListItem> profileNames;

@ -47,13 +47,14 @@ public static class Profile
/// <summary> /// <summary>
/// Checks if AM2R 1.1 has been installed already, aka if a valid AM2R 1.1 Zip exists. /// Checks if AM2R 1.1 has been installed already, aka if a valid AM2R 1.1 Zip exists.
/// This method will store the result in a cache and return that, unless it's invalidated by <paramref name="invalidateCache"/>.
/// </summary> /// </summary>
/// <param name="invalidateCache">Determines if the AM2R_11 Cache should be invalidated</param> /// <param name="invalidateCache">Determines if the AM2R_11 Cache should be invalidated.</param>
/// <returns></returns> /// <returns></returns>
public static bool Is11Installed(bool invalidateCache = false) public static bool Is11Installed(bool invalidateCache = false)
{ {
// Only invalidate if we need to // Only invalidate if we need to
if (invalidateCache) InvalidateAM2R11InstallCacheIfNecessary(); if (invalidateCache) InvalidateAM2R11InstallCache();
// If we have a cache, return that instead // If we have a cache, return that instead
if (isAM2R11InstalledCache != null) return isAM2R11InstalledCache.Value; if (isAM2R11InstalledCache != null) return isAM2R11InstalledCache.Value;
@ -78,7 +79,7 @@ public static class Profile
/// <summary> /// <summary>
/// Invalidates <see cref="isAM2R11InstalledCache"/> if necessary. /// Invalidates <see cref="isAM2R11InstalledCache"/> if necessary.
/// </summary> /// </summary>
private static void InvalidateAM2R11InstallCacheIfNecessary() private static void InvalidateAM2R11InstallCache()
{ {
// If the file exists, and its hash matches with ours, don't invalidate // If the file exists, and its hash matches with ours, don't invalidate
if (File.Exists(CrossPlatformOperations.CURRENTPATH + "/AM2R_11.zip") && if (File.Exists(CrossPlatformOperations.CURRENTPATH + "/AM2R_11.zip") &&
@ -88,6 +89,64 @@ public static class Profile
isAM2R11InstalledCache = null; isAM2R11InstalledCache = null;
} }
/// <summary>
/// Checks if a Zip file is a valid AM2R_1.1 zip.
/// </summary>
/// <param name="zipPath">Full Path to the Zip file to check.</param>
/// <returns><see cref="IsZipAM2R11ReturnCodes"/> detailing the result</returns>
public static IsZipAM2R11ReturnCodes CheckIfZipIsAM2R11(string zipPath)
{
const string d3dHash = "86e39e9161c3d930d93822f1563c280d";
const string dataWinHash = "f2b84fe5ba64cb64e284be1066ca08ee";
const string am2rHash = "15253f7a66d6ea3feef004ebbee9b438";
string tmpPath = Path.GetTempPath() + Path.GetFileNameWithoutExtension(zipPath);
// Clean up in case folder exists already
if (Directory.Exists(tmpPath))
Directory.Delete(tmpPath, true);
Directory.CreateDirectory(tmpPath);
// Open archive
ZipArchive am2rZip = ZipFile.OpenRead(zipPath);
// Check if exe exists anywhere
ZipArchiveEntry am2rExe = am2rZip.Entries.FirstOrDefault(x => x.FullName.Contains("AM2R.exe"));
if (am2rExe == null)
return IsZipAM2R11ReturnCodes.MissingOrInvalidAM2RExe;
// Check if it's not in a subfolder. if it'd be in a subfolder, fullname would be "folder/AM2R.exe"
if (am2rExe.FullName != "AM2R.exe")
return IsZipAM2R11ReturnCodes.GameIsInASubfolder;
// Check validity
am2rExe.ExtractToFile(tmpPath + "/" + am2rExe.FullName);
if (HelperMethods.CalculateMD5(tmpPath + "/" + am2rExe.FullName) != am2rHash)
return IsZipAM2R11ReturnCodes.MissingOrInvalidAM2RExe;
// Check if data.win exists / is valid
ZipArchiveEntry dataWin = am2rZip.Entries.FirstOrDefault(x => x.FullName == "data.win");
if (dataWin == null)
return IsZipAM2R11ReturnCodes.MissingOrInvalidDataWin;
dataWin.ExtractToFile(tmpPath + "/" + dataWin.FullName);
if (HelperMethods.CalculateMD5(tmpPath + "/" + dataWin.FullName) != dataWinHash)
return IsZipAM2R11ReturnCodes.MissingOrInvalidDataWin;
// Check if d3d.dll exists / is valid
ZipArchiveEntry d3dx = am2rZip.Entries.FirstOrDefault(x => x.FullName == "D3DX9_43.dll");
if (d3dx == null)
return IsZipAM2R11ReturnCodes.MissingOrInvalidD3DX9_43Dll;
d3dx.ExtractToFile(tmpPath + "/" + d3dx.FullName);
if (HelperMethods.CalculateMD5(tmpPath + "/" + d3dx.FullName) != d3dHash)
return IsZipAM2R11ReturnCodes.MissingOrInvalidD3DX9_43Dll;
// Clean up
Directory.Delete(tmpPath, true);
// If we didn't exit before, everything is fine
log.Info("AM2R_11 check successful!");
return IsZipAM2R11ReturnCodes.Successful;
}
/// <summary> /// <summary>
/// Git Pulls from the repository. /// Git Pulls from the repository.
/// </summary> /// </summary>
@ -240,9 +299,7 @@ public static class Profile
// Delete folder in Mods // Delete folder in Mods
if (Directory.Exists(CrossPlatformOperations.CURRENTPATH + profile.DataPath)) if (Directory.Exists(CrossPlatformOperations.CURRENTPATH + profile.DataPath))
{
HelperMethods.DeleteDirectory(CrossPlatformOperations.CURRENTPATH + profile.DataPath); HelperMethods.DeleteDirectory(CrossPlatformOperations.CURRENTPATH + profile.DataPath);
}
// Delete the zip file in Mods // Delete the zip file in Mods
if (File.Exists(CrossPlatformOperations.CURRENTPATH + profile.DataPath + ".zip")) if (File.Exists(CrossPlatformOperations.CURRENTPATH + profile.DataPath + ".zip"))
@ -254,9 +311,7 @@ public static class Profile
// Delete folder in Profiles // Delete folder in Profiles
if (Directory.Exists(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name)) if (Directory.Exists(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name))
{
HelperMethods.DeleteDirectory(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name); HelperMethods.DeleteDirectory(CrossPlatformOperations.CURRENTPATH + "/Profiles/" + profile.Name);
}
log.Info("Successfully deleted profile " + profile.Name + "."); log.Info("Successfully deleted profile " + profile.Name + ".");
} }
@ -497,6 +552,7 @@ public static class Profile
progress.Report(100); progress.Report(100);
return; return;
} }
log.Info("Creating Android APK for profile " + profile.Name + "."); log.Info("Creating Android APK for profile " + profile.Name + ".");
// Create working dir after some cleanup // Create working dir after some cleanup
@ -782,62 +838,4 @@ public static class Profile
// isValid seems to only check for a .git folder, and there are cases where that exists, but not the profile.xml // isValid seems to only check for a .git folder, and there are cases where that exists, but not the profile.xml
return Repository.IsValid(CrossPlatformOperations.CURRENTPATH + "/PatchData") && File.Exists(CrossPlatformOperations.CURRENTPATH + "/PatchData/profile.xml"); return Repository.IsValid(CrossPlatformOperations.CURRENTPATH + "/PatchData") && File.Exists(CrossPlatformOperations.CURRENTPATH + "/PatchData/profile.xml");
} }
/// <summary>
/// Checks if a Zip file is a valid AM2R_1.1 zip.
/// </summary>
/// <param name="zipPath">Full Path to the Zip file to check.</param>
/// <returns><see cref="IsZipAM2R11ReturnCodes"/> detailing the result</returns>
public static IsZipAM2R11ReturnCodes CheckIfZipIsAM2R11(string zipPath)
{
const string d3dHash = "86e39e9161c3d930d93822f1563c280d";
const string dataWinHash = "f2b84fe5ba64cb64e284be1066ca08ee";
const string am2rHash = "15253f7a66d6ea3feef004ebbee9b438";
string tmpPath = Path.GetTempPath() + Path.GetFileNameWithoutExtension(zipPath);
// Clean up in case folder exists already
if (Directory.Exists(tmpPath))
Directory.Delete(tmpPath, true);
Directory.CreateDirectory(tmpPath);
// Open archive
ZipArchive am2rZip = ZipFile.OpenRead(zipPath);
// Check if exe exists anywhere
ZipArchiveEntry am2rExe = am2rZip.Entries.FirstOrDefault(x => x.FullName.Contains("AM2R.exe"));
if (am2rExe == null)
return IsZipAM2R11ReturnCodes.MissingOrInvalidAM2RExe;
// Check if it's not in a subfolder. if it'd be in a subfolder, fullname would be "folder/AM2R.exe"
if (am2rExe.FullName != "AM2R.exe")
return IsZipAM2R11ReturnCodes.GameIsInASubfolder;
// Check validity
am2rExe.ExtractToFile(tmpPath + "/" + am2rExe.FullName);
if (HelperMethods.CalculateMD5(tmpPath + "/" + am2rExe.FullName) != am2rHash)
return IsZipAM2R11ReturnCodes.MissingOrInvalidAM2RExe;
// Check if data.win exists / is valid
ZipArchiveEntry dataWin = am2rZip.Entries.FirstOrDefault(x => x.FullName == "data.win");
if (dataWin == null)
return IsZipAM2R11ReturnCodes.MissingOrInvalidDataWin;
dataWin.ExtractToFile(tmpPath + "/" + dataWin.FullName);
if (HelperMethods.CalculateMD5(tmpPath + "/" + dataWin.FullName) != dataWinHash)
return IsZipAM2R11ReturnCodes.MissingOrInvalidDataWin;
// Check if d3d.dll exists / is valid
ZipArchiveEntry d3dx = am2rZip.Entries.FirstOrDefault(x => x.FullName == "D3DX9_43.dll");
if (d3dx == null)
return IsZipAM2R11ReturnCodes.MissingOrInvalidD3DX9_43Dll;
d3dx.ExtractToFile(tmpPath + "/" + d3dx.FullName);
if (HelperMethods.CalculateMD5(tmpPath + "/" + d3dx.FullName) != d3dHash)
return IsZipAM2R11ReturnCodes.MissingOrInvalidD3DX9_43Dll;
// Clean up
Directory.Delete(tmpPath, true);
// If we didn't exit before, everything is fine
log.Info("AM2R_11 check successful!");
return IsZipAM2R11ReturnCodes.Successful;
}
} }
Loading…
Cancel
Save