You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
AM2RModPacker/Atomic/ModPacker.cs

408 lines
16 KiB

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Atomic.Language;
using AtomicLib;
using Eto.Forms;
namespace Atomic;
public partial class ModPacker : Form
{
private const string Version = Core.Version;
private readonly FileFilter apkFileFilter = new FileFilter(Text.APKFileFilter, ".apk");
private readonly FileFilter zipFileFilter = new FileFilter(Text.ZipArchivesFileFilter, ".zip");
private readonly Dictionary<ProfileOperatingSystems, Button> buttonLookupTable;
private readonly Dictionary<ProfileOperatingSystems, CheckBox> checkboxLookupTable;
private readonly Dictionary<ProfileOperatingSystems, PropertyInfo> isModLoadedLookupTable;
// Lookup dictionaries, filled in Constructor
private readonly Dictionary<ProfileOperatingSystems, Label> labelLookupTable;
private readonly ModCreationInfo modInfo = new ModCreationInfo();
private readonly Dictionary<ProfileOperatingSystems, FieldInfo> modPathLookupTable;
#region Eto events
private void CustomSaveCheckBoxChecked_Changed(object sender, EventArgs e)
{
customSaveButton.Enabled = customSaveCheckBox.Checked.Value;
customSaveTextBox.Enabled = customSaveCheckBox.Checked.Value;
UpdateCreateButton();
}
private void CustomSaveDataButton_Click(object sender, EventArgs e)
{
string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
bool wasSuccessful = false;
SelectFolderDialog dialog = new SelectFolderDialog();
Regex saveRegex = null;
string initialDir = "";
if (OS.IsWindows)
{
initialDir = Environment.GetEnvironmentVariable("LocalAppData");
home = home.Replace("\\", "\\\\"); // This is \ -> \\
saveRegex = new Regex($@"{home}\\AppData\\Local\\"); // This is to ensure, that the save directory is valid.
}
else if (OS.IsLinux)
{
initialDir = home + "/.config";
saveRegex = new Regex($@"{home}/\.config/"); // GMS hardcodes save into ~/.config on linux
}
else if (OS.IsMac)
{
initialDir = $@"{home}/Library/Application Support/";
saveRegex = new Regex($@"{home}/Library/Application Support/");
}
else
{
throw new NotSupportedException("The current operating system is not supported!");
}
dialog.Directory = initialDir;
// TODO: clean this while loop a little
string saveFolderPath = null;
while (!wasSuccessful)
{
if (dialog.ShowDialog(this) == DialogResult.Ok)
{
Match match = saveRegex.Match(dialog.Directory);
if (match.Success == false)
{
MessageBox.Show(this, Text.InvalidSaveDirectory);
}
else
{
wasSuccessful = true;
saveFolderPath = dialog.Directory.Replace(match.Value, "%localappdata%/");
// If we don't do this, custom save locations are going to fail on Linux
if (OS.IsWindows)
saveFolderPath = saveFolderPath.Replace("\\", "/");
// On Mac, we need to adjust the path
if (OS.IsMac)
saveFolderPath = saveFolderPath.Replace("com.yoyogames.am2r", "AM2R");
// if someone has a custom save path inside of am2r and creates these within game maker, they will always be lower case
// we need to adjust them here to lowercase as well, as otherwise launcher gets problems on nix systems
const string vanillaPrefix = "%localappdata%/AM2R/";
if (saveFolderPath.Contains(vanillaPrefix))
saveFolderPath = vanillaPrefix + saveFolderPath.Substring(vanillaPrefix.Length).ToLower();
}
}
else
{
wasSuccessful = true;
}
}
customSaveTextBox.Text = saveFolderPath;
UpdateCreateButton();
}
private void YYCCheckBox_CheckedChanged(object sender, EventArgs e)
{
// Don't do anything if it's been disabled
if (!yycCheckBox.Checked.Value)
return;
// Disable mac stuff, as its incompatible with yyc
macCheckBox.Checked = false;
macLabel.Visible = false;
macButton.Enabled = false;
modInfo.MacModPath = null;
}
private void MacCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (!yycCheckBox.Checked.Value)
{
OSCheckboxChanged(ProfileOperatingSystems.Mac);
}
else if (macCheckBox.Checked.Value)
{
MessageBox.Show(this, Text.YYCMacUnsupported, Text.Warning, MessageBoxButtons.OK, MessageBoxType.Warning);
macCheckBox.Checked = false;
}
}
private void OriginalZipButton_Click(object sender, EventArgs e)
{
// Open window to select AM2R 1.1
modInfo.AM2R11Path = SelectFile(Text.SelectAM2R11FileDialog, zipFileFilter);
originalZipLabel.Visible = modInfo.IsAM2R11Loaded;
UpdateCreateButton();
}
private void CreateButton_Click(object sender, EventArgs e)
{
if (String.IsNullOrWhiteSpace(nameTextBox.Text) || String.IsNullOrWhiteSpace(authorTextBox.Text) || String.IsNullOrWhiteSpace(versionTextBox.Text))
{
MessageBox.Show(this, Text.FieldsMissing, Text.Error, MessageBoxButtons.OK, MessageBoxType.Error);
return;
}
// Trim the mod name textbox so that it has no leading/ending whitespace.
// Also check for invalid file names
nameTextBox.Text = nameTextBox.Text.Trim();
if (Path.GetInvalidFileNameChars().Any(nameTextBox.Text.Contains))
{
MessageBox.Show(this, Text.NameInvalidCharacters + "\n" + String.Join("\n", Path.GetInvalidFileNameChars()));
return;
}
// Verify 1.1
IsZipAM2R11ReturnCodes result11 = Core.CheckIfZipIsAM2R11(modInfo.AM2R11Path);
if (result11 != IsZipAM2R11ReturnCodes.Successful)
{
MessageBox.Show(this, Text.AM2R11Invalid + " " + result11);
AbortPatch();
return;
}
createLabel.Visible = true;
createLabel.Text = Text.PackagingMods;
bool PromptAndSaveOSMod(ProfileOperatingSystems os)
{
string output;
using (SaveFileDialog saveFile = new SaveFileDialog { Title = String.Format(Text.SaveOSModProfile, GetLocalizedStringOfOS(os)), Filters = { zipFileFilter } })
{
if (saveFile.ShowDialog(this) == DialogResult.Ok)
{
output = saveFile.FileName;
}
else
{
createLabel.Text = Text.ModPackagingAborted;
return false;
}
}
// Some filepickers don't automatically set the file extension
if (!output.ToLower().EndsWith(".zip"))
output += ".zip";
LoadProfileParameters(os);
try
{
Core.CreateModPack(modInfo, output);
}
catch (Exception exception)
{
MessageBox.Show(this, exception.ToString(), Text.Error, MessageBoxButtons.OK, MessageBoxType.Error);
AbortPatch();
return false;
}
return true;
}
bool CheckForProfileXML(ZipArchive zipFile)
{
if (zipFile.Entries.All(f => f.Name != "profile.xml"))
return true;
DialogResult result = MessageBox.Show(this, Text.ProfileXMLFound, Text.Warning, MessageBoxButtons.YesNo, MessageBoxType.Warning);
if (result == DialogResult.Yes)
return true;
AbortPatch();
return false;
}
// TODO: look if its not possible to generalize these
if (windowsCheckBox.Checked.Value)
{
ZipArchive windowsZip = ZipFile.Open(modInfo.WindowsModPath, ZipArchiveMode.Read);
if (windowsZip.Entries.All(f => f.FullName != "AM2R.exe"))
{
// TODO: make method for these $1 replacements
DialogResult result = MessageBox.Show(this, String.Format(Text.ModdedGameNotFound, Text.Windows), Text.Warning, MessageBoxButtons.YesNo, MessageBoxType.Warning);
if (result != DialogResult.Yes)
{
AbortPatch();
return;
}
}
if (!CheckForProfileXML(windowsZip))
return;
if (!PromptAndSaveOSMod(ProfileOperatingSystems.Windows))
return;
}
if (linuxCheckBox.Checked.Value)
{
ZipArchive linuxZip = ZipFile.Open(modInfo.LinuxModPath, ZipArchiveMode.Read);
if (linuxZip.Entries.All(f => f.FullName != "AM2R") && linuxZip.Entries.All(f => f.FullName != "runner"))
{
DialogResult result = MessageBox.Show(this, String.Format(Text.ModdedGameNotFound, Text.Linux), Text.Warning, MessageBoxButtons.YesNo, MessageBoxType.Warning);
if (result != DialogResult.Yes)
{
AbortPatch();
return;
}
}
if (!CheckForProfileXML(linuxZip))
return;
if (!PromptAndSaveOSMod(ProfileOperatingSystems.Linux))
return;
}
if (macCheckBox.Checked.Value)
{
ZipArchive macZip = ZipFile.Open(modInfo.MacModPath, ZipArchiveMode.Read);
if (macZip.Entries.All(f => f.FullName != "AM2R.app/Contents/MacOS/Mac_Runner"))
{
DialogResult result = MessageBox.Show(this, String.Format(Text.ModdedGameNotFound, Text.Mac), Text.Warning, MessageBoxButtons.YesNo, MessageBoxType.Warning);
if (result != DialogResult.Yes)
AbortPatch();
}
if (!CheckForProfileXML(macZip))
return;
if (!PromptAndSaveOSMod(ProfileOperatingSystems.Mac))
return;
}
createLabel.Text = "Mod package(s) created!";
}
private void ModPacker_Closing(object sender, EventArgs e)
{
if (currentConfig.FillInContents)
{
currentConfig.Fields.ModName = nameTextBox.Text;
currentConfig.Fields.Author = authorTextBox.Text;
currentConfig.Fields.Version = versionTextBox.Text;
currentConfig.Fields.Notes = modNotesTextBox.Text;
currentConfig.Fields.UsesCustomSave = customSaveCheckBox.Checked.Value;
currentConfig.Fields.CustomSaveDir = customSaveTextBox.Text;
currentConfig.Fields.UsesCustomMusic = musicCheckBox.Checked.Value;
currentConfig.Fields.UsesYYC = yycCheckBox.Checked.Value;
currentConfig.Fields.SupportsWindows = windowsCheckBox.Checked.Value;
currentConfig.Fields.SupportsLinux = linuxCheckBox.Checked.Value;
currentConfig.Fields.SupportsMac = macCheckBox.Checked.Value;
currentConfig.Fields.SupportsAndroid = apkCheckBox.Checked.Value;
}
Config.SaveConfig(currentConfig);
}
#endregion
private static string GetLocalizedStringOfOS(ProfileOperatingSystems os)
{
return os switch
{
ProfileOperatingSystems.Windows => Text.Windows,
ProfileOperatingSystems.Linux => Text.Linux,
ProfileOperatingSystems.Mac => Text.Mac,
ProfileOperatingSystems.Android => Text.Android,
_ => "Unknown"
};
}
private void LoadProfileParameters(ProfileOperatingSystems operatingSystem)
{
// TODO: give the control events where they assign the values directly?
modInfo.Profile.Name = nameTextBox.Text;
modInfo.Profile.Author = authorTextBox.Text;
modInfo.Profile.Version = versionTextBox.Text;
modInfo.Profile.UsesCustomMusic = musicCheckBox.Checked.Value;
modInfo.Profile.UsesYYC = yycCheckBox.Checked.Value;
modInfo.Profile.SupportsAndroid = apkCheckBox.Checked.Value;
modInfo.Profile.ProfileNotes = modNotesTextBox.Text;
modInfo.Profile.OperatingSystem = operatingSystem.ToString();
if (customSaveCheckBox.Checked.Value && !String.IsNullOrWhiteSpace(customSaveTextBox.Text))
modInfo.Profile.SaveLocation = customSaveTextBox.Text;
else
modInfo.Profile.SaveLocation = "%localappdata%/AM2R";
if (operatingSystem == ProfileOperatingSystems.Linux)
{
modInfo.Profile.SaveLocation = modInfo.Profile.SaveLocation.Replace("%localappdata%", "~/.config");
}
else if (operatingSystem == ProfileOperatingSystems.Mac)
{
if (modInfo.Profile.SaveLocation.Contains("%localappdata%/AM2R"))
modInfo.Profile.SaveLocation = modInfo.Profile.SaveLocation.Replace("%localappdata%/AM2R", "~/Library/Application Support/com.yoyogames.am2r");
else
modInfo.Profile.SaveLocation = "~/Library/Application Support/com.yoyogames." + new DirectoryInfo(modInfo.Profile.SaveLocation).Name.ToLower();
}
}
private void AbortPatch()
{
// Set labels
createLabel.Text = Text.ModPackagingAborted;
}
private void UpdateCreateButton()
{
if (modInfo.IsAM2R11Loaded && // AM2R_11 zip must be provided
(!windowsCheckBox.Checked.Value || modInfo.IsWindowsModLoaded) && // either Windows is disabled OR windows is provided
(!apkCheckBox.Checked.Value || modInfo.IsApkModLoaded) && // either APK is disabled OR APK is provided
(!linuxCheckBox.Checked.Value || modInfo.IsLinuxModLoaded) && // either Linux is disabled OR linux is provided
(!macCheckBox.Checked.Value || modInfo.IsMacModLoaded) && // either Mac is disabled OR mac is provided
(modInfo.IsWindowsModLoaded || modInfo.IsLinuxModLoaded || modInfo.IsMacModLoaded) && // one desktop OS has to be selected
(!customSaveCheckBox.Checked.Value || customSaveTextBox.Text != "")) // either custom saves are disabled OR custom save is provided
createButton.Enabled = true;
else
createButton.Enabled = false;
}
private string SelectFile(string title, FileFilter filter)
{
using OpenFileDialog fileFinder = new OpenFileDialog
{
Filters = { filter },
Title = title,
CheckFileExists = true,
};
if (fileFinder.ShowDialog(this) != DialogResult.Ok)
return null;
string location = fileFinder.FileName;
return location;
}
private void OSCheckboxChanged(ProfileOperatingSystems os)
{
CheckBox osCheckbox = checkboxLookupTable[os];
Button osButton = buttonLookupTable[os];
Label osLabel = labelLookupTable[os];
FieldInfo osModPath = modPathLookupTable[os];
// If it was disabled, clean the appropriate attributes
osButton.Enabled = osCheckbox.Checked.Value;
if (!osCheckbox.Checked.Value)
{
osLabel.Visible = false;
osModPath.SetValue(modInfo, null);
}
UpdateCreateButton();
}
private void OSButtonClicked(ProfileOperatingSystems os)
{
string pickerMessage = String.Format(Text.SelectModdedFile, GetLocalizedStringOfOS(os), os == ProfileOperatingSystems.Android ? Text.APK : Text.Zip);
Label osLabel = labelLookupTable[os];
FieldInfo osModPath = modPathLookupTable[os];
PropertyInfo isOsModLoaded = isModLoadedLookupTable[os];
// Open window to select modded file
string selectedFile = SelectFile(pickerMessage, os == ProfileOperatingSystems.Android ? apkFileFilter : zipFileFilter);
osModPath.SetValue(modInfo, selectedFile);
osLabel.Visible = (bool)isOsModLoaded.GetValue(modInfo);
UpdateCreateButton();
}
}