Clean up Profile class more

pull/32/head
Miepee 4 years ago
parent b2e7ad697a
commit 73b035a34c

@ -253,6 +253,7 @@ public static class CrossPlatformOperations
/// <param name="output">Full Path to the output file.</param> /// <param name="output">Full Path to the output file.</param>
public static void ApplyXdeltaPatch(string original, string patch, string output) public static void ApplyXdeltaPatch(string original, string patch, string output)
{ {
//TODO: some slight cleanup
// For *whatever reason* **sometimes** xdelta patching doesn't work, if output = original. So I'm fixing that here. // For *whatever reason* **sometimes** xdelta patching doesn't work, if output = original. So I'm fixing that here.
string originalOutput = output; string originalOutput = output;
if (original == output) if (original == output)
@ -297,6 +298,7 @@ public static class CrossPlatformOperations
File.Move(output, originalOutput); File.Move(output, originalOutput);
} }
//TODO: doc and cleanup
public static void RunJavaJar(string arguments = null, string workingDirectory = null) public static void RunJavaJar(string arguments = null, string workingDirectory = null)
{ {
workingDirectory ??= Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); workingDirectory ??= Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);

@ -8,7 +8,6 @@ using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using AM2RLauncherLib.XML; using AM2RLauncherLib.XML;
using log4net.Util;
namespace AM2RLauncherLib; namespace AM2RLauncherLib;
@ -90,53 +89,59 @@ public static class Profile
/// <summary> /// <summary>
/// Checks if a Zip file is a valid AM2R_1.1 zip. /// Checks if a Zip file is a valid AM2R_1.1 zip.
/// </summary> /// </summary>
/// <param name="zipPath">Full Path to the Zip file to check.</param> /// <param name="zipPath">Full Path to the Zip file to validate.</param>
/// <returns><see cref="IsZipAM2R11ReturnCodes"/> detailing the result</returns> /// <returns><see cref="IsZipAM2R11ReturnCodes"/> detailing the result</returns>
public static IsZipAM2R11ReturnCodes CheckIfZipIsAM2R11(string zipPath) public static IsZipAM2R11ReturnCodes CheckIfZipIsAM2R11(string zipPath)
{ {
const string d3dHash = "86e39e9161c3d930d93822f1563c280d"; const string d3dHash = "86e39e9161c3d930d93822f1563c280d";
const string dataWinHash = "f2b84fe5ba64cb64e284be1066ca08ee"; const string dataWinHash = "f2b84fe5ba64cb64e284be1066ca08ee";
const string am2rHash = "15253f7a66d6ea3feef004ebbee9b438"; const string am2rHash = "15253f7a66d6ea3feef004ebbee9b438";
string tmpPath = Path.GetTempPath() + Path.GetFileNameWithoutExtension(zipPath); string tmpPath = Path.GetTempPath() + Path.GetFileNameWithoutExtension(zipPath);
// Clean up in case folder exists already // Clean up in case folder exists already
if (Directory.Exists(tmpPath)) if (Directory.Exists(tmpPath))
Directory.Delete(tmpPath, true); Directory.Delete(tmpPath, true);
Directory.CreateDirectory(tmpPath); Directory.CreateDirectory(tmpPath);
// Open archive // Open archive
ZipArchive am2rZip = ZipFile.OpenRead(zipPath); ZipArchive am2rZip = ZipFile.OpenRead(zipPath);
// Check if exe exists anywhere // Check if exe exists anywhere
ZipArchiveEntry am2rExe = am2rZip.Entries.FirstOrDefault(x => x.FullName.Contains("AM2R.exe")); ZipArchiveEntry am2rExe = am2rZip.Entries.FirstOrDefault(x => x.FullName.Contains("AM2R.exe"));
if (am2rExe == null) if (am2rExe == null)
return IsZipAM2R11ReturnCodes.MissingOrInvalidAM2RExe; return IsZipAM2R11ReturnCodes.MissingOrInvalidAM2RExe;
// Check if it's not in a subfolder. if it'd be in a subfolder, fullname would be "folder/AM2R.exe" // Check if it's not in a subfolder. if it'd be in a subfolder, fullname would be "folder/AM2R.exe"
if (am2rExe.FullName != "AM2R.exe") if (am2rExe.FullName != "AM2R.exe")
return IsZipAM2R11ReturnCodes.GameIsInASubfolder; return IsZipAM2R11ReturnCodes.GameIsInASubfolder;
// Check validity // Check validity
am2rExe.ExtractToFile($"{tmpPath}/{am2rExe.FullName}"); am2rExe.ExtractToFile($"{tmpPath}/{am2rExe.FullName}");
if (HelperMethods.CalculateMD5($"{tmpPath}/{am2rExe.FullName}") != am2rHash) if (HelperMethods.CalculateMD5($"{tmpPath}/{am2rExe.FullName}") != am2rHash)
return IsZipAM2R11ReturnCodes.MissingOrInvalidAM2RExe; return IsZipAM2R11ReturnCodes.MissingOrInvalidAM2RExe;
// Check if data.win exists / is valid // Check if data.win exists / is valid
ZipArchiveEntry dataWin = am2rZip.Entries.FirstOrDefault(x => x.FullName == "data.win"); ZipArchiveEntry dataWin = am2rZip.Entries.FirstOrDefault(x => x.FullName == "data.win");
if (dataWin == null) if (dataWin == null)
return IsZipAM2R11ReturnCodes.MissingOrInvalidDataWin; return IsZipAM2R11ReturnCodes.MissingOrInvalidDataWin;
dataWin.ExtractToFile($"{tmpPath}/{dataWin.FullName}"); dataWin.ExtractToFile($"{tmpPath}/{dataWin.FullName}");
if (HelperMethods.CalculateMD5($"{tmpPath}/{dataWin.FullName}") != dataWinHash) if (HelperMethods.CalculateMD5($"{tmpPath}/{dataWin.FullName}") != dataWinHash)
return IsZipAM2R11ReturnCodes.MissingOrInvalidDataWin; return IsZipAM2R11ReturnCodes.MissingOrInvalidDataWin;
// Check if d3d.dll exists / is valid // Check if d3d.dll exists / is valid
ZipArchiveEntry d3dx = am2rZip.Entries.FirstOrDefault(x => x.FullName == "D3DX9_43.dll"); ZipArchiveEntry d3dx = am2rZip.Entries.FirstOrDefault(x => x.FullName == "D3DX9_43.dll");
if (d3dx == null) if (d3dx == null)
return IsZipAM2R11ReturnCodes.MissingOrInvalidD3DX943Dll; return IsZipAM2R11ReturnCodes.MissingOrInvalidD3DX943Dll;
d3dx.ExtractToFile($"{tmpPath}/{d3dx.FullName}"); d3dx.ExtractToFile($"{tmpPath}/{d3dx.FullName}");
if (HelperMethods.CalculateMD5($"{tmpPath}/{d3dx.FullName}") != d3dHash) if (HelperMethods.CalculateMD5($"{tmpPath}/{d3dx.FullName}") != d3dHash)
return IsZipAM2R11ReturnCodes.MissingOrInvalidD3DX943Dll; return IsZipAM2R11ReturnCodes.MissingOrInvalidD3DX943Dll;
// Clean up // Clean up
Directory.Delete(tmpPath, true); Directory.Delete(tmpPath, true);
@ -146,7 +151,7 @@ public static class Profile
} }
/// <summary> /// <summary>
/// Git Pulls from the repository. /// Git Pulls patching content from the repository.
/// </summary> /// </summary>
public static void PullPatchData(Func<TransferProgress, bool> transferProgressHandlerMethod) public static void PullPatchData(Func<TransferProgress, bool> transferProgressHandlerMethod)
{ {
@ -214,19 +219,15 @@ public static class Profile
if (!File.Exists($"{dir.FullName}/profile.xml")) if (!File.Exists($"{dir.FullName}/profile.xml"))
continue; continue;
ProfileXML prof = Serializer.Deserialize<ProfileXML>(File.ReadAllText($"{dir.FullName}/profile.xml")); ProfileXML profile = Serializer.Deserialize<ProfileXML>(File.ReadAllText($"{dir.FullName}/profile.xml"));
profile.DataPath = $"/Mods/{dir.Name}";
// Safety check for non-installable profiles // Safety check for non-installable profiles
if (prof.Installable || IsProfileInstalled(prof))
{
prof.DataPath = $"/Mods/{dir.Name}";
profileList.Add(prof);
}
// If not installable and isn't installed, remove it // If not installable and isn't installed, remove it
else if (!IsProfileInstalled(prof)) if (!profile.Installable && IsProfileInstalled(profile))
{ DeleteProfile(profile);
prof.DataPath = $"/Mods/{dir.Name}"; else
DeleteProfile(prof); profileList.Add(profile);
}
} }
log.Info($"Loaded {profileList.Count} profile(s)."); log.Info($"Loaded {profileList.Count} profile(s).");
@ -242,6 +243,7 @@ public static class Profile
// Temporarily serialize and deserialize to essentially "clone" the variable as otherwise we'd modify references // Temporarily serialize and deserialize to essentially "clone" the variable as otherwise we'd modify references
File.WriteAllText($"{Path.GetTempPath()}/{profile.Name}", Serializer.Serialize<ProfileXML>(profile)); File.WriteAllText($"{Path.GetTempPath()}/{profile.Name}", Serializer.Serialize<ProfileXML>(profile));
profile = Serializer.Deserialize<ProfileXML>(File.ReadAllText($"{Path.GetTempPath()}/{profile.Name}")); profile = Serializer.Deserialize<ProfileXML>(File.ReadAllText($"{Path.GetTempPath()}/{profile.Name}"));
File.Delete($"{Path.GetTempPath()}/{profile.Name}");
string originalName = profile.Name; string originalName = profile.Name;
// Change name to include version and be unique // Change name to include version and be unique
@ -322,12 +324,11 @@ public static class Profile
{ {
log.Info($"Installing profile {profile.Name}..."); log.Info($"Installing profile {profile.Name}...");
string profilesHomePath = Core.ProfilesPath; string profilePath = $"{Core.ProfilesPath}/{profile.Name}";
string profilePath = $"{profilesHomePath}/{profile.Name}";
// Failsafe for Profiles directory // Failsafe for Profiles directory
if (!Directory.Exists(profilesHomePath)) if (!Directory.Exists(Core.ProfilesPath))
Directory.CreateDirectory(profilesHomePath); Directory.CreateDirectory(Core.ProfilesPath);
// This failsafe should NEVER get triggered, but Miepee's broken this too much for me to trust it otherwise. // This failsafe should NEVER get triggered, but Miepee's broken this too much for me to trust it otherwise.
if (Directory.Exists(profilePath)) if (Directory.Exists(profilePath))
@ -336,7 +337,7 @@ public static class Profile
// Create profile directory // Create profile directory
Directory.CreateDirectory(profilePath); Directory.CreateDirectory(profilePath);
// Switch profilePath on Gtk // Switch profilePath on Linux and Mac, as they need special handling
if (OS.IsLinux) if (OS.IsLinux)
{ {
profilePath += "/assets"; profilePath += "/assets";
@ -346,22 +347,18 @@ public static class Profile
{ {
// Folder structure for mac is like this: // Folder structure for mac is like this:
// am2r.app -> Contents // am2r.app -> Contents
// -Frameworks (some libs) // |-Frameworks (some libs)
// -MacOS (runner) // |-MacOS (runner)
// -Resources (asset path) // |-Resources (asset path)
profilePath += "/AM2R.app/Contents"; profilePath += "/AM2R.app/Contents";
Directory.CreateDirectory(profilePath); Directory.CreateDirectory(profilePath);
Directory.CreateDirectory($"{profilePath}/MacOS"); Directory.CreateDirectory($"{profilePath}/MacOS");
Directory.CreateDirectory($"{profilePath}/Resources"); Directory.CreateDirectory($"{profilePath}/Resources");
profilePath += "/Resources"; profilePath += "/Resources";
log.Info("ProfileInstallation: Created folder structure.");
} }
// Extract 1.1 // Extract 1.1
ZipFile.ExtractToDirectory(Core.AM2R11File, profilePath); ZipFile.ExtractToDirectory(Core.AM2R11File, profilePath);
// Extracted 1.1
progress.Report(33); progress.Report(33);
log.Info("Profile folder created and AM2R_11.zip extracted."); log.Info("Profile folder created and AM2R_11.zip extracted.");
@ -394,16 +391,16 @@ public static class Profile
log.Error($"{OS.Name} does not have valid runner / data.win names!"); log.Error($"{OS.Name} does not have valid runner / data.win names!");
} }
// Patch runner and data file.
log.Info($"Attempting to patch in {profilePath}"); log.Info($"Attempting to patch in {profilePath}");
if (OS.IsWindows) if (OS.IsWindows)
{ {
// Patch game executable
if (profile.UsesYYC) if (profile.UsesYYC)
{ {
CrossPlatformOperations.ApplyXdeltaPatch($"{profilePath}/data.win", $"{dataPath}/AM2R.xdelta", $"{profilePath}/{exe}"); CrossPlatformOperations.ApplyXdeltaPatch($"{profilePath}/data.win", $"{dataPath}/AM2R.xdelta", $"{profilePath}/{exe}");
// Delete 1.1's data.win, we don't need it anymore! // Delete 1.1's data.win, we don't need it anymore!
// TODO: *theoretically* if someone would make some game like serradius in gms2 and push that as a yyc am2r mod, this *will* break!
File.Delete($"{profilePath}/data.win"); File.Delete($"{profilePath}/data.win");
} }
else else
@ -412,11 +409,12 @@ public static class Profile
CrossPlatformOperations.ApplyXdeltaPatch($"{profilePath}/AM2R.exe", $"{dataPath}/AM2R.xdelta", $"{profilePath}/{exe}"); CrossPlatformOperations.ApplyXdeltaPatch($"{profilePath}/AM2R.exe", $"{dataPath}/AM2R.xdelta", $"{profilePath}/{exe}");
} }
} }
else if (OS.IsUnix) // YYC and VM look exactly the same on Linux and Mac so we're all good here. // YYC and VM look exactly the same on Linux and Mac so we're all good here.
else if (OS.IsUnix)
{ {
CrossPlatformOperations.ApplyXdeltaPatch($"{profilePath}/data.win", $"{dataPath}/game.xdelta", $"{profilePath}/{dataWin}"); CrossPlatformOperations.ApplyXdeltaPatch($"{profilePath}/data.win", $"{dataPath}/game.xdelta", $"{profilePath}/{dataWin}");
CrossPlatformOperations.ApplyXdeltaPatch($"{profilePath}/AM2R.exe", $"{dataPath}/AM2R.xdelta", $"{profilePath}/{exe}"); CrossPlatformOperations.ApplyXdeltaPatch($"{profilePath}/AM2R.exe", $"{dataPath}/AM2R.xdelta", $"{profilePath}/{exe}");
// Just in case the resulting file isn't chmod-ed... // Just in case the resulting file isn't set as executable...
Process.Start("chmod", $"+x \"{profilePath}/{exe}\"")?.WaitForExit(); Process.Start("chmod", $"+x \"{profilePath}/{exe}\"")?.WaitForExit();
// These are not needed by linux or Mac at all, so we delete them // These are not needed by linux or Mac at all, so we delete them
@ -426,7 +424,7 @@ public static class Profile
// Move exe one directory out on Linux, move to MacOS folder instead on Mac // Move exe one directory out on Linux, move to MacOS folder instead on Mac
if (OS.IsLinux) if (OS.IsLinux)
File.Move($"{profilePath}/{exe}", $"{profilePath.Substring(0, profilePath.LastIndexOf("/"))}/{exe}"); File.Move($"{profilePath}/{exe}", $"{profilePath}/../{exe}");
else else
File.Move($"{profilePath}/{exe}", $"{profilePath.Replace("Resources", "MacOS")}/{exe}"); File.Move($"{profilePath}/{exe}", $"{profilePath.Replace("Resources", "MacOS")}/{exe}");
} }
@ -447,7 +445,6 @@ public static class Profile
if (!profile.UsesCustomMusic && useHqMusic) if (!profile.UsesCustomMusic && useHqMusic)
HelperMethods.DirectoryCopy($"{Core.PatchDataPath}/data/HDR_HQ_in-game_music", profilePath); HelperMethods.DirectoryCopy($"{Core.PatchDataPath}/data/HDR_HQ_in-game_music", profilePath);
// Linux post-process // Linux post-process
if (OS.IsLinux) if (OS.IsLinux)
{ {
@ -473,9 +470,10 @@ public static class Profile
File.Copy($"{profilePath}/{exe}", $"{profilePath}/AM2R.AppDir/usr/bin/{exe}"); File.Copy($"{profilePath}/{exe}", $"{profilePath}/AM2R.AppDir/usr/bin/{exe}");
progress.Report(66); progress.Report(66);
log.Info("Gtk-specific formatting finished."); log.Info("Linux specific formatting finished.");
// Temp save the currentWorkingDirectory and console.error, change it to profilePath and null, call the script, and change it back. // Temp save the currentWorkingDirectory and STDERR, change it to profilePath and null, call the tool, and change it back.
// Reason why STDERR is changed is because the tool prints some output to there that we don't want
string workingDir = Directory.GetCurrentDirectory(); string workingDir = Directory.GetCurrentDirectory();
TextWriter cliError = Console.Error; TextWriter cliError = Console.Error;
Directory.SetCurrentDirectory(profilePath); Directory.SetCurrentDirectory(profilePath);
@ -512,17 +510,17 @@ public static class Profile
File.Copy($"{Core.PatchDataPath}/data/PkgInfo", $"{profilePath.Replace("Resources", "")}/PkgInfo", true); File.Copy($"{Core.PatchDataPath}/data/PkgInfo", $"{profilePath.Replace("Resources", "")}/PkgInfo", true);
//Put profilePath back to what it was before //Put profilePath back to what it was before
profilePath = $"{profilesHomePath}/{profile.Name}"; profilePath = $"{Core.ProfilesPath}/{profile.Name}";
} }
// Copy profile.xml so we can grab data to compare for updates later! // Copy profile.xml so we can grab data to compare for updates later!
// tldr; check if we're in PatchData or not // check if we're in PatchData or not, as we need to search for profile.xml in different locations.
if (new DirectoryInfo(dataPath).Parent?.Name == "PatchData") if (new DirectoryInfo(dataPath).Parent?.Name == "PatchData")
File.Copy($"{dataPath}/../profile.xml", $"{profilePath}/profile.xml"); File.Copy($"{dataPath}/../profile.xml", $"{profilePath}/profile.xml");
else else
File.Copy($"{dataPath}/profile.xml", $"{profilePath}/profile.xml"); File.Copy($"{dataPath}/profile.xml", $"{profilePath}/profile.xml");
// Installed datafiles // Done
progress.Report(100); progress.Report(100);
log.Info($"Successfully installed profile {profile.Name}."); log.Info($"Successfully installed profile {profile.Name}.");
} }
@ -560,10 +558,12 @@ public static class Profile
log.Info($"Creating Android APK for profile {profile.Name}."); log.Info($"Creating Android APK for profile {profile.Name}.");
// Create working dir after some cleanup // Create working dir after some cleanup
string apktoolPath = $"{Core.PatchDataPath}/utilities/android/apktool.jar", string apktoolPath = $"{Core.PatchDataPath}/utilities/android/apktool.jar";
uberPath = $"{Core.PatchDataPath}/utilities/android/uber-apk-signer.jar", string uberPath = $"{Core.PatchDataPath}/utilities/android/uber-apk-signer.jar";
tempDir = new DirectoryInfo($"{CrossPlatformOperations.CurrentPath}/temp").FullName, string tempDir = new DirectoryInfo($"{CrossPlatformOperations.CurrentPath}/temp").FullName;
dataPath = CrossPlatformOperations.CurrentPath + profile.DataPath; string dataPath = CrossPlatformOperations.CurrentPath + profile.DataPath;
// Clean up in case Directory exists already
if (Directory.Exists(tempDir)) if (Directory.Exists(tempDir))
Directory.Delete(tempDir, true); Directory.Delete(tempDir, true);
Directory.CreateDirectory(tempDir); Directory.CreateDirectory(tempDir);
@ -583,6 +583,7 @@ public static class Profile
if (useHqMusic) if (useHqMusic)
HelperMethods.DirectoryCopy($"{Core.PatchDataPath}/data/HDR_HQ_in-game_music", workingDir); HelperMethods.DirectoryCopy($"{Core.PatchDataPath}/data/HDR_HQ_in-game_music", workingDir);
// Yes, I'm aware this is dumb. If you've got any better ideas for how to copy a seemingly randomly named .ini from this folder to the APK, please let me know. // Yes, I'm aware this is dumb. If you've got any better ideas for how to copy a seemingly randomly named .ini from this folder to the APK, please let me know.
foreach (FileInfo file in new DirectoryInfo(dataPath).GetFiles().Where(f => f.Name.EndsWith("ini"))) foreach (FileInfo file in new DirectoryInfo(dataPath).GetFiles().Where(f => f.Name.EndsWith("ini")))
File.Copy(file.FullName, $"{workingDir}/{file.Name}"); File.Copy(file.FullName, $"{workingDir}/{file.Name}");
@ -637,6 +638,7 @@ public static class Profile
/// </summary> /// </summary>
public static void RunGame(ProfileXML profile, bool useLogging, string envVars = "") public static void RunGame(ProfileXML profile, bool useLogging, string envVars = "")
{ {
//TODO: double check this and clean
// These are used on both windows and linux for game logging // These are used on both windows and linux for game logging
string savePath = OS.IsWindows ? profile.SaveLocation.Replace("%localappdata%", Environment.GetEnvironmentVariable("LOCALAPPDATA")) string savePath = OS.IsWindows ? profile.SaveLocation.Replace("%localappdata%", Environment.GetEnvironmentVariable("LOCALAPPDATA"))
: profile.SaveLocation.Replace("~", CrossPlatformOperations.Home); : profile.SaveLocation.Replace("~", CrossPlatformOperations.Home);

@ -54,7 +54,7 @@ public class ProfileXML
[XmlAttribute("ProfileNotes")] [XmlAttribute("ProfileNotes")]
public string ProfileNotes public string ProfileNotes
{ get; set; } { get; set; }
/// <summary>This gets calculated at runtime, by the MainForm. Indicates where the install data for the mod is stored.</summary> /// <summary>This gets calculated at runtime, by the Launcher. Indicates where the install data for the mod is stored.</summary>
[XmlIgnore] [XmlIgnore]
public string DataPath public string DataPath
{ get; set; } { get; set; }

Loading…
Cancel
Save