Merge pull request #6 from Miepee/eto

Move project from winforms to Eto
pull/9/head
Lojemiru 4 years ago committed by GitHub
commit 57f17f708f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28803.156
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AM2RModPacker", "AM2RModPacker.csproj", "{7C7450E2-D2AE-4DA3-B8E3-FC953F50A0C7}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AM2RModPacker", "AM2RModPacker\AM2RModPacker.csproj", "{13E58D3E-D34E-4354-B5D3-AC43B05E7212}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -11,10 +11,10 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7C7450E2-D2AE-4DA3-B8E3-FC953F50A0C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C7450E2-D2AE-4DA3-B8E3-FC953F50A0C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C7450E2-D2AE-4DA3-B8E3-FC953F50A0C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C7450E2-D2AE-4DA3-B8E3-FC953F50A0C7}.Release|Any CPU.Build.0 = Release|Any CPU
{13E58D3E-D34E-4354-B5D3-AC43B05E7212}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{13E58D3E-D34E-4354-B5D3-AC43B05E7212}.Debug|Any CPU.Build.0 = Debug|Any CPU
{13E58D3E-D34E-4354-B5D3-AC43B05E7212}.Release|Any CPU.ActiveCfg = Release|Any CPU
{13E58D3E-D34E-4354-B5D3-AC43B05E7212}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

@ -1,146 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{7C7450E2-D2AE-4DA3-B8E3-FC953F50A0C7}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>AM2RModPacker</RootNamespace>
<AssemblyName>AM2RModPacker</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
<IsWebBootstrapper>false</IsWebBootstrapper>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>2.0.1.0</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>icon64.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.WindowsAPICodePack, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\WindowsAPICodePack-Core.1.1.1\lib\Microsoft.WindowsAPICodePack.dll</HintPath>
</Reference>
<Reference Include="Microsoft.WindowsAPICodePack.Shell, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\WindowsAPICodePack-Shell.1.1.1\lib\Microsoft.WindowsAPICodePack.Shell.dll</HintPath>
</Reference>
<Reference Include="Microsoft.WindowsAPICodePack.ShellExtensions, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\Microsoft.WindowsAPICodePack-Shell.1.1.0.0\lib\Microsoft.WindowsAPICodePack.ShellExtensions.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ModPacker.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="ModPacker.Designer.cs">
<DependentUpon>ModPacker.cs</DependentUpon>
</Compile>
<Compile Include="XML\ModProfileXML.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="XML\Serializer.cs" />
<EmbeddedResource Include="ModPacker.resx">
<DependentUpon>ModPacker.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<None Include="utilities\android\apktool.yml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="utilities\android\LICENSE">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Content Include="icon64.ico" />
<Content Include="utilities\android\apktool.jar">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="utilities\xdelta\LICENSE">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<Content Include="utilities\xdelta\xdelta3.exe">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.7.2">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4.7.2 %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

@ -0,0 +1,51 @@
<Project Sdk="Eto.Forms/2.7.2">
<!--
Set the BuildPlatform property to the Eto platform you wish to build for.
The default is the platform you are building on.
Valid values: Wpf, Windows, Mac64, XamMac2, Gtk, Direct2D
-->
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<None Update="utilities\android\apktool.yml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="utilities\android\LICENSE">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="utilities\xdelta\LICENSE">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="icon64.ico" />
<Content Include="utilities\android\apktool.jar">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="utilities\xdelta\xdelta3.exe">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Compile Update="Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
</Project>

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>AM2RModPacker</string>
<key>CFBundleIdentifier</key>
<string>com.example.AM2RModPacker</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>LSMinimumSystemVersion</key>
<string>10.14</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>NSHumanReadableCopyright</key>
<string></string>
<key>CFBundleIconFile</key>
<string>MacIcon.icns</string>
</dict>
</plist>

Binary file not shown.

