diff --git a/AM2RModPacker.csproj b/AM2RModPacker.csproj index ff06b12..fc43b2b 100644 --- a/AM2RModPacker.csproj +++ b/AM2RModPacker.csproj @@ -51,8 +51,14 @@ icon64.ico - - packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll + + packages\Microsoft.WindowsAPICodePack-Core.1.1.0.0\lib\Microsoft.WindowsAPICodePack.dll + + + packages\Microsoft.WindowsAPICodePack-Shell.1.1.0.0\lib\Microsoft.WindowsAPICodePack.Shell.dll + + + packages\Microsoft.WindowsAPICodePack-Shell.1.1.0.0\lib\Microsoft.WindowsAPICodePack.ShellExtensions.dll @@ -68,15 +74,16 @@ - Form ModPacker.cs + + ModPacker.cs @@ -100,6 +107,9 @@ Settings.settings True + + PreserveNewest + PreserveNewest @@ -131,5 +141,6 @@ false + \ No newline at end of file diff --git a/ModPacker.Designer.cs b/ModPacker.Designer.cs index 60bb452..ef9fdc6 100644 --- a/ModPacker.Designer.cs +++ b/ModPacker.Designer.cs @@ -42,12 +42,16 @@ this.NameTextBox = new System.Windows.Forms.TextBox(); this.label1 = new System.Windows.Forms.Label(); this.panel3 = new System.Windows.Forms.Panel(); + this.LinuxCheckBox = new System.Windows.Forms.CheckBox(); + this.WindowsCheckBox = new System.Windows.Forms.CheckBox(); this.ApkLabel = new System.Windows.Forms.Label(); this.ApkButton = new System.Windows.Forms.Button(); this.AndroidCheckBox = new System.Windows.Forms.CheckBox(); this.YYCCheckBox = new System.Windows.Forms.CheckBox(); this.SaveCheckBox = new System.Windows.Forms.CheckBox(); this.MusicCheckBox = new System.Windows.Forms.CheckBox(); + this.CustomSaveDataButton = new System.Windows.Forms.Button(); + this.SaveDirectoryLabel = new System.Windows.Forms.Label(); this.panel1.SuspendLayout(); this.panel2.SuspendLayout(); this.panel3.SuspendLayout(); @@ -175,6 +179,10 @@ // // panel3 // + this.panel3.Controls.Add(this.SaveDirectoryLabel); + this.panel3.Controls.Add(this.CustomSaveDataButton); + this.panel3.Controls.Add(this.LinuxCheckBox); + this.panel3.Controls.Add(this.WindowsCheckBox); this.panel3.Controls.Add(this.ApkLabel); this.panel3.Controls.Add(this.ApkButton); this.panel3.Controls.Add(this.AndroidCheckBox); @@ -186,6 +194,30 @@ this.panel3.Size = new System.Drawing.Size(291, 111); this.panel3.TabIndex = 2; // + // LinuxCheckBox + // + this.LinuxCheckBox.AutoSize = true; + this.LinuxCheckBox.Location = new System.Drawing.Point(194, 66); + this.LinuxCheckBox.Name = "LinuxCheckBox"; + this.LinuxCheckBox.Size = new System.Drawing.Size(51, 17); + this.LinuxCheckBox.TabIndex = 9; + this.LinuxCheckBox.Text = "Linux"; + this.LinuxCheckBox.UseVisualStyleBackColor = true; + this.LinuxCheckBox.CheckedChanged += new System.EventHandler(this.LinuxCheckBox_CheckedChanged); + // + // WindowsCheckBox + // + this.WindowsCheckBox.AutoSize = true; + this.WindowsCheckBox.Checked = true; + this.WindowsCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; + this.WindowsCheckBox.Location = new System.Drawing.Point(194, 45); + this.WindowsCheckBox.Name = "WindowsCheckBox"; + this.WindowsCheckBox.Size = new System.Drawing.Size(70, 17); + this.WindowsCheckBox.TabIndex = 8; + this.WindowsCheckBox.Text = "Windows"; + this.WindowsCheckBox.UseVisualStyleBackColor = true; + this.WindowsCheckBox.CheckedChanged += new System.EventHandler(this.WindowsCheckBox_CheckedChanged); + // // ApkLabel // this.ApkLabel.AutoSize = true; @@ -210,7 +242,6 @@ // AndroidCheckBox // this.AndroidCheckBox.AutoSize = true; - this.AndroidCheckBox.Enabled = false; this.AndroidCheckBox.Location = new System.Drawing.Point(4, 66); this.AndroidCheckBox.Name = "AndroidCheckBox"; this.AndroidCheckBox.Size = new System.Drawing.Size(107, 17); @@ -232,17 +263,18 @@ // SaveCheckBox // this.SaveCheckBox.AutoSize = true; - this.SaveCheckBox.Location = new System.Drawing.Point(4, 24); + this.SaveCheckBox.Location = new System.Drawing.Point(4, 3); this.SaveCheckBox.Name = "SaveCheckBox"; this.SaveCheckBox.Size = new System.Drawing.Size(163, 17); this.SaveCheckBox.TabIndex = 3; this.SaveCheckBox.Text = "Uses custom savedata folder"; this.SaveCheckBox.UseVisualStyleBackColor = true; + this.SaveCheckBox.CheckedChanged += new System.EventHandler(this.SaveCheckBox_CheckedChanged); // // MusicCheckBox // this.MusicCheckBox.AutoSize = true; - this.MusicCheckBox.Location = new System.Drawing.Point(4, 3); + this.MusicCheckBox.Location = new System.Drawing.Point(4, 24); this.MusicCheckBox.Margin = new System.Windows.Forms.Padding(2, 3, 3, 3); this.MusicCheckBox.Name = "MusicCheckBox"; this.MusicCheckBox.Size = new System.Drawing.Size(117, 17); @@ -250,6 +282,28 @@ this.MusicCheckBox.Text = "Uses custom music"; this.MusicCheckBox.UseVisualStyleBackColor = true; // + // CustomSaveDataButton + // + this.CustomSaveDataButton.Enabled = false; + this.CustomSaveDataButton.Location = new System.Drawing.Point(194, 3); + this.CustomSaveDataButton.Name = "CustomSaveDataButton"; + this.CustomSaveDataButton.Size = new System.Drawing.Size(77, 19); + this.CustomSaveDataButton.TabIndex = 10; + this.CustomSaveDataButton.Text = "Select folder"; + this.CustomSaveDataButton.UseVisualStyleBackColor = true; + this.CustomSaveDataButton.Click += new System.EventHandler(this.CustomSaveDataButton_Click); + // + // SaveDirectoryLabel + // + this.SaveDirectoryLabel.AutoSize = true; + this.SaveDirectoryLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.SaveDirectoryLabel.Location = new System.Drawing.Point(118, 25); + this.SaveDirectoryLabel.Name = "SaveDirectoryLabel"; + this.SaveDirectoryLabel.Size = new System.Drawing.Size(87, 13); + this.SaveDirectoryLabel.TabIndex = 11; + this.SaveDirectoryLabel.Text = "SaveDirectory"; + this.SaveDirectoryLabel.Visible = false; + // // ModPacker // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -296,6 +350,10 @@ private System.Windows.Forms.CheckBox MusicCheckBox; private System.Windows.Forms.Label ApkLabel; private System.Windows.Forms.Label CreateLabel; + private System.Windows.Forms.CheckBox LinuxCheckBox; + private System.Windows.Forms.CheckBox WindowsCheckBox; + private System.Windows.Forms.Button CustomSaveDataButton; + private System.Windows.Forms.Label SaveDirectoryLabel; } } diff --git a/ModPacker.cs b/ModPacker.cs index 635ce65..476e6b9 100644 --- a/ModPacker.cs +++ b/ModPacker.cs @@ -11,7 +11,8 @@ using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Diagnostics; -using Newtonsoft.Json; +using Microsoft.WindowsAPICodePack.Dialogs; +using System.Text.RegularExpressions; namespace AM2R_ModPacker { @@ -20,12 +21,13 @@ namespace AM2R_ModPacker private static readonly string ORIGINAL_MD5 = "f2b84fe5ba64cb64e284be1066ca08ee"; private bool isOriginalLoaded, isModLoaded, isApkLoaded; private string localPath, originalPath, modPath, apkPath; - private static readonly string[] DATAFILES_BLACKLIST = { "data.win", "AM2R.exe", "D3DX9_43.dll" }; - private ModProfile profile; + private static readonly string[] DATAFILES_BLACKLIST = { "data.win", "AM2R.exe", "D3DX9_43.dll", "game.unx" }; + private static string saveFilePath = null; + private ModProfileXML profile; public ModPacker() { InitializeComponent(); - profile = new ModProfile(1, "", "", false, "default", false, false); + profile = new ModProfileXML(1, "", "", false, "default", false, false, "path"); // (1, "", "", false, "default", false, false); isOriginalLoaded = false; isModLoaded = false; isApkLoaded = false; @@ -63,7 +65,7 @@ namespace AM2R_ModPacker string output; - if (profile.name == "" || profile.author == "") + if (profile.Name == "" || profile.Author == "") { MessageBox.Show("Text field missing! Mod packaging aborted.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; @@ -144,19 +146,27 @@ namespace AM2R_ModPacker } // Create AM2R.exe and data.win patches - if (profile.usesYYC) + if (profile.OperatingSystem == "Windows") { - CreatePatch(tempOriginalPath + "\\data.win", tempModPath + "\\AM2R.exe", tempProfilePath + "\\AM2R.xdelta"); + if (profile.UsesYYC) + { + CreatePatch(tempOriginalPath + "\\data.win", tempModPath + "\\AM2R.exe", tempProfilePath + "\\AM2R.xdelta"); + } + else + { + CreatePatch(tempOriginalPath + "\\data.win", tempModPath + "\\data.win", tempProfilePath + "\\data.xdelta"); + + CreatePatch(tempOriginalPath + "\\AM2R.exe", tempModPath + "\\AM2R.exe", tempProfilePath + "\\AM2R.xdelta"); + } } - else + else if (profile.OperatingSystem == "Linux") { - CreatePatch(tempOriginalPath + "\\data.win", tempModPath + "\\data.win", tempProfilePath + "\\data.xdelta"); - - CreatePatch(tempOriginalPath + "\\AM2R.exe", tempModPath + "\\AM2R.exe", tempProfilePath + "\\AM2R.xdelta"); + CreatePatch(tempOriginalPath + "\\data.win", tempModPath + "\\assets\\game.unx", tempProfilePath + "\\game.xdelta"); + CreatePatch(tempOriginalPath + "\\AM2R.exe", tempModPath + "\\AM2R", tempProfilePath + "\\AM2R.xdelta"); } // Create game.droid patch and wrapper if Android is supported - if (profile.android) + if (profile.Android) { string tempAndroid = Directory.CreateDirectory(tempPath + "\\android").FullName; @@ -240,15 +250,23 @@ namespace AM2R_ModPacker proc.WaitForExit(); } + string tempAndroidProfilePath = tempProfilePath + "\\android"; + Directory.CreateDirectory(tempAndroidProfilePath); + + File.Move(tempProfilePath + "\\AM2RWrapper.apk", tempAndroidProfilePath + "\\AM2RWrapper.apk"); + File.Move(tempProfilePath + "\\AM2R.ini", tempAndroidProfilePath + "\\AM2R.ini"); + File.Move(localPath + "\\utilities\\android\\apktool.yml", tempAndroidProfilePath + "\\apktool.yml"); } // Copy datafiles (exclude .ogg if custom music is not selected) DirectoryInfo dinfo = new DirectoryInfo(tempModPath); + if (profile.OperatingSystem == "Linux") + dinfo = new DirectoryInfo(tempModPath + "\\assets"); Directory.CreateDirectory(tempProfilePath + "\\files_to_copy"); - if (profile.usesCustomMusic) + if (profile.UsesCustomMusic) { // Copy files, excluding the blacklist CopyFilesRecursive(dinfo, DATAFILES_BLACKLIST, tempProfilePath + "\\files_to_copy"); @@ -258,16 +276,20 @@ namespace AM2R_ModPacker // 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 CopyFilesRecursive(dinfo, blacklist, tempProfilePath + "\\files_to_copy"); - } + } // Export profile as JSON - string jsonOutput = JsonConvert.SerializeObject(profile); - File.WriteAllText(tempProfilePath + "\\modmeta.json", jsonOutput); + string xmlOutput = Serializer.Serialize(profile); ;// JsonConvert.SerializeObject(profile); + File.WriteAllText(tempProfilePath + "\\profile.xml", xmlOutput); // Compress temp folder to .zip if (File.Exists(output)) @@ -299,22 +321,38 @@ namespace AM2R_ModPacker UpdateCreateButton(); } + private void WindowsCheckBox_CheckedChanged(object sender, EventArgs e) + { + LinuxCheckBox.Checked = !WindowsCheckBox.Checked; + } + + private void LinuxCheckBox_CheckedChanged(object sender, EventArgs e) + { + WindowsCheckBox.Checked = !LinuxCheckBox.Checked; + } + + private void SaveCheckBox_CheckedChanged(object sender, EventArgs e) + { + CustomSaveDataButton.Enabled = SaveCheckBox.Checked; + } + #endregion private void LoadProfileParameters() { - profile.name = NameTextBox.Text; - profile.author = AuthorTextBox.Text; - profile.usesCustomMusic = MusicCheckBox.Checked; - profile.usesYYC = YYCCheckBox.Checked; - profile.android = AndroidCheckBox.Checked; - if (SaveCheckBox.Checked) + profile.Name = NameTextBox.Text; + profile.Author = AuthorTextBox.Text; + profile.UsesCustomMusic = MusicCheckBox.Checked; + profile.UsesYYC = YYCCheckBox.Checked; + profile.Android = AndroidCheckBox.Checked; + profile.OperatingSystem = WindowsCheckBox.Checked ? "Windows" : "Linux"; + if (SaveCheckBox.Checked && saveFilePath != null) { - profile.saveLocation = "custom"; + profile.SaveLocation = saveFilePath; } else { - profile.saveLocation = "default"; + profile.SaveLocation = "default"; } } @@ -327,12 +365,14 @@ namespace AM2R_ModPacker originalPath = ""; modPath = ""; apkPath = ""; + saveFilePath = null; // Set labels CreateLabel.Text = "Mod packaging aborted!"; OriginalLabel.Visible = false; ModLabel.Visible = false; ApkLabel.Visible = false; + SaveDirectoryLabel.Visible = false; // Remove temp directory if (Directory.Exists(Path.GetTempPath() + "\\AM2RModPacker")) @@ -341,6 +381,38 @@ namespace AM2R_ModPacker } } + private void CustomSaveDataButton_Click(object sender, EventArgs e) + { + bool wasSuccessfull = false; + Regex 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 + + // we either use this nuget package. Or we choose a very bad native solution. Went for nuget instead. + CommonOpenFileDialog dialog = new CommonOpenFileDialog(); + dialog.InitialDirectory = Environment.GetEnvironmentVariable("LocalAppData"); + dialog.IsFolderPicker = true; + while (!wasSuccessfull) + { + if (dialog.ShowDialog() == CommonFileDialogResult.Ok) + { + Match match = saveRegex.Match(dialog.FileName); + if (match.Success == false) + MessageBox.Show("Invalid Save Directory! Please choose one in %LocalAppData%"); + else + { + wasSuccessfull = true; + saveFilePath = dialog.FileName.Replace(match.Value, "%LOCALAPPDATA%"); + } + } + else + { + wasSuccessfull = true; + saveFilePath = null; + } + } + SaveDirectoryLabel.Visible = wasSuccessfull; + SaveDirectoryLabel.Text = saveFilePath; + } + private void CopyFilesRecursive(DirectoryInfo source, string[] blacklist, string destination) { foreach (FileInfo file in source.GetFiles()) diff --git a/ModProfile.cs b/ModProfile.cs deleted file mode 100644 index f3ca61c..0000000 --- a/ModProfile.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace AM2R_ModPacker -{ - internal class ModProfile - { - public int version { get; set; } - public string name { get; set; } - public string author { get; set; } - public bool usesCustomMusic { get; set; } - public string saveLocation { get; set; } - public bool android { get; set; } - public bool usesYYC { get; set; } - public ModProfile(int version, string name, string author, bool usesCustomMusic, string saveLocation, bool android, bool usesYYC) - { - this.version = version; - this.name = name; - this.author = author; - this.usesCustomMusic = usesCustomMusic; - this.saveLocation = saveLocation; - this.android = android; - this.usesYYC = usesYYC; - } - } -} diff --git a/XML/ModProfileXML.cs b/XML/ModProfileXML.cs new file mode 100644 index 0000000..b50bd0a --- /dev/null +++ b/XML/ModProfileXML.cs @@ -0,0 +1,60 @@ +using System; +using System.Xml.Serialization; + +namespace AM2R_ModPacker +{ + /// + /// Class that handles how the modmeta settings are saved as XML. + /// + [Serializable] + [XmlRoot("message")] + public class ModProfileXML + { + [XmlAttribute("Version")] + public decimal Version + { get; set; } + + [XmlAttribute("Name")] + public string Name + { get; set; } + + [XmlAttribute("Author")] + public string Author + { get; set; } + + [XmlAttribute("UsesCustomMusic")] + public bool UsesCustomMusic + { get; set; } + + [XmlAttribute("SaveLocation")] + public string SaveLocation + { get; set; } + + [XmlAttribute("SupportsAndroid")] + public bool Android + { get; set; } + + [XmlAttribute("UsesYYC")] + public bool UsesYYC + { get; set; } + + [XmlAttribute("OperatingSystem")] + public string OperatingSystem + { get; set; } + + public ModProfileXML() + { } + + public ModProfileXML(decimal version, string name, string author, bool usesCustomMusic, string saveLocation, bool android, bool usesYYC, string operatingSystem) + { + Version = version; + Name = name; + Author = author; + UsesCustomMusic = usesCustomMusic; + SaveLocation = saveLocation; + Android = android; + UsesYYC = usesYYC; + OperatingSystem = operatingSystem; + } + } +} diff --git a/XML/Serializer.cs b/XML/Serializer.cs new file mode 100644 index 0000000..4053890 --- /dev/null +++ b/XML/Serializer.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Web; +using System.Text; + +namespace AM2R_ModPacker +{ + /// + /// The Serializer class, that serializes to and deserializes from XML files. + /// + public class Serializer + { + /// + /// Serializes as a to XML. + /// + /// The class to serialize to. + /// The object that will be serialized. + /// The serialized XML as a . + public static string Serialize(object item) + { + Type t = typeof(T); + MemoryStream memStream = new MemoryStream(); + System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(t); + + serializer.Serialize(memStream, item); + + string xml = Encoding.UTF8.GetString(memStream.ToArray()); + + memStream.Flush(); + memStream.Close(); + memStream.Dispose(); + memStream = null; + + return xml; + } + + /// + /// Deserialize into an object of class that can be assigned. + /// + /// The class that will be deserialized to. + /// An XML that will be deserialized. + /// A deserialized object of class from . + public static T Deserialize(string xmlString) + { + Type t = typeof(T); + using (MemoryStream memStream = new MemoryStream(Encoding.UTF8.GetBytes(xmlString))) + { + System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(t); + return (T)serializer.Deserialize(memStream); + } + } + } +} \ No newline at end of file diff --git a/packages.config b/packages.config index a9de8b5..d469167 100644 --- a/packages.config +++ b/packages.config @@ -1,4 +1,5 @@  - + + \ No newline at end of file diff --git a/utilities/android/apktool.yml b/utilities/android/apktool.yml new file mode 100644 index 0000000..0a1dac5 --- /dev/null +++ b/utilities/android/apktool.yml @@ -0,0 +1,24 @@ +!!brut.androlib.meta.MetaInfo +apkFileName: AM2RWrapper.apk +compressionType: false +doNotCompress: +- resources.arsc +- ogg +isFrameworkApk: false +packageInfo: + forcedPackageId: '127' + renameManifestPackage: null +sdkInfo: + minSdkVersion: '9' + targetSdkVersion: '23' +sharedLibrary: false +sparseResources: true +unknownFiles: {} +usesFramework: + ids: + - 1 + tag: null +version: 2.5.0 +versionInfo: + versionCode: '1005012' + versionName: 1.5.2