From 679636f27513f819efc73df5c4373935123341c3 Mon Sep 17 00:00:00 2001 From: Miepee Date: Thu, 8 Dec 2022 01:01:42 +0100 Subject: [PATCH] more todos + move more methods into lib --- AM2RModPacker/ModPacker.cs | 272 ++++--------------------------------- AM2RModPackerLib/Core.cs | 247 ++++++++++++++++++++++++++++++++- 2 files changed, 269 insertions(+), 250 deletions(-) diff --git a/AM2RModPacker/ModPacker.cs b/AM2RModPacker/ModPacker.cs index d30eb70..c0d19a7 100644 --- a/AM2RModPacker/ModPacker.cs +++ b/AM2RModPacker/ModPacker.cs @@ -13,12 +13,10 @@ namespace AM2RModPacker; public partial class ModPacker : Form { private static readonly string version = Core.Version; - private const string originalMD5 = "f2b84fe5ba64cb64e284be1066ca08ee"; private bool isOriginalLoaded, isModLoaded, isApkLoaded, isLinuxLoaded; // TODO: do not get current directory, wont end well private readonly string localPath = Directory.GetCurrentDirectory(); private string originalPath, modPath, apkPath, linuxPath; - private readonly string[] DATAFILES_BLACKLIST = { "data.win", "AM2R.exe", "D3DX9_43.dll", "game.unx" }; private string saveFilePath; private readonly ModProfileXML profile; @@ -33,28 +31,31 @@ public partial class ModPacker : Form UpdateCreateButton(); } - //TODO: go through this method private void CustomSaveDataButton_Click(object sender, EventArgs e) { bool wasSuccessful = false; // TODO: make sure that the validation works on other platforms too - var saveRegex = new Regex(@"C:\\Users\\.*\\AppData\\Local\\"); //this is to ensure, that the save directory is valid. so far, this is only important for windows - + var winSaveRegex = new Regex(@"C:\\Users\\.*\\AppData\\Local\\"); //this is to ensure, that the save directory is valid. so far, this is only important for windows + var linSaveRegex = new Regex(@"$/\.config"); // Linux users can set their home directory wherever they want to, for each individual application + //TODO: var macSaveRegex var dialog = new SelectFolderDialog(); - // currently not implemented in eto - //dialog.InitialDirectory = Environment.GetEnvironmentVariable("LocalAppData"); + // TODO: .config on linux + dialog.Directory = Environment.GetEnvironmentVariable("LocalAppData"); while (!wasSuccessful) { if (dialog.ShowDialog(this) == DialogResult.Ok) { - var match = saveRegex.Match(dialog.Directory); + Match match; + match = winSaveRegex.Match(dialog.Directory); if (match.Success == false) { + // TODO: different error messages MessageBox.Show("Invalid Save Directory! Please choose one in %LocalAppData%"); } else { + //TODO: needs to be OS specific wasSuccessful = true; saveFilePath = dialog.Directory.Replace(match.Value, "%localappdata%/"); saveFilePath = saveFilePath.Replace("\\", "/"); // if we don't do this, custom save locations are going to fail on Linux @@ -149,7 +150,13 @@ public partial class ModPacker : Form } } LoadProfileParameters(ProfileOperatingSystems.Windows); - CreateModPack(profile, modPath, output); + + (bool successful, string errorcode) = Core.CreateModPack(profile, modPath,originalPath, apkPath, output); + if (!successful) + { + MessageBox.Show(errorcode, "Error", MessageBoxButtons.OK, MessageBoxType.Error); + AbortPatch(); + } if (linuxCheckBox.Checked.Value) { @@ -164,248 +171,19 @@ public partial class ModPacker : Form } } LoadProfileParameters(ProfileOperatingSystems.Linux); - CreateModPack(profile, linuxPath, output); - } - //TODO: mac - - createLabel.Text = "Mod package(s) created!"; - } - #endregion - - // TODO: go over thhis - private void CreateModPack( ModProfileXML profile, string input, string output) - { - // Cleanup in case of previous errors - if (Directory.Exists(Path.GetTempPath() + "/AM2RModPacker")) - Directory.Delete(Path.GetTempPath() + "/AM2RModPacker", true); - - // Create temp work folders - string tempPath, - tempOriginalPath, - tempModPath, - tempProfilePath; - - // We might not have permission to access to the temp directory, so we need to catch the exception. - try - { - tempPath = Directory.CreateDirectory(Path.GetTempPath() + "/AM2RModPacker").FullName; - tempOriginalPath = Directory.CreateDirectory(tempPath + "/original").FullName; - tempModPath = Directory.CreateDirectory(tempPath + "/mod").FullName; - tempProfilePath = Directory.CreateDirectory(tempPath + "/profile").FullName; - } - catch (System.Security.SecurityException) - { - MessageBox.Show("Could not create temp directory! Please run the application with administrator rights.", "Error", MessageBoxButtons.OK, MessageBoxType.Error); - AbortPatch(); - return; - } - - // Extract 1.1 and modded AM2R to their own directories in temp work - ZipFile.ExtractToDirectory(originalPath, tempOriginalPath); - ZipFile.ExtractToDirectory(input, tempModPath); - - if (Directory.Exists(tempModPath + "/AM2R")) - tempModPath += "/AM2R"; - - // Verify 1.1 with an MD5. If it does not match, exit cleanly and provide a warning window. - try - { - // TODO: dont. do what launcher does - string newMD5 = Core.CalculateMD5(tempOriginalPath + "/data.win"); - - if (newMD5 != originalMD5) + (successful, errorcode) = Core.CreateModPack(profile, linuxPath, originalPath, apkPath, output); + if (!successful) { - // Show error box - MessageBox.Show("1.1 data.win does not meet MD5 checksum! Mod packaging aborted.\n1.1 MD5: " + originalMD5 + "\nYour MD5: " + newMD5, "Error", MessageBoxButtons.OK, MessageBoxType.Error); + MessageBox.Show(errorcode, "Error", MessageBoxButtons.OK, MessageBoxType.Error); AbortPatch(); - return; - } - } - catch (FileNotFoundException) - { - // Show error message - MessageBox.Show("data.win not found! Are you sure you selected AM2R 1.1? Mod packaging aborted.", "Error", MessageBoxButtons.OK, MessageBoxType.Error); - AbortPatch(); - return; - } - - // Create AM2R.exe and data.win patches - if (profile.OperatingSystem == "Windows") - { - if (!File.Exists(tempModPath + "/AM2R.exe")) - { - var result = MessageBox.Show("Modded game not found, make sure it's not placed in any subfolders.\nCreated profile will likely not be installable, are you sure you want to continue?", "WARNING", MessageBoxButtons.YesNo, MessageBoxType.Warning); - if (result != DialogResult.Yes) - AbortPatch(); - } - - if (File.Exists(tempModPath + "profile.xml")) - { - var result = MessageBox.Show("profile.xml found. This file is used by the AM2RLauncher to determine profile stats and its inclusion may make the profile uninstallable. Are you sure you want to continue?", "WARNING", MessageBoxButtons.YesNo, MessageBoxType.Warning); - if (result != DialogResult.Yes) - AbortPatch(); - } - - if (profile.UsesYYC) - { - Core.CreatePatch(tempOriginalPath + "/data.win", tempModPath + "/AM2R.exe", tempProfilePath + "/AM2R.xdelta"); - } - else - { - Core.CreatePatch(tempOriginalPath + "/data.win", tempModPath + "/data.win", tempProfilePath + "/data.xdelta"); - Core.CreatePatch(tempOriginalPath + "/AM2R.exe", tempModPath + "/AM2R.exe", tempProfilePath + "/AM2R.xdelta"); - } - } - else if (profile.OperatingSystem == "Linux") - { - string runnerName = File.Exists(tempModPath + "/" + "AM2R") ? "AM2R" : "runner"; - - if (!File.Exists(tempModPath + "/" + runnerName)) - { - var result = MessageBox.Show("Modded Linux game not found, make sure it's not placed in any subfolders.\nCreated profile will likely not be installable, are you sure you want to continue?", "WARNING", MessageBoxButtons.YesNo, MessageBoxType.Warning); - if (result != DialogResult.Yes) - AbortPatch(); - } - - if (File.Exists(tempModPath + "profile.xml")) - { - var result = MessageBox.Show("profile.xml found. This file is used by the AM2RLauncher to determine profile stats and its inclusion may make the profile uninstallable. Are you sure you want to continue?", "WARNING", MessageBoxButtons.YesNo, MessageBoxType.Warning); - if (result != DialogResult.Yes) - AbortPatch(); } - - Core.CreatePatch(tempOriginalPath + "/data.win", tempModPath + "/assets/game.unx", tempProfilePath + "/game.xdelta"); - Core.CreatePatch(tempOriginalPath + "/AM2R.exe", tempModPath + "/" + runnerName, tempProfilePath + "/AM2R.xdelta"); } - // todo: mac - - // Create game.droid patch and wrapper if Android is supported - if (profile.Android) - { - string tempAndroid = Directory.CreateDirectory(tempPath + "/android").FullName; - - // Extract APK - // - java -jar apktool.jar d "%~dp0AM2RWrapper_old.apk" - - // Process startInfo - // TODO: cross platform - var procStartInfo = new ProcessStartInfo - { - FileName = "cmd.exe", - WorkingDirectory = tempAndroid, - Arguments = "/C java -jar \"" + localPath + "/utilities/android/apktool.jar\" d -f -o \"" + tempAndroid + "\" \"" + apkPath + "\"", - UseShellExecute = false, - CreateNoWindow = true - }; - - // Run process - using (var proc = new Process { StartInfo = procStartInfo }) - { - proc.Start(); - proc.WaitForExit(); - } - - // Create game.droid patch - Core.CreatePatch(tempOriginalPath + "/data.win", tempAndroid + "/assets/game.droid", tempProfilePath + "/droid.xdelta"); - - // Delete excess files in APK - - // Create whitelist - string[] whitelist = { "splash.png", "portrait_splash.png" }; - - // Get directory - var androidAssets = new DirectoryInfo(tempAndroid + "/assets"); - - - // Delete files - foreach (var file in androidAssets.GetFiles()) - { - if (file.Name.EndsWith(".ini") && file.Name != "modifiers.ini") - { - if (File.Exists(tempProfilePath + "/AM2R.ini")) - // This shouldn't be a problem... normally... - File.Delete(tempProfilePath + "/AM2R.ini"); - File.Copy(file.FullName, tempProfilePath + "/AM2R.ini"); - } - - if (!whitelist.Contains(file.Name)) - File.Delete(file.FullName); - } - - foreach (var dir in androidAssets.GetDirectories()) - Directory.Delete(dir.FullName, true); - - // Create wrapper - - // Process startInfo - // - java -jar apktool.jar b "%~dp0AM2RWrapper_old" -o "%~dp0AM2RWrapper.apk" - var procStartInfo2 = new ProcessStartInfo - { - FileName = "cmd.exe", - WorkingDirectory = tempAndroid, - Arguments = "/C java -jar \"" + localPath + "/utilities/android/apktool.jar\" b -f \"" + tempAndroid + "\" -o \"" + tempProfilePath + "/AM2RWrapper.apk\"", - UseShellExecute = false, - CreateNoWindow = true - }; - - // Run process - using (var proc = new Process { StartInfo = procStartInfo2 }) - { - proc.Start(); - proc.WaitForExit(); - } - - string tempAndroidProfilePath = tempProfilePath + "/android"; - Directory.CreateDirectory(tempAndroidProfilePath); - - File.Move(tempProfilePath + "/AM2RWrapper.apk", tempAndroidProfilePath + "/AM2RWrapper.apk"); - if (File.Exists(tempProfilePath + "/AM2R.ini")) - File.Move(tempProfilePath + "/AM2R.ini", tempAndroidProfilePath + "/AM2R.ini"); - } - - // Copy datafiles (exclude .ogg if custom music is not selected) - - var dirInfo = new DirectoryInfo(tempModPath); - if (profile.OperatingSystem == "Linux") - dirInfo = new DirectoryInfo(tempModPath + "/assets"); - - Directory.CreateDirectory(tempProfilePath + "/files_to_copy"); - - if (profile.UsesCustomMusic) - { - // Copy files, excluding the blacklist - Core.CopyFilesRecursive(dirInfo, DATAFILES_BLACKLIST, tempProfilePath + "/files_to_copy"); - } - else - { - // Get list of 1.1's music files - string[] musFiles = Directory.GetFiles(tempOriginalPath, "*.ogg").Select(file => Path.GetFileName(file)).ToArray(); - - if (profile.OperatingSystem == "Linux") - musFiles = Directory.GetFiles(tempOriginalPath, "*.ogg").Select(file => Path.GetFileName(file).ToLower()).ToArray(); - - - // Combine musFiles with the known datafiles for a blacklist - string[] blacklist = musFiles.Concat(DATAFILES_BLACKLIST).ToArray(); - - // Copy files, excluding the blacklist - Core.CopyFilesRecursive(dirInfo, blacklist, tempProfilePath + "/files_to_copy"); - } - - // Export profile as XML - string xmlOutput = Serializer.Serialize(profile); - File.WriteAllText(tempProfilePath + "/profile.xml", xmlOutput); - - // Compress temp folder to .zip - if (File.Exists(output)) - File.Delete(output); - - ZipFile.CreateFromDirectory(tempProfilePath, output); + //TODO: mac - // Delete temp folder - Directory.Delete(tempPath, true); + createLabel.Text = "Mod package(s) created!"; } - + #endregion + private void LoadProfileParameters(ProfileOperatingSystems operatingSystem) { profile.Name = nameTextBox.Text; @@ -448,9 +226,7 @@ public partial class ModPacker : Form if (Directory.Exists(Path.GetTempPath() + "/AM2RModPacker")) Directory.Delete(Path.GetTempPath() + "/AM2RModPacker", true); } - - - + private void UpdateCreateButton() { if (isOriginalLoaded && // AM2R_11 zip must be provided diff --git a/AM2RModPackerLib/Core.cs b/AM2RModPackerLib/Core.cs index 64cb95a..01cb7c4 100644 --- a/AM2RModPackerLib/Core.cs +++ b/AM2RModPackerLib/Core.cs @@ -1,6 +1,7 @@ using System.Diagnostics; using System.IO.Compression; using System.Security.Cryptography; +using AM2RModPackerLib.XML; namespace AM2RModPackerLib; @@ -14,10 +15,252 @@ public enum ProfileOperatingSystems public static class Core { public static readonly string Version = "2.0.3"; - + private const string originalMD5 = "f2b84fe5ba64cb64e284be1066ca08ee"; + private static readonly string[] DATAFILES_BLACKLIST = { "data.win", "AM2R.exe", "D3DX9_43.dll", "game.unx" }; private static readonly string localPath = Directory.GetCurrentDirectory(); - + // TODO: go over thhis and clean + public static (bool, string) CreateModPack(ModProfileXML profile, string input, string originalPath, string apkPath, string output) + { + // Cleanup in case of previous errors + if (Directory.Exists(Path.GetTempPath() + "/AM2RModPacker")) + Directory.Delete(Path.GetTempPath() + "/AM2RModPacker", true); + + // Create temp work folders + string tempPath, + tempOriginalPath, + tempModPath, + tempProfilePath; + + // We might not have permission to access to the temp directory, so we need to catch the exception. + try + { + tempPath = Directory.CreateDirectory(Path.GetTempPath() + "/AM2RModPacker").FullName; + tempOriginalPath = Directory.CreateDirectory(tempPath + "/original").FullName; + tempModPath = Directory.CreateDirectory(tempPath + "/mod").FullName; + tempProfilePath = Directory.CreateDirectory(tempPath + "/profile").FullName; + } + catch (System.Security.SecurityException) + { + return (false, "Could not create temp directory! Please run the application with administrator rights."); + } + + // Extract 1.1 and modded AM2R to their own directories in temp work + ZipFile.ExtractToDirectory(originalPath, tempOriginalPath); + ZipFile.ExtractToDirectory(input, tempModPath); + + if (Directory.Exists(tempModPath + "/AM2R")) + tempModPath += "/AM2R"; + + // Verify 1.1 with an MD5. If it does not match, exit cleanly and provide a warning window. + try + { + // TODO: dont. do what launcher does + string newMD5 = Core.CalculateMD5(tempOriginalPath + "/data.win"); + + if (newMD5 != originalMD5) + { + return (false, "1.1 data.win does not meet MD5 checksum! Mod packaging aborted.\n1.1 MD5: " + originalMD5 + "\nYour MD5: " + newMD5); + } + } + catch (FileNotFoundException) + { + return (false, "data.win not found! Are you sure you selected AM2R 1.1? Mod packaging aborted."); + } + + // Create AM2R.exe and data.win patches + if (profile.OperatingSystem == "Windows") + { + if (!File.Exists(tempModPath + "/AM2R.exe")) + { //TODO: put this onto the outer method + /* + var result = MessageBox.Show("Modded game not found, make sure it's not placed in any subfolders.\nCreated profile will likely not be installable, are you sure you want to continue?", "WARNING", MessageBoxButtons.YesNo, MessageBoxType.Warning); + if (result != DialogResult.Yes) + { + AbortPatch(); + return (false, ""); + }*/ + } + + if (File.Exists(tempModPath + "profile.xml")) + { + //TODO: put this onto the outer method + /*var result = MessageBox.Show("profile.xml found. This file is used by the AM2RLauncher to determine profile stats and its inclusion may make the profile uninstallable. Are you sure you want to continue?", "WARNING", MessageBoxButtons.YesNo, MessageBoxType.Warning); + if (result != DialogResult.Yes) + { + AbortPatch(); + return (false, ""); + }*/ + } + + if (profile.UsesYYC) + { + Core.CreatePatch(tempOriginalPath + "/data.win", tempModPath + "/AM2R.exe", tempProfilePath + "/AM2R.xdelta"); + } + else + { + Core.CreatePatch(tempOriginalPath + "/data.win", tempModPath + "/data.win", tempProfilePath + "/data.xdelta"); + Core.CreatePatch(tempOriginalPath + "/AM2R.exe", tempModPath + "/AM2R.exe", tempProfilePath + "/AM2R.xdelta"); + } + } + else if (profile.OperatingSystem == "Linux") + { + string runnerName = File.Exists(tempModPath + "/" + "AM2R") ? "AM2R" : "runner"; + + if (!File.Exists(tempModPath + "/" + runnerName)) + {//TODO: put this onto the outer method + /* + var result = MessageBox.Show("Modded Linux game not found, make sure it's not placed in any subfolders.\nCreated profile will likely not be installable, are you sure you want to continue?", "WARNING", MessageBoxButtons.YesNo, MessageBoxType.Warning); + if (result != DialogResult.Yes) + { + AbortPatch(); + return (false, ""); + }*/ + } + + if (File.Exists(tempModPath + "profile.xml")) + { + //TODO: put this onto the outer method + /* var result = MessageBox.Show("profile.xml found. This file is used by the AM2RLauncher to determine profile stats and its inclusion may make the profile uninstallable. Are you sure you want to continue?", "WARNING", MessageBoxButtons.YesNo, MessageBoxType.Warning); + if (result != DialogResult.Yes) + { + AbortPatch(); + return (false, ""); + }*/ + } + + Core.CreatePatch(tempOriginalPath + "/data.win", tempModPath + "/assets/game.unx", tempProfilePath + "/game.xdelta"); + Core.CreatePatch(tempOriginalPath + "/AM2R.exe", tempModPath + "/" + runnerName, tempProfilePath + "/AM2R.xdelta"); + } + // todo: mac + + // Create game.droid patch and wrapper if Android is supported + if (profile.Android) + { + string tempAndroid = Directory.CreateDirectory(tempPath + "/android").FullName; + + // Extract APK + // - java -jar apktool.jar d "%~dp0AM2RWrapper_old.apk" + + // Process startInfo + // TODO: cross platform + var procStartInfo = new ProcessStartInfo + { + FileName = "cmd.exe", + WorkingDirectory = tempAndroid, + Arguments = "/C java -jar \"" + localPath + "/utilities/android/apktool.jar\" d -f -o \"" + tempAndroid + "\" \"" + apkPath + "\"", + UseShellExecute = false, + CreateNoWindow = true + }; + + // Run process + using (var proc = new Process { StartInfo = procStartInfo }) + { + proc.Start(); + proc.WaitForExit(); + } + + // Create game.droid patch + Core.CreatePatch(tempOriginalPath + "/data.win", tempAndroid + "/assets/game.droid", tempProfilePath + "/droid.xdelta"); + + // Delete excess files in APK + + // Create whitelist + string[] whitelist = { "splash.png", "portrait_splash.png" }; + + // Get directory + var androidAssets = new DirectoryInfo(tempAndroid + "/assets"); + + + // Delete files + foreach (var file in androidAssets.GetFiles()) + { + if (file.Name.EndsWith(".ini") && file.Name != "modifiers.ini") + { + if (File.Exists(tempProfilePath + "/AM2R.ini")) + // This shouldn't be a problem... normally... + File.Delete(tempProfilePath + "/AM2R.ini"); + File.Copy(file.FullName, tempProfilePath + "/AM2R.ini"); + } + + if (!whitelist.Contains(file.Name)) + File.Delete(file.FullName); + } + + foreach (var dir in androidAssets.GetDirectories()) + Directory.Delete(dir.FullName, true); + + // Create wrapper + + // Process startInfo + // - java -jar apktool.jar b "%~dp0AM2RWrapper_old" -o "%~dp0AM2RWrapper.apk" + var procStartInfo2 = new ProcessStartInfo + { + FileName = "cmd.exe", + WorkingDirectory = tempAndroid, + Arguments = "/C java -jar \"" + localPath + "/utilities/android/apktool.jar\" b -f \"" + tempAndroid + "\" -o \"" + tempProfilePath + "/AM2RWrapper.apk\"", + UseShellExecute = false, + CreateNoWindow = true + }; + + // Run process + using (var proc = new Process { StartInfo = procStartInfo2 }) + { + proc.Start(); + proc.WaitForExit(); + } + + string tempAndroidProfilePath = tempProfilePath + "/android"; + Directory.CreateDirectory(tempAndroidProfilePath); + + File.Move(tempProfilePath + "/AM2RWrapper.apk", tempAndroidProfilePath + "/AM2RWrapper.apk"); + if (File.Exists(tempProfilePath + "/AM2R.ini")) + File.Move(tempProfilePath + "/AM2R.ini", tempAndroidProfilePath + "/AM2R.ini"); + } + + // Copy datafiles (exclude .ogg if custom music is not selected) + + var dirInfo = new DirectoryInfo(tempModPath); + if (profile.OperatingSystem == "Linux") + dirInfo = new DirectoryInfo(tempModPath + "/assets"); + + Directory.CreateDirectory(tempProfilePath + "/files_to_copy"); + + if (profile.UsesCustomMusic) + { + // Copy files, excluding the blacklist + Core.CopyFilesRecursive(dirInfo, DATAFILES_BLACKLIST, tempProfilePath + "/files_to_copy"); + } + else + { + // Get list of 1.1's music files + string[] musFiles = Directory.GetFiles(tempOriginalPath, "*.ogg").Select(file => Path.GetFileName(file)).ToArray(); + + if (profile.OperatingSystem == "Linux") + musFiles = Directory.GetFiles(tempOriginalPath, "*.ogg").Select(file => Path.GetFileName(file).ToLower()).ToArray(); + + + // Combine musFiles with the known datafiles for a blacklist + string[] blacklist = musFiles.Concat(DATAFILES_BLACKLIST).ToArray(); + + // Copy files, excluding the blacklist + Core.CopyFilesRecursive(dirInfo, blacklist, tempProfilePath + "/files_to_copy"); + } + + // Export profile as XML + string xmlOutput = Serializer.Serialize(profile); + File.WriteAllText(tempProfilePath + "/profile.xml", xmlOutput); + + // Compress temp folder to .zip + if (File.Exists(output)) + File.Delete(output); + + ZipFile.CreateFromDirectory(tempProfilePath, output); + + // Delete temp folder + Directory.Delete(tempPath, true); + return (true, ""); + } public static void CreatePatch(string original, string modified, string output)