@ -0,0 +1,134 @@
using Eto.Forms;
using Eto.Drawing;
using AM2RModPacker.XML;
using System.IO;
namespace AM2RModPacker;
public partial class ModPacker : Form
{
public ModPacker()
{
profile = new ModProfileXML("", 1, "", "", "", false, "", false, false, ""); // (1, "", "", false, "default", false, false);
isOriginalLoaded = false;
isModLoaded = false;
isApkLoaded = false;
isLinuxLoaded = false;
localPath = Directory.GetCurrentDirectory();
originalPath = "";
modPath = "";
linuxPath = "";
apkPath = "";
Title = "AM2R ModPacker " + VERSION;
// Currently broken as I don't know how to do this from Rider
//Icon = Icon.FromResource("icon64.ico");
MinimumSize = new Size(550, 430);
var mainContent = new DynamicLayout() { Spacing = new Size(15, 15) };
var leftSide = new DynamicLayout() { Padding = 10, Spacing = new Size(5, 5) };
var rightSide = new DynamicLayout() { Padding = 10, Spacing = new Size(5, 5) };
// Left side
var modInfoPanel = new DynamicLayout() { Spacing = new Size(5, 5) };
modInfoPanel.AddRow(nameLabel, NameTextBox);
modInfoPanel.AddRow(authorLabel, AuthorTextBox);
modInfoPanel.AddRow(versionLabel, versionTextBox);
modInfoPanel.AddRow(modNotesLabel);
var modNotesPanel = new DynamicLayout();
modNotesPanel.AddRow(modNotesTextBox);
// Combine together
leftSide.AddRow(modInfoPanel);
leftSide.AddRow(modNotesPanel);
// Right Side
var savePanel = new DynamicLayout();
savePanel.AddRow(SaveCheckBox, null, winSaveButton);
var saveTextPanel = new DynamicLayout();
saveTextPanel.AddRow(saveTextBox);
var miscOptionsPanel = new DynamicLayout() { Spacing = new Size(5, 5) };
miscOptionsPanel.AddRow(MusicCheckBox);
miscOptionsPanel.AddRow(YYCCheckBox);
miscOptionsPanel.AddRow(AndroidCheckBox);
miscOptionsPanel.AddRow(ApkButton, ApkLabel);
miscOptionsPanel.AddRow(linuxCheckBox);
miscOptionsPanel.AddRow(linuxButton, linuxLabel);
var loadZipsPanel = new DynamicLayout() { Spacing = new Size(5, 5) };
loadZipsPanel.AddRow(OriginalButton, null, ModButton);
loadZipsPanel.AddRow(OriginalLabel, null, ModLabel);
var resultPanel = new DynamicLayout() { Spacing = new Size(5, 5) };
resultPanel.AddRow(CreateButton);
resultPanel.AddRow(CreateLabel);
// Combine together
rightSide.AddRow(savePanel);
rightSide.AddRow(saveTextPanel);
rightSide.AddRow(miscOptionsPanel);
rightSide.AddRow(new Label() { Text = "I am a spacer!", TextColor = this.BackgroundColor });
rightSide.AddRow(loadZipsPanel);
rightSide.AddRow(resultPanel);
// Combine all into main panel and assign
Splitter splitter = new Splitter() {};
splitter.Panel1 = leftSide;
splitter.Panel2 = rightSide;
splitter.Panel1MinimumSize = 180;
splitter.Panel2MinimumSize = 320;
splitter.Orientation = Orientation.Horizontal;
mainContent.Add(splitter);
Content = mainContent;
// Assign events
OriginalButton.Click += OriginalButton_Click;
ModButton.Click += ModButton_Click;
CreateButton.Click += CreateButton_Click;
ApkButton.Click += ApkButton_Click;
AndroidCheckBox.CheckedChanged += AndroidCheckBox_CheckedChanged;
SaveCheckBox.CheckedChanged += SaveCheckBox_CheckedChanged;
linuxCheckBox.CheckedChanged += linuxCheckBox_CheckedChanged;
linuxButton.Click += linuxButton_Click;
winSaveButton.Click += CustomSaveDataButton_Click;
}
#region Design Elements
private Label nameLabel = new Label() { Text = "Mod name:" };
private TextBox NameTextBox = new TextBox();
private Label authorLabel = new Label() { Text = "Author:" };
private TextBox AuthorTextBox = new TextBox();
private Label versionLabel = new Label() { Text = "Version:" };
private TextBox versionTextBox = new TextBox();
private Label modNotesLabel = new Label() { Text = "Mod notes:" };
private TextArea modNotesTextBox = new TextArea() { };
private CheckBox SaveCheckBox = new CheckBox() { Text = "Uses custom save directory" };
private Button winSaveButton = new Button() { Text = "Select folder", Enabled = false };
private TextBox saveTextBox = new TextBox();
private CheckBox MusicCheckBox = new CheckBox() { Text = "Uses custom music" };
private CheckBox YYCCheckBox = new CheckBox() { Text = "Uses the YoYo Compiler" };
private CheckBox AndroidCheckBox = new CheckBox() { Text = "Supports Android" };
private Button ApkButton = new Button() { Text = "Load modded Android APK", Enabled = false };
private Label ApkLabel = new Label() { Text = "Modded APK loaded!", Visible = false };
private CheckBox linuxCheckBox = new CheckBox() { Text = "Supports Linux" };
private Button linuxButton = new Button() { Text = "Load modded Linux .zip", Enabled = false };
private Label linuxLabel = new Label() { Text = "Modded Linux game loaded!", Visible = false };
private Button OriginalButton = new Button() { Text = "Load 1.1" };
private Label OriginalLabel = new Label() { Text = "1.1 loaded!", Visible = false};
private Button ModButton = new Button() { Text = "Load modded game" };
private Label ModLabel = new Label() { Text = "Mod loaded!", Visible = false };
private Button CreateButton = new Button() { Text = "Create mod package(s)", Enabled = false };
private Label CreateLabel = new Label() { Text = "Mod package created!", Visible = false};
#endregion
}

@ -0,0 +1,569 @@
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using AM2RModPacker.XML;
using Eto.Forms;
namespace AM2RModPacker;
public partial class ModPacker : Form
{
private static readonly string VERSION = "2.0.3";
private static readonly string ORIGINAL_MD5 = "f2b84fe5ba64cb64e284be1066ca08ee";
private bool isOriginalLoaded, isModLoaded, isApkLoaded, isLinuxLoaded;
private string localPath, originalPath, modPath, apkPath, linuxPath;
private static readonly string[] DATAFILES_BLACKLIST = { "data.win", "AM2R.exe", "D3DX9_43.dll", "game.unx" };
private static string saveFilePath = null;
private ModProfileXML profile;
private FileFilter zipFileFilter = new FileFilter("zip archives (*.zip)", ".zip");
#region Eto events
private void OriginalButton_Click(object sender, EventArgs e)
{
// Open window to select AM2R 1.1
(isOriginalLoaded, originalPath) = SelectFile("Please select AM2R_11.zip", zipFileFilter);
OriginalLabel.Visible = isOriginalLoaded;
UpdateCreateButton();
}
private void ModButton_Click(object sender, EventArgs e)
{
// Open window to select modded AM2R
(isModLoaded, modPath) = SelectFile("Please select your custom AM2R .zip", zipFileFilter);
ModLabel.Visible = isModLoaded;
UpdateCreateButton();
}
private void CreateButton_Click(object sender, EventArgs e)
{
if (NameTextBox.Text == "" || AuthorTextBox.Text == "" || versionTextBox.Text == "")
{
MessageBox.Show("Text field missing! Mod packaging aborted.", "Error", MessageBoxButtons.OK, MessageBoxType.Error);
return;
}
if (Path.GetInvalidFileNameChars().Any(NameTextBox.Text.Contains))
{
MessageBox.Show("Name contains invalid characters! These characters are not allowed:\n" + string.Join("\n", Path.GetInvalidFileNameChars()));
return;
}
CreateLabel.Visible = true;
CreateLabel.Text = "Packaging mod(s)... This could take a while!";
string output;
using (SaveFileDialog saveFile = new SaveFileDialog { Title = "Save Windows mod profile", Filters = {zipFileFilter} })
{
if (saveFile.ShowDialog(this) == DialogResult.Ok)
{
output = saveFile.FileName;
}
else
{
CreateLabel.Text = "Mod packaging aborted!";
return;
}
}
CreateModPack("Windows", modPath, output);
if (linuxCheckBox.Checked.Value)
{
using (SaveFileDialog saveFile = new SaveFileDialog { Title = "Save Linux mod profile", Filters = {zipFileFilter} })
{
if (saveFile.ShowDialog(this) == DialogResult.Ok)
{
output = saveFile.FileName;
}
else
{
CreateLabel.Text = "Mod packaging aborted!";
return;
}
}
CreateModPack("Linux", linuxPath, output);
}
CreateLabel.Text = "Mod package(s) created!";
}
private void CreateModPack(string operatingSystem, string input, string output)
{
LoadProfileParameters(operatingSystem);
// 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
{
string newMD5 = CalculateMD5(tempOriginalPath + "\\data.win");
if (newMD5 != ORIGINAL_MD5)
{
// Show error box
MessageBox.Show("1.1 data.win does not meet MD5 checksum! Mod packaging aborted.\n1.1 MD5: " + ORIGINAL_MD5 + "\nYour MD5: " + newMD5, "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)
{
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 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();
}
CreatePatch(tempOriginalPath + "\\data.win", tempModPath + "\\assets\\game.unx", tempProfilePath + "\\game.xdelta");
CreatePatch(tempOriginalPath + "\\AM2R.exe", tempModPath + "\\" + runnerName, tempProfilePath + "\\AM2R.xdelta");
}
// 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
ProcessStartInfo 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 (Process proc = new Process { StartInfo = procStartInfo })
{
proc.Start();
proc.WaitForExit();
}
// Create game.droid patch
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
DirectoryInfo androidAssets = new DirectoryInfo(tempAndroid + "\\assets");
// Delete files
foreach (FileInfo 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 (DirectoryInfo dir in androidAssets.GetDirectories())
{
Directory.Delete(dir.FullName, true);
}
// Create wrapper
// Process startInfo
// - java -jar apktool.jar b "%~dp0AM2RWrapper_old" -o "%~dp0AM2RWrapper.apk"
ProcessStartInfo 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 (Process 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)
DirectoryInfo dinfo = new DirectoryInfo(tempModPath);
if (profile.OperatingSystem == "Linux")
dinfo = new DirectoryInfo(tempModPath + "\\assets");
Directory.CreateDirectory(tempProfilePath + "\\files_to_copy");
if (profile.UsesCustomMusic)
{
// Copy files, excluding the blacklist
CopyFilesRecursive(dinfo, 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
CopyFilesRecursive(dinfo, blacklist, tempProfilePath + "\\files_to_copy");
}
// Export profile as XML
string xmlOutput = Serializer.Serialize<ModProfileXML>(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);
}
private void ApkButton_Click(object sender, EventArgs e)
{
// Open window to select modded AM2R APK
(isApkLoaded, apkPath) = SelectFile("Please select your custom AM2R .apk", "android application packages (*.apk)|*.apk");
ApkLabel.Visible = isApkLoaded;
UpdateCreateButton();
}
private void AndroidCheckBox_CheckedChanged(object sender, EventArgs e)
{
ApkButton.Enabled = AndroidCheckBox.Checked.Value;
UpdateCreateButton();
}
private void SaveCheckBox_CheckedChanged(object sender, EventArgs e)
{
winSaveButton.Enabled = SaveCheckBox.Checked.Value;
saveTextBox.Enabled = SaveCheckBox.Checked.Value;
UpdateCreateButton();
}
#endregion
private void LoadProfileParameters(string operatingSystem)
{
profile.Name = NameTextBox.Text;
profile.Author = AuthorTextBox.Text;
profile.Version = versionTextBox.Text;
profile.UsesCustomMusic = MusicCheckBox.Checked.Value;
profile.UsesYYC = YYCCheckBox.Checked.Value;
profile.Android = AndroidCheckBox.Checked.Value;
profile.ProfileNotes = modNotesTextBox.Text;
profile.OperatingSystem = operatingSystem;
if (SaveCheckBox.Checked.Value && saveTextBox.Text != "")
{
profile.SaveLocation = saveTextBox.Text;
}
else
{
profile.SaveLocation = "%localappdata%/AM2R";
}
if (operatingSystem == "Linux")
{
profile.SaveLocation = profile.SaveLocation.Replace("%localappdata%", "~/.config");
}
}
private void AbortPatch()
{
// Unload files
isOriginalLoaded = false;
isModLoaded = false;
isApkLoaded = false;
isLinuxLoaded = false;
originalPath = "";
modPath = "";
apkPath = "";
linuxPath = "";
saveFilePath = null;
// Set labels
CreateLabel.Text = "Mod packaging aborted!";
OriginalLabel.Visible = false;
ModLabel.Visible = false;
ApkLabel.Visible = false;
linuxLabel.Visible = false;
// Remove temp directory
if (Directory.Exists(Path.GetTempPath() + "\\AM2RModPacker"))
{
Directory.Delete(Path.GetTempPath() + "\\AM2RModPacker", true);
}
}
private void linuxCheckBox_CheckedChanged(object sender, EventArgs e)
{
linuxButton.Enabled = linuxCheckBox.Checked.Value;
UpdateCreateButton();
}
private void linuxButton_Click(object sender, EventArgs e)
{
// Open window to select modded Linux .zip
(isLinuxLoaded, linuxPath) = SelectFile("Please select your custom Linux AM2R .zip", zipFileFilter);
linuxLabel.Visible = isLinuxLoaded;
UpdateCreateButton();
}
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
SelectFolderDialog dialog = new SelectFolderDialog();
// currently not implemented in eto
//dialog.InitialDirectory = Environment.GetEnvironmentVariable("LocalAppData");
while (!wasSuccessfull)
{
if (dialog.ShowDialog(this) == DialogResult.Ok)
{
Match match = saveRegex.Match(dialog.Directory);
if (match.Success == false)
MessageBox.Show("Invalid Save Directory! Please choose one in %LocalAppData%");
else
{
wasSuccessfull = 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
// if someone has a custom save path inside of am2r and creates these whithin 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
string vanillaPrefix = "%localappdata%/AM2R/";
if (saveFilePath.Contains(vanillaPrefix))
saveFilePath = vanillaPrefix + saveFilePath.Substring(vanillaPrefix.Length).ToLower();
}
}
else
{
wasSuccessfull = true;
saveFilePath = null;
}
}
saveTextBox.Text = saveFilePath;
}
private void CopyFilesRecursive(DirectoryInfo source, string[] blacklist, string destination)
{
foreach (FileInfo file in source.GetFiles())
{
if (!blacklist.Contains(file.Name))
{
file.CopyTo(destination + "\\" + file.Name);
}
}
foreach (DirectoryInfo dir in source.GetDirectories())
{
// Folders need to be lowercase, because GM only reads from lowercase names on *nix systems. Windows is case-insensitive so doesnt matter for them
string newDir = Directory.CreateDirectory(destination + "\\" + dir.Name.ToLower()).FullName;
CopyFilesRecursive(dir, blacklist, newDir);
}
}
private void UpdateCreateButton()
{
if (isOriginalLoaded &&
isModLoaded &&
(!AndroidCheckBox.Checked.Value || isApkLoaded) &&
(!linuxCheckBox.Checked.Value || isLinuxLoaded) &&
(!SaveCheckBox.Checked.Value || saveTextBox.Text != ""))
{
CreateButton.Enabled = true;
}
else
{
CreateButton.Enabled = false;
}
}
// Thanks, stackoverflow: https://stackoverflow.com/questions/10520048/calculate-md5-checksum-for-a-file
private string CalculateMD5(string filename)
{
using (var stream = File.OpenRead(filename))
{
using (var md5 = MD5.Create())
{
var hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
}
}
private void CreatePatch(string original, string modified, string output)
{
// Specify process start info
ProcessStartInfo parameters = new ProcessStartInfo
{
FileName = localPath + "\\utilities\\xdelta\\xdelta3.exe",
WorkingDirectory = localPath,
UseShellExecute = false,
CreateNoWindow = true,
Arguments = "-f -e -s \"" + original + "\" \"" + modified + "\" \"" + output + "\""
};
// Launch process and wait for exit. using statement automatically disposes the object for us!
using (Process proc = new Process { StartInfo = parameters })
{
proc.Start();
proc.WaitForExit();
}
}
private (bool, string) SelectFile(string title, FileFilter filter)
{
using (OpenFileDialog fileFinder = new OpenFileDialog() {Filters = { filter }})
{
fileFinder.Title = title;
fileFinder.CurrentFilter = fileFinder.Filters.First();
fileFinder.CheckFileExists = true;
if (fileFinder.ShowDialog(this) == DialogResult.Ok)
{
string location = fileFinder.FileName;
return (true, location);
}
else return (false, "");
}
}
}

@ -0,0 +1,14 @@
using System;
using Eto.Forms;
using Eto.Drawing;
namespace AM2RModPacker;
class Program
{
[STAThread]
static void Main(string[] args)
{
new Application(Eto.Platform.Detect).Run(new ModPacker());
}
}

@ -0,0 +1,48 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace AM2RModPacker {
using System;
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static System.Resources.ResourceManager resourceMan;
private static System.Globalization.CultureInfo resourceCulture;
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
internal static System.Resources.ResourceManager ResourceManager {
get {
if (object.Equals(null, resourceMan)) {
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("AM2RModPacker.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
internal static System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

@ -1,7 +1,7 @@
using System;
using System.Xml.Serialization;
namespace AM2R_ModPacker
namespace AM2RModPacker.XML
{
/// <summary>
/// Class that handles how the modmeta settings are saved as XML.
@ -73,4 +73,4 @@ namespace AM2R_ModPacker
Installable = installable;
}
}
}
}

@ -1,12 +1,8 @@
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
namespace AM2RModPacker.XML
{
/// <summary>
/// The <c>Serializer</c> class, that serializes to and deserializes from XML files.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
</configuration>

426
ModPacker.Designer.cs generated

@ -1,426 +0,0 @@
namespace AM2R_ModPacker
{
partial class ModPacker
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ModPacker));
this.panel1 = new System.Windows.Forms.Panel();
this.CreateLabel = new System.Windows.Forms.Label();
this.CreateButton = new System.Windows.Forms.Button();
this.ModLabel = new System.Windows.Forms.Label();
this.ModButton = new System.Windows.Forms.Button();
this.OriginalLabel = new System.Windows.Forms.Label();
this.OriginalButton = new System.Windows.Forms.Button();
this.panel2 = new System.Windows.Forms.Panel();
this.versionTextBox = new System.Windows.Forms.TextBox();
this.versionLabel = new System.Windows.Forms.Label();
this.AuthorTextBox = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
this.NameTextBox = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.panel3 = new System.Windows.Forms.Panel();
this.linuxLabel = new System.Windows.Forms.Label();
this.linuxButton = new System.Windows.Forms.Button();
this.saveTextBox = new System.Windows.Forms.TextBox();
this.linuxCheckBox = new System.Windows.Forms.CheckBox();
this.winSaveButton = new System.Windows.Forms.Button();
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.modNotesTextBox = new System.Windows.Forms.TextBox();
this.panel4 = new System.Windows.Forms.Panel();
this.modNotesLabel = new System.Windows.Forms.Label();
this.panel1.SuspendLayout();
this.panel2.SuspendLayout();
this.panel3.SuspendLayout();
this.panel4.SuspendLayout();
this.SuspendLayout();
//
// panel1
//
this.panel1.Controls.Add(this.CreateLabel);
this.panel1.Controls.Add(this.CreateButton);
this.panel1.Controls.Add(this.ModLabel);
this.panel1.Controls.Add(this.ModButton);
this.panel1.Controls.Add(this.OriginalLabel);
this.panel1.Controls.Add(this.OriginalButton);
this.panel1.Location = new System.Drawing.Point(309, 210);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(291, 86);
this.panel1.TabIndex = 0;
//
// CreateLabel
//
this.CreateLabel.Anchor = System.Windows.Forms.AnchorStyles.Top;
this.CreateLabel.Location = new System.Drawing.Point(5, 71);
this.CreateLabel.Name = "CreateLabel";
this.CreateLabel.Size = new System.Drawing.Size(283, 13);
this.CreateLabel.TabIndex = 6;
this.CreateLabel.Text = "Mod package created!";
this.CreateLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter;
this.CreateLabel.Visible = false;
//
// CreateButton
//
this.CreateButton.Enabled = false;
this.CreateButton.Location = new System.Drawing.Point(3, 45);
this.CreateButton.Name = "CreateButton";
this.CreateButton.Size = new System.Drawing.Size(285, 23);
this.CreateButton.TabIndex = 5;
this.CreateButton.Text = "Create mod package(s)";
this.CreateButton.UseVisualStyleBackColor = true;
this.CreateButton.Click += new System.EventHandler(this.CreateButton_Click);
//
// ModLabel
//
this.ModLabel.AutoSize = true;
this.ModLabel.Location = new System.Drawing.Point(201, 29);
this.ModLabel.Name = "ModLabel";
this.ModLabel.Size = new System.Drawing.Size(66, 13);
this.ModLabel.TabIndex = 4;
this.ModLabel.Text = "Mod loaded!";
this.ModLabel.Visible = false;
//
// ModButton
//
this.ModButton.Location = new System.Drawing.Point(177, 3);
this.ModButton.Name = "ModButton";
this.ModButton.Size = new System.Drawing.Size(111, 23);
this.ModButton.TabIndex = 3;
this.ModButton.Text = "Load modded game";
this.ModButton.UseVisualStyleBackColor = true;
this.ModButton.Click += new System.EventHandler(this.ModButton_Click);
//
// OriginalLabel
//
this.OriginalLabel.AutoSize = true;
this.OriginalLabel.Location = new System.Drawing.Point(28, 29);
this.OriginalLabel.Name = "OriginalLabel";
this.OriginalLabel.Size = new System.Drawing.Size(60, 13);
this.OriginalLabel.TabIndex = 2;
this.OriginalLabel.Text = "1.1 loaded!";
this.OriginalLabel.Visible = false;
//
// OriginalButton
//
this.OriginalButton.Location = new System.Drawing.Point(3, 3);
this.OriginalButton.Name = "OriginalButton";
this.OriginalButton.Size = new System.Drawing.Size(111, 23);
this.OriginalButton.TabIndex = 1;
this.OriginalButton.Text = "Load 1.1";
this.OriginalButton.UseVisualStyleBackColor = true;
this.OriginalButton.Click += new System.EventHandler(this.OriginalButton_Click);
//
// panel2
//
this.panel2.Controls.Add(this.versionTextBox);
this.panel2.Controls.Add(this.versionLabel);
this.panel2.Controls.Add(this.AuthorTextBox);
this.panel2.Controls.Add(this.label2);
this.panel2.Controls.Add(this.NameTextBox);
this.panel2.Controls.Add(this.label1);
this.panel2.Location = new System.Drawing.Point(12, 3);
this.panel2.Name = "panel2";
this.panel2.Size = new System.Drawing.Size(291, 77);
this.panel2.TabIndex = 1;
//
// versionTextBox
//
this.versionTextBox.Location = new System.Drawing.Point(63, 50);
this.versionTextBox.Name = "versionTextBox";
this.versionTextBox.Size = new System.Drawing.Size(225, 20);
this.versionTextBox.TabIndex = 12;
//
// versionLabel
//
this.versionLabel.AutoSize = true;
this.versionLabel.Location = new System.Drawing.Point(2, 53);
this.versionLabel.Name = "versionLabel";
this.versionLabel.Size = new System.Drawing.Size(45, 13);
this.versionLabel.TabIndex = 11;
this.versionLabel.Text = "Version:";
//
// AuthorTextBox
//
this.AuthorTextBox.Location = new System.Drawing.Point(63, 28);
this.AuthorTextBox.MaxLength = 30;
this.AuthorTextBox.Name = "AuthorTextBox";
this.AuthorTextBox.Size = new System.Drawing.Size(225, 20);
this.AuthorTextBox.TabIndex = 10;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(2, 31);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(41, 13);
this.label2.TabIndex = 9;
this.label2.Text = "Author:";
//
// NameTextBox
//
this.NameTextBox.Location = new System.Drawing.Point(63, 6);
this.NameTextBox.MaxLength = 30;
this.NameTextBox.Name = "NameTextBox";
this.NameTextBox.Size = new System.Drawing.Size(225, 20);
this.NameTextBox.TabIndex = 8;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(2, 9);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(60, 13);
this.label1.TabIndex = 7;
this.label1.Text = "Mod name:";
//
// panel3
//
this.panel3.Controls.Add(this.linuxLabel);
this.panel3.Controls.Add(this.linuxButton);
this.panel3.Controls.Add(this.saveTextBox);
this.panel3.Controls.Add(this.linuxCheckBox);
this.panel3.Controls.Add(this.winSaveButton);
this.panel3.Controls.Add(this.ApkLabel);
this.panel3.Controls.Add(this.ApkButton);
this.panel3.Controls.Add(this.AndroidCheckBox);
this.panel3.Controls.Add(this.YYCCheckBox);
this.panel3.Controls.Add(this.SaveCheckBox);
this.panel3.Controls.Add(this.MusicCheckBox);
this.panel3.Location = new System.Drawing.Point(309, 3);
this.panel3.Name = "panel3";
this.panel3.Size = new System.Drawing.Size(291, 201);
this.panel3.TabIndex = 2;
//
// linuxLabel
//
this.linuxLabel.AutoSize = true;
this.linuxLabel.Location = new System.Drawing.Point(147, 164);
this.linuxLabel.Name = "linuxLabel";
this.linuxLabel.Size = new System.Drawing.Size(141, 13);
this.linuxLabel.TabIndex = 18;
this.linuxLabel.Text = "Modded Linux game loaded!";
this.linuxLabel.Visible = false;
//
// linuxButton
//
this.linuxButton.Enabled = false;
this.linuxButton.Location = new System.Drawing.Point(3, 159);
this.linuxButton.Name = "linuxButton";
this.linuxButton.Size = new System.Drawing.Size(143, 23);
this.linuxButton.TabIndex = 17;
this.linuxButton.Text = "Load modded Linux .zip";
this.linuxButton.UseVisualStyleBackColor = true;
this.linuxButton.Click += new System.EventHandler(this.linuxButton_Click);
//
// saveTextBox
//
this.saveTextBox.Enabled = false;
this.saveTextBox.Location = new System.Drawing.Point(4, 28);
this.saveTextBox.Name = "saveTextBox";
this.saveTextBox.Size = new System.Drawing.Size(283, 20);
this.saveTextBox.TabIndex = 15;
//
// linuxCheckBox
//
this.linuxCheckBox.AutoSize = true;
this.linuxCheckBox.Location = new System.Drawing.Point(4, 141);
this.linuxCheckBox.Name = "linuxCheckBox";
this.linuxCheckBox.Size = new System.Drawing.Size(96, 17);
this.linuxCheckBox.TabIndex = 12;
this.linuxCheckBox.Text = "Supports Linux";
this.linuxCheckBox.UseVisualStyleBackColor = true;
this.linuxCheckBox.CheckedChanged += new System.EventHandler(this.linuxCheckBox_CheckedChanged);
//
// winSaveButton
//
this.winSaveButton.Enabled = false;
this.winSaveButton.Location = new System.Drawing.Point(177, 7);
this.winSaveButton.Name = "winSaveButton";
this.winSaveButton.Size = new System.Drawing.Size(111, 19);
this.winSaveButton.TabIndex = 10;
this.winSaveButton.Text = "Select folder";
this.winSaveButton.UseVisualStyleBackColor = true;
this.winSaveButton.Click += new System.EventHandler(this.CustomSaveDataButton_Click);
//
// ApkLabel
//
this.ApkLabel.AutoSize = true;
this.ApkLabel.Location = new System.Drawing.Point(180, 120);
this.ApkLabel.Name = "ApkLabel";
this.ApkLabel.Size = new System.Drawing.Size(108, 13);
this.ApkLabel.TabIndex = 7;
this.ApkLabel.Text = "Modded APK loaded!";
this.ApkLabel.Visible = false;
//
// ApkButton
//
this.ApkButton.Enabled = false;
this.ApkButton.Location = new System.Drawing.Point(3, 115);
this.ApkButton.Name = "ApkButton";
this.ApkButton.Size = new System.Drawing.Size(143, 23);
this.ApkButton.TabIndex = 6;
this.ApkButton.Text = "Load modded Android APK";
this.ApkButton.UseVisualStyleBackColor = true;
this.ApkButton.Click += new System.EventHandler(this.ApkButton_Click);
//
// AndroidCheckBox
//
this.AndroidCheckBox.AutoSize = true;
this.AndroidCheckBox.Location = new System.Drawing.Point(4, 95);
this.AndroidCheckBox.Name = "AndroidCheckBox";
this.AndroidCheckBox.Size = new System.Drawing.Size(107, 17);
this.AndroidCheckBox.TabIndex = 5;
this.AndroidCheckBox.Text = "Supports Android";
this.AndroidCheckBox.UseVisualStyleBackColor = true;
this.AndroidCheckBox.CheckedChanged += new System.EventHandler(this.AndroidCheckBox_CheckedChanged);
//
// YYCCheckBox
//
this.YYCCheckBox.AutoSize = true;
this.YYCCheckBox.Location = new System.Drawing.Point(4, 74);
this.YYCCheckBox.Name = "YYCCheckBox";
this.YYCCheckBox.Size = new System.Drawing.Size(140, 17);
this.YYCCheckBox.TabIndex = 4;
this.YYCCheckBox.Text = "Uses the YoYo Compiler";
this.YYCCheckBox.UseVisualStyleBackColor = true;
//
// SaveCheckBox
//
this.SaveCheckBox.AutoSize = true;
this.SaveCheckBox.Location = new System.Drawing.Point(4, 8);
this.SaveCheckBox.Name = "SaveCheckBox";
this.SaveCheckBox.Size = new System.Drawing.Size(156, 17);
this.SaveCheckBox.TabIndex = 3;
this.SaveCheckBox.Text = "Uses custom save directory";
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, 52);
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);
this.MusicCheckBox.TabIndex = 2;
this.MusicCheckBox.Text = "Uses custom music";
this.MusicCheckBox.UseVisualStyleBackColor = true;
//
// modNotesTextBox
//
this.modNotesTextBox.Location = new System.Drawing.Point(3, 16);
this.modNotesTextBox.Multiline = true;
this.modNotesTextBox.Name = "modNotesTextBox";
this.modNotesTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.modNotesTextBox.Size = new System.Drawing.Size(284, 198);
this.modNotesTextBox.TabIndex = 3;
//
// panel4
//
this.panel4.Controls.Add(this.modNotesLabel);
this.panel4.Controls.Add(this.modNotesTextBox);
this.panel4.Location = new System.Drawing.Point(12, 80);
this.panel4.Name = "panel4";
this.panel4.Size = new System.Drawing.Size(291, 216);
this.panel4.TabIndex = 4;
//
// modNotesLabel
//
this.modNotesLabel.AutoSize = true;
this.modNotesLabel.Location = new System.Drawing.Point(2, 0);
this.modNotesLabel.Name = "modNotesLabel";
this.modNotesLabel.Size = new System.Drawing.Size(60, 13);
this.modNotesLabel.TabIndex = 4;
this.modNotesLabel.Text = "Mod notes:";
//
// ModPacker
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(615, 309);
this.Controls.Add(this.panel4);
this.Controls.Add(this.panel3);
this.Controls.Add(this.panel2);
this.Controls.Add(this.panel1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.MaximumSize = new System.Drawing.Size(631, 348);
this.MinimumSize = new System.Drawing.Size(631, 348);
this.Name = "ModPacker";
this.Text = "AM2R ModPacker";
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.panel2.ResumeLayout(false);
this.panel2.PerformLayout();
this.panel3.ResumeLayout(false);
this.panel3.PerformLayout();
this.panel4.ResumeLayout(false);
this.panel4.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Label OriginalLabel;
private System.Windows.Forms.Button OriginalButton;
private System.Windows.Forms.Label ModLabel;
private System.Windows.Forms.Button ModButton;
private System.Windows.Forms.Button CreateButton;
private System.Windows.Forms.Panel panel2;
private System.Windows.Forms.TextBox AuthorTextBox;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox NameTextBox;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Panel panel3;
private System.Windows.Forms.Button ApkButton;
private System.Windows.Forms.CheckBox AndroidCheckBox;
private System.Windows.Forms.CheckBox YYCCheckBox;
private System.Windows.Forms.CheckBox SaveCheckBox;
private System.Windows.Forms.CheckBox MusicCheckBox;
private System.Windows.Forms.Label ApkLabel;
private System.Windows.Forms.Label CreateLabel;
private System.Windows.Forms.Button winSaveButton;
private System.Windows.Forms.TextBox saveTextBox;
private System.Windows.Forms.CheckBox linuxCheckBox;
private System.Windows.Forms.Button linuxButton;
private System.Windows.Forms.Label linuxLabel;
private System.Windows.Forms.TextBox versionTextBox;
private System.Windows.Forms.Label versionLabel;
private System.Windows.Forms.TextBox modNotesTextBox;
private System.Windows.Forms.Panel panel4;
private System.Windows.Forms.Label modNotesLabel;
}
}

@ -1,595 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.IO.Compression;
using System.Security.Cryptography;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;
using Microsoft.WindowsAPICodePack.Dialogs;
using System.Text.RegularExpressions;
namespace AM2R_ModPacker
{
public partial class ModPacker : Form
{
private static readonly string VERSION = "2.0.3";
private static readonly string ORIGINAL_MD5 = "f2b84fe5ba64cb64e284be1066ca08ee";
private bool isOriginalLoaded, isModLoaded, isApkLoaded, isLinuxLoaded;
private string localPath, originalPath, modPath, apkPath, linuxPath;
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 ModProfileXML("", 1, "", "", "", false, "", false, false, ""); // (1, "", "", false, "default", false, false);
isOriginalLoaded = false;
isModLoaded = false;
isApkLoaded = false;
isLinuxLoaded = false;
localPath = Directory.GetCurrentDirectory();
originalPath = "";
modPath = "";
linuxPath = "";
apkPath = "";
Text = "AM2R ModPacker " + VERSION;
}
#region WinForms events
private void OriginalButton_Click(object sender, EventArgs e)
{
// Open window to select AM2R 1.1
(isOriginalLoaded, originalPath) = SelectFile("Please select AM2R_11.zip", "zip", "zip files (*.zip)|*.zip");
OriginalLabel.Visible = isOriginalLoaded;
UpdateCreateButton();
}
private void ModButton_Click(object sender, EventArgs e)
{
// Open window to select modded AM2R
(isModLoaded, modPath) = SelectFile("Please select your custom AM2R .zip", "zip", "zip files (*.zip)|*.zip");
ModLabel.Visible = isModLoaded;
UpdateCreateButton();
}
private void CreateButton_Click(object sender, EventArgs e)
{
if (NameTextBox.Text == "" || AuthorTextBox.Text == "" || versionTextBox.Text == "")
{
MessageBox.Show("Text field missing! Mod packaging aborted.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (Path.GetInvalidFileNameChars().Any(NameTextBox.Text.Contains))
{
MessageBox.Show("Name contains invalid characters! These characters are not allowed:\n" + string.Join("\n", Path.GetInvalidFileNameChars()));
return;
}
CreateLabel.Visible = true;
CreateLabel.Text = "Packaging mod(s)... This could take a while!";
string output;
using (SaveFileDialog saveFile = new SaveFileDialog { RestoreDirectory = true, Title = "Save Windows mod profile", Filter = "zip files (*.zip)|*.zip", AddExtension = true })
{
if (saveFile.ShowDialog() == DialogResult.OK)
{
output = saveFile.FileName;
}
else
{
CreateLabel.Text = "Mod packaging aborted!";
return;
}
}
CreateModPack("Windows", modPath, output);
if (linuxCheckBox.Checked)
{
using (SaveFileDialog saveFile = new SaveFileDialog { RestoreDirectory = true, Title = "Save Linux mod profile", Filter = "zip files (*.zip)|*.zip", AddExtension = true })
{
if (saveFile.ShowDialog() == DialogResult.OK)
{
output = saveFile.FileName;
}
else
{
CreateLabel.Text = "Mod packaging aborted!";
return;
}
}
CreateModPack("Linux", linuxPath, output);
}
CreateLabel.Text = "Mod package(s) created!";
}
private void CreateModPack(string operatingSystem, string input, string output)
{
LoadProfileParameters(operatingSystem);
// 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, MessageBoxIcon.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
{
string newMD5 = CalculateMD5(tempOriginalPath + "\\data.win");
if (newMD5 != ORIGINAL_MD5)
{
// Show error box
MessageBox.Show("1.1 data.win does not meet MD5 checksum! Mod packaging aborted.\n1.1 MD5: " + ORIGINAL_MD5 + "\nYour MD5: " + newMD5, "Error", MessageBoxButtons.OK, MessageBoxIcon.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, MessageBoxIcon.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, MessageBoxIcon.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, MessageBoxIcon.Warning);
if (result != DialogResult.Yes)
AbortPatch();
}
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 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, MessageBoxIcon.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, MessageBoxIcon.Warning);
if (result != DialogResult.Yes)
AbortPatch();
}
CreatePatch(tempOriginalPath + "\\data.win", tempModPath + "\\assets\\game.unx", tempProfilePath + "\\game.xdelta");
CreatePatch(tempOriginalPath + "\\AM2R.exe", tempModPath + "\\" + runnerName, tempProfilePath + "\\AM2R.xdelta");
}
// 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
ProcessStartInfo 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 (Process proc = new Process { StartInfo = procStartInfo })
{
proc.Start();
proc.WaitForExit();
}
// Create game.droid patch
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
DirectoryInfo androidAssets = new DirectoryInfo(tempAndroid + "\\assets");
// Delete files
foreach (FileInfo 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 (DirectoryInfo dir in androidAssets.GetDirectories())
{
Directory.Delete(dir.FullName, true);
}
// Create wrapper
// Process startInfo
// - java -jar apktool.jar b "%~dp0AM2RWrapper_old" -o "%~dp0AM2RWrapper.apk"
ProcessStartInfo 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 (Process 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)
DirectoryInfo dinfo = new DirectoryInfo(tempModPath);
if (profile.OperatingSystem == "Linux")
dinfo = new DirectoryInfo(tempModPath + "\\assets");
Directory.CreateDirectory(tempProfilePath + "\\files_to_copy");
if (profile.UsesCustomMusic)
{
// Copy files, excluding the blacklist
CopyFilesRecursive(dinfo, 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
CopyFilesRecursive(dinfo, blacklist, tempProfilePath + "\\files_to_copy");
}
// Export profile as XML
string xmlOutput = Serializer.Serialize<ModProfileXML>(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);
}
private void ApkButton_Click(object sender, EventArgs e)
{
// Open window to select modded AM2R APK
(isApkLoaded, apkPath) = SelectFile("Please select your custom AM2R .apk", "apk", "android application packages (*.apk)|*.apk");
ApkLabel.Visible = isApkLoaded;
UpdateCreateButton();
}
private void AndroidCheckBox_CheckedChanged(object sender, EventArgs e)
{
ApkButton.Enabled = AndroidCheckBox.Checked;
UpdateCreateButton();
}
private void SaveCheckBox_CheckedChanged(object sender, EventArgs e)
{
winSaveButton.Enabled = SaveCheckBox.Checked;
saveTextBox.Enabled = SaveCheckBox.Checked;
UpdateCreateButton();
}
#endregion
private void LoadProfileParameters(string operatingSystem)
{
profile.Name = NameTextBox.Text;
profile.Author = AuthorTextBox.Text;
profile.Version = versionTextBox.Text;
profile.UsesCustomMusic = MusicCheckBox.Checked;
profile.UsesYYC = YYCCheckBox.Checked;
profile.Android = AndroidCheckBox.Checked;
profile.ProfileNotes = modNotesTextBox.Text;
profile.OperatingSystem = operatingSystem;
if (SaveCheckBox.Checked && saveTextBox.Text != "")
{
profile.SaveLocation = saveTextBox.Text;
}
else
{
profile.SaveLocation = "%localappdata%/AM2R";
}
if (operatingSystem == "Linux")
{
profile.SaveLocation = profile.SaveLocation.Replace("%localappdata%", "~/.config");
}
}
private void AbortPatch()
{
// Unload files
isOriginalLoaded = false;
isModLoaded = false;
isApkLoaded = false;
isLinuxLoaded = false;
originalPath = "";
modPath = "";
apkPath = "";
linuxPath = "";
saveFilePath = null;
// Set labels
CreateLabel.Text = "Mod packaging aborted!";
OriginalLabel.Visible = false;
ModLabel.Visible = false;
ApkLabel.Visible = false;
linuxLabel.Visible = false;
// Remove temp directory
if (Directory.Exists(Path.GetTempPath() + "\\AM2RModPacker"))
{
Directory.Delete(Path.GetTempPath() + "\\AM2RModPacker", true);
}
}
private void linuxCheckBox_CheckedChanged(object sender, EventArgs e)
{
linuxButton.Enabled = linuxCheckBox.Checked;
UpdateCreateButton();
}
private void linuxButton_Click(object sender, EventArgs e)
{
// Open window to select modded Linux .zip
(isLinuxLoaded, linuxPath) = SelectFile("Please select your custom Linux AM2R .zip", "zip", "zip archives (*.zip)|*.zip");
linuxLabel.Visible = isLinuxLoaded;
UpdateCreateButton();
}
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%/");
saveFilePath = saveFilePath.Replace("\\", "/"); // if we don't do this, custom save locations are going to fail on Linux
// if someone has a custom save path inside of am2r and creates these whithin 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
string vanillaPrefix = "%localappdata%/AM2R/";
if (saveFilePath.Contains(vanillaPrefix))
saveFilePath = vanillaPrefix + saveFilePath.Substring(vanillaPrefix.Length).ToLower();
}
}
else
{
wasSuccessfull = true;
saveFilePath = null;
}
}
saveTextBox.Text = saveFilePath;
}
private void CopyFilesRecursive(DirectoryInfo source, string[] blacklist, string destination)
{
foreach (FileInfo file in source.GetFiles())
{
if (!blacklist.Contains(file.Name))
{
file.CopyTo(destination + "\\" + file.Name);
}
}
foreach (DirectoryInfo dir in source.GetDirectories())
{
// Folders need to be lowercase, because GM only reads from lowercase names on *nix systems. Windows is case-insensitive so doesnt matter for them
string newDir = Directory.CreateDirectory(destination + "\\" + dir.Name.ToLower()).FullName;
CopyFilesRecursive(dir, blacklist, newDir);
}
}
private void UpdateCreateButton()
{
if (isOriginalLoaded &&
isModLoaded &&
(!AndroidCheckBox.Checked || isApkLoaded) &&
(!linuxCheckBox.Checked || isLinuxLoaded) &&
(!SaveCheckBox.Checked || saveTextBox.Text != ""))
{
CreateButton.Enabled = true;
}
else
{
CreateButton.Enabled = false;
}
}
// Thanks, stackoverflow: https://stackoverflow.com/questions/10520048/calculate-md5-checksum-for-a-file
private string CalculateMD5(string filename)
{
using (var stream = File.OpenRead(filename))
{
using (var md5 = MD5.Create())
{
var hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
}
}
private void CreatePatch(string original, string modified, string output)
{
// Specify process start info
ProcessStartInfo parameters = new ProcessStartInfo
{
FileName = localPath + "\\utilities\\xdelta\\xdelta3.exe",
WorkingDirectory = localPath,
UseShellExecute = false,
CreateNoWindow = true,
Arguments = "-f -e -s \"" + original + "\" \"" + modified + "\" \"" + output + "\""
};
// Launch process and wait for exit. using statement automatically disposes the object for us!
using (Process proc = new Process { StartInfo = parameters })
{
proc.Start();
proc.WaitForExit();
}
}
private (bool, string) SelectFile(string title, string extension, string filter)
{
using (OpenFileDialog fileFinder = new OpenFileDialog())
{
fileFinder.RestoreDirectory = true;
fileFinder.Title = title;
fileFinder.DefaultExt = extension;
fileFinder.Filter = filter;
fileFinder.CheckFileExists = true;
fileFinder.CheckPathExists = true;
fileFinder.Multiselect = false;
if (fileFinder.ShowDialog() == DialogResult.OK)
{
string location = fileFinder.FileName;
return (true, location);
}
else return (false, "");
}
}
}
}

File diff suppressed because it is too large Load Diff

@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AM2R_ModPacker
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ModPacker());
}
}
}

@ -1,36 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("AM2R ModPacker")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("AM2R ModPacker")]
[assembly: AssemblyCopyright("Copyright © Lojemiru 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("7c7450e2-d2ae-4da3-b8e3-fc953f50a0c7")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.0.2.0")]
[assembly: AssemblyFileVersion("2.0.2.0")]

@ -1,63 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace AM2RModPacker.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AM2RModPacker.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

@ -1,120 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

@ -1,26 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace AM2RModPacker.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

@ -1,7 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.WindowsAPICodePack-Core" version="1.1.0.0" targetFramework="net472" />
<package id="Microsoft.WindowsAPICodePack-Shell" version="1.1.0.0" targetFramework="net472" />
<package id="WindowsAPICodePack-Core" version="1.1.1" targetFramework="net472" />
<package id="WindowsAPICodePack-Shell" version="1.1.1" targetFramework="net472" />
</packages>
Loading…
Cancel
Save