Lib: use string interpolation + clean up archiveProfile

pull/32/head
Miepee 4 years ago
parent b2da8d251d
commit b2e7ad697a

@ -8,6 +8,7 @@ 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;
@ -28,7 +29,6 @@ public enum IsZipAM2R11ReturnCodes
/// </summary> /// </summary>
public static class Profile public static class Profile
{ {
/// <summary> /// <summary>
/// The logger for <see cref="Core"/>, used to write any caught exceptions. /// The logger for <see cref="Core"/>, used to write any caught exceptions.
/// </summary> /// </summary>
@ -66,7 +66,7 @@ public static class Profile
// Check if it's valid, if not log it, rename it and silently leave // Check if it's valid, if not log it, rename it and silently leave
if (returnCode != IsZipAM2R11ReturnCodes.Successful) if (returnCode != IsZipAM2R11ReturnCodes.Successful)
{ {
log.Info("Detected invalid AM2R_11 zip with following error code: " + returnCode); log.Info($"Detected invalid AM2R_11 zip with following error code: {returnCode}");
HelperMethods.RecursiveRollover(am2r11file); HelperMethods.RecursiveRollover(am2r11file);
isAM2R11InstalledCache = false; isAM2R11InstalledCache = false;
return false; return false;
@ -117,24 +117,24 @@ public static class Profile
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
@ -193,9 +193,9 @@ public static class Profile
List<ProfileXML> profileList = new List<ProfileXML>(); List<ProfileXML> profileList = new List<ProfileXML>();
// Check for and add the Community Updates profile // Check for and add the Community Updates profile
if (File.Exists(Core.PatchDataPath + "/profile.xml")) if (File.Exists($"{Core.PatchDataPath}/profile.xml"))
{ {
ProfileXML profile = Serializer.Deserialize<ProfileXML>(File.ReadAllText(Core.PatchDataPath + "/profile.xml")); ProfileXML profile = Serializer.Deserialize<ProfileXML>(File.ReadAllText($"{Core.PatchDataPath}/profile.xml"));
profile.DataPath = "/PatchData/data"; profile.DataPath = "/PatchData/data";
profileList.Add(profile); profileList.Add(profile);
} }
@ -211,79 +211,78 @@ public static class Profile
foreach (DirectoryInfo dir in modsDir.GetDirectories()) foreach (DirectoryInfo dir in modsDir.GetDirectories())
{ {
// If no profile.xml exists we don't add anything // If no profile.xml exists we don't add anything
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 prof = Serializer.Deserialize<ProfileXML>(File.ReadAllText($"{dir.FullName}/profile.xml"));
// Safety check for non-installable profiles // Safety check for non-installable profiles
if (prof.Installable || IsProfileInstalled(prof)) if (prof.Installable || IsProfileInstalled(prof))
{ {
prof.DataPath = "/Mods/" + dir.Name; prof.DataPath = $"/Mods/{dir.Name}";
profileList.Add(prof); 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)) else if (!IsProfileInstalled(prof))
{ {
prof.DataPath = "/Mods/" + dir.Name; prof.DataPath = $"/Mods/{dir.Name}";
DeleteProfile(prof); DeleteProfile(prof);
} }
} }
log.Info("Loaded " + profileList.Count + " profile(s)."); log.Info($"Loaded {profileList.Count} profile(s).");
return profileList; return profileList;
} }
/// <summary> /// <summary>
/// Archives a given Profile by making a copy with "Name (version)". Does silently nothing if user archives already exist /// Archives a given Profile by making a copy with "Name (version)". Silently does nothing if user archives already exist.
/// </summary> /// </summary>
/// <param name="profile">The profile to archive</param> /// <param name="profile">The profile to archive</param>
public static void ArchiveProfile(ProfileXML profile) public static void ArchiveProfile(ProfileXML 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}"));
string originalName = profile.Name; string originalName = profile.Name;
// Change name to include version and be unique // Change name to include version and be unique
profile.Name += " (" + profile.Version + ")"; profile.Name += $" ({profile.Version})";
// if we're archiving community updates, remove the "latest" part // if we're archiving community updates, remove the "latest" part
profile.Name = profile.Name.Replace("Community Updates Latest", "Community Updates"); profile.Name = profile.Name.Replace("Community Updates Latest", "Community Updates");
log.Info("Archiving " + profile.Name); log.Info($"Archiving {profile.Name}");
string profileArchivePath = Core.ProfilesPath + "/" + profile.Name; // Rename directory in the profiles folder
string profileArchivePath = $"{Core.ProfilesPath}/{profile.Name}";
// Do NOT overwrite if a path with this name already exists! It is likely an existing user archive. // If our desired rename already exists, it's probably a user archive...
if (!Directory.Exists(profileArchivePath)) // so we just delete the original folder and move on.
if (Directory.Exists(profileArchivePath))
{ {
// Rename current profile if we have it installed HelperMethods.DeleteDirectory($"{Core.ProfilesPath}/{originalName}");
if (Directory.Exists(Core.ProfilesPath + "/" + originalName)) log.Info("Cancelling archival! User-defined archive in Profiles already exists.");
Directory.Move(Core.ProfilesPath + "/" + originalName, profileArchivePath); return;
}
// Set as non-installable so that it's just treated as a launching reference // Rename current profile if we have it installed
profile.Installable = false; if (Directory.Exists($"{Core.ProfilesPath}/{originalName}"))
Directory.Move($"{Core.ProfilesPath}/{originalName}", profileArchivePath);
string modArchivePath = Core.ModsPath + "/" + profile.Name; // Set as non-installable so that it's just treated as a launching reference
profile.Installable = false;
// Do NOT overwrite if a path with this name already exists! It is likely an existing user archive. // Try archiving profile as a mod
if (!Directory.Exists(modArchivePath)) string modArchivePath = $"{Core.ModsPath}/{profile.Name}";
{
Directory.CreateDirectory(modArchivePath); if (Directory.Exists(modArchivePath))
File.WriteAllText(modArchivePath + "/profile.xml", Serializer.Serialize<ProfileXML>(profile));
log.Info("Finished archival.");
}
else
{
HelperMethods.DeleteDirectory(profileArchivePath);
log.Info("Cancelling archival! User-defined archive in Mods already exists.");
}
}
// If our desired rename already exists, it's probably a user archive... so we just delete the original folder and move on with installation of the new version.
else
{ {
HelperMethods.DeleteDirectory(Core.ProfilesPath + "/" + originalName); HelperMethods.DeleteDirectory(profileArchivePath);
log.Info("Cancelling archival! User-defined archive in Profiles already exists."); log.Info("Cancelling archival! User-defined archive in Mods already exists.");
return;
} }
Directory.CreateDirectory(modArchivePath);
File.WriteAllText($"{modArchivePath}/profile.xml", Serializer.Serialize<ProfileXML>(profile));
log.Info("Finished archival.");
} }
/// <summary> /// <summary>
@ -292,25 +291,25 @@ public static class Profile
/// <param name="profile">The profile to delete.</param> /// <param name="profile">The profile to delete.</param>
public static void DeleteProfile(ProfileXML profile) public static void DeleteProfile(ProfileXML profile)
{ {
log.Info("Attempting to delete profile " + profile.Name + "..."); log.Info($"Attempting to delete profile {profile.Name}...");
// Delete folder in Mods // Delete folder in Mods
if (Directory.Exists(CrossPlatformOperations.CurrentPath + profile.DataPath)) if (Directory.Exists(CrossPlatformOperations.CurrentPath + profile.DataPath))
HelperMethods.DeleteDirectory(CrossPlatformOperations.CurrentPath + profile.DataPath); HelperMethods.DeleteDirectory(CrossPlatformOperations.CurrentPath + profile.DataPath);
// Delete the zip file in Mods // Delete the zip file in Mods
if (File.Exists(CrossPlatformOperations.CurrentPath + profile.DataPath + ".zip")) if (File.Exists($"{CrossPlatformOperations.CurrentPath}{profile.DataPath}.zip"))
{ {
// For some reason, it was set at read only, so we undo that here // For some reason, it was set at read only, so we undo that here
File.SetAttributes(CrossPlatformOperations.CurrentPath + profile.DataPath + ".zip", FileAttributes.Normal); File.SetAttributes($"{CrossPlatformOperations.CurrentPath}{profile.DataPath}.zip", FileAttributes.Normal);
File.Delete(CrossPlatformOperations.CurrentPath + profile.DataPath + ".zip"); File.Delete($"{CrossPlatformOperations.CurrentPath}{profile.DataPath}.zip");
} }
// Delete folder in Profiles // Delete folder in Profiles
if (Directory.Exists(Core.ProfilesPath + "/" + profile.Name)) if (Directory.Exists($"{Core.ProfilesPath}/{profile.Name}"))
HelperMethods.DeleteDirectory(Core.ProfilesPath + "/" + profile.Name); HelperMethods.DeleteDirectory($"{Core.ProfilesPath}/{profile.Name}");
log.Info("Successfully deleted profile " + profile.Name + "."); log.Info($"Successfully deleted profile {profile.Name}.");
} }
/// <summary> /// <summary>
@ -321,10 +320,10 @@ public static class Profile
/// <param name="progress">Provides the current progress of this method.</param> /// <param name="progress">Provides the current progress of this method.</param>
public static void InstallProfile(ProfileXML profile, bool useHqMusic, IProgress<int> progress) public static void InstallProfile(ProfileXML profile, bool useHqMusic, IProgress<int> progress)
{ {
log.Info("Installing profile " + profile.Name + "..."); log.Info($"Installing profile {profile.Name}...");
string profilesHomePath = Core.ProfilesPath; string profilesHomePath = Core.ProfilesPath;
string profilePath = profilesHomePath + "/" + profile.Name; string profilePath = $"{profilesHomePath}/{profile.Name}";
// Failsafe for Profiles directory // Failsafe for Profiles directory
if (!Directory.Exists(profilesHomePath)) if (!Directory.Exists(profilesHomePath))
@ -352,8 +351,8 @@ public static class Profile
// -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."); log.Info("ProfileInstallation: Created folder structure.");
@ -381,9 +380,9 @@ public static class Profile
{ {
dataWin = "game.unx"; dataWin = "game.unx";
// Use the exe name based on the desktop file in the AppImage, rather than hard coding it. // Use the exe name based on the desktop file in the AppImage, rather than hard coding it.
string desktopContents = File.ReadAllText(Core.PatchDataPath + "/data/AM2R.AppDir/AM2R.desktop"); string desktopContents = File.ReadAllText($"{Core.PatchDataPath}/data/AM2R.AppDir/AM2R.desktop");
exe = Regex.Match(desktopContents, @"(?<=Exec=).*").Value; exe = Regex.Match(desktopContents, @"(?<=Exec=).*").Value;
log.Info("According to AppImage desktop file, using \"" + exe + "\" as game name."); log.Info($"According to AppImage desktop file, using \"{exe}\" as game name.");
} }
else if (OS.IsMac) else if (OS.IsMac)
{ {
@ -392,48 +391,48 @@ public static class Profile
} }
else else
{ {
log.Error(OS.Name + " does not have valid runner / data.win names!"); log.Error($"{OS.Name} does not have valid runner / data.win names!");
} }
log.Info("Attempting to patch in " + profilePath); log.Info($"Attempting to patch in {profilePath}");
if (OS.IsWindows) if (OS.IsWindows)
{ {
// Patch game executable // 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!
File.Delete(profilePath + "/data.win"); File.Delete($"{profilePath}/data.win");
} }
else else
{ {
CrossPlatformOperations.ApplyXdeltaPatch(profilePath + "/data.win", dataPath + "/data.xdelta", profilePath + "/" + dataWin); CrossPlatformOperations.ApplyXdeltaPatch($"{profilePath}/data.win", $"{dataPath}/data.xdelta", $"{profilePath}/{dataWin}");
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. else if (OS.IsUnix) // YYC and VM look exactly the same on Linux and Mac so we're all good here.
{ {
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 chmod-ed...
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
File.Delete(profilePath + "/data.win"); File.Delete($"{profilePath}/data.win");
File.Delete(profilePath + "/AM2R.exe"); File.Delete($"{profilePath}/AM2R.exe");
File.Delete(profilePath + "/D3DX9_43.dll"); File.Delete($"{profilePath}/D3DX9_43.dll");
// 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.Substring(0, profilePath.LastIndexOf("/"))}/{exe}");
else else
File.Move(profilePath + "/" + exe, profilePath.Replace("Resources", "MacOS") + "/" + exe); File.Move($"{profilePath}/{exe}", $"{profilePath.Replace("Resources", "MacOS")}/{exe}");
} }
else else
{ {
log.Error(OS.Name + " does not have patching methods!"); log.Error($"{OS.Name} does not have patching methods!");
} }
// Applied patch // Applied patch
@ -442,11 +441,11 @@ public static class Profile
log.Info("xdelta patch(es) applied."); log.Info("xdelta patch(es) applied.");
// Install new datafiles // Install new datafiles
HelperMethods.DirectoryCopy(dataPath + "/files_to_copy", profilePath); HelperMethods.DirectoryCopy($"{dataPath}/files_to_copy", profilePath);
// HQ music // HQ music
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
@ -458,20 +457,20 @@ public static class Profile
// Rename all songs to lowercase // Rename all songs to lowercase
foreach (FileInfo file in new DirectoryInfo(assetsPath).GetFiles()) foreach (FileInfo file in new DirectoryInfo(assetsPath).GetFiles())
{ {
if (file.Name.EndsWith(".ogg") && !File.Exists(file.DirectoryName + "/" + file.Name.ToLower())) if (file.Name.EndsWith(".ogg") && !File.Exists($"{file.DirectoryName}/{file.Name.ToLower()}"))
File.Move(file.FullName, file.DirectoryName + "/" + file.Name.ToLower()); File.Move(file.FullName, $"{file.DirectoryName}/{file.Name.ToLower()}");
} }
// Copy AppImage template to here // Copy AppImage template to here
HelperMethods.DirectoryCopy(Core.PatchDataPath + "/data/AM2R.AppDir", profilePath + "/AM2R.AppDir/"); HelperMethods.DirectoryCopy($"{Core.PatchDataPath}/data/AM2R.AppDir", $"{profilePath}/AM2R.AppDir/");
// Safety checks, in case the folders don't exist // Safety checks, in case the folders don't exist
Directory.CreateDirectory(profilePath + "/AM2R.AppDir/usr/bin/"); Directory.CreateDirectory($"{profilePath}/AM2R.AppDir/usr/bin/");
Directory.CreateDirectory(profilePath + "/AM2R.AppDir/usr/bin/assets/"); Directory.CreateDirectory($"{profilePath}/AM2R.AppDir/usr/bin/assets/");
// Copy game assets to the AppImageDir // Copy game assets to the AppImageDir
HelperMethods.DirectoryCopy(assetsPath, profilePath + "/AM2R.AppDir/usr/bin/assets/"); HelperMethods.DirectoryCopy(assetsPath, $"{profilePath}/AM2R.AppDir/usr/bin/assets/");
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("Gtk-specific formatting finished.");
@ -482,16 +481,16 @@ public static class Profile
Directory.SetCurrentDirectory(profilePath); Directory.SetCurrentDirectory(profilePath);
Console.SetError(new StreamWriter(Stream.Null)); Console.SetError(new StreamWriter(Stream.Null));
Environment.SetEnvironmentVariable("ARCH", "x86_64"); Environment.SetEnvironmentVariable("ARCH", "x86_64");
Process.Start(Core.PatchDataPath + "/utilities/appimagetool-x86_64.AppImage", "-n AM2R.AppDir")?.WaitForExit(); Process.Start($"{Core.PatchDataPath}/utilities/appimagetool-x86_64.AppImage", "-n AM2R.AppDir")?.WaitForExit();
Directory.SetCurrentDirectory(workingDir); Directory.SetCurrentDirectory(workingDir);
Console.SetError(cliError); Console.SetError(cliError);
// Clean files // Clean files
Directory.Delete(profilePath + "/AM2R.AppDir", true); Directory.Delete($"{profilePath}/AM2R.AppDir", true);
Directory.Delete(assetsPath, true); Directory.Delete(assetsPath, true);
File.Delete(profilePath + "/" + exe); File.Delete($"{profilePath}/{exe}");
if (File.Exists(profilePath + "/AM2R.AppImage")) File.Delete(profilePath + "/AM2R.AppImage"); if (File.Exists($"{profilePath}/AM2R.AppImage")) File.Delete($"{profilePath}/AM2R.AppImage");
File.Move(profilePath + "/" + "AM2R-x86_64.AppImage", profilePath + "/AM2R.AppImage"); File.Move($"{profilePath}/AM2R-x86_64.AppImage", $"{profilePath}/AM2R.AppImage");
} }
// Mac post-process // Mac post-process
else if (OS.IsMac) else if (OS.IsMac)
@ -499,33 +498,33 @@ public static class Profile
// Rename all songs to lowercase // Rename all songs to lowercase
foreach (FileInfo file in new DirectoryInfo(profilePath).GetFiles()) foreach (FileInfo file in new DirectoryInfo(profilePath).GetFiles())
{ {
if (file.Name.EndsWith(".ogg") && !File.Exists(file.DirectoryName + "/" + file.Name.ToLower())) if (file.Name.EndsWith(".ogg") && !File.Exists($"{file.DirectoryName}/{file.Name.ToLower()}"))
File.Move(file.FullName, file.DirectoryName + "/" + file.Name.ToLower()); File.Move(file.FullName, $"{file.DirectoryName}/{file.Name.ToLower()}");
} }
// Loading custom fonts crashes on Mac, so we delete those if they exist // Loading custom fonts crashes on Mac, so we delete those if they exist
if (Directory.Exists(profilePath + "/lang/fonts")) if (Directory.Exists($"{profilePath}/lang/fonts"))
Directory.Delete(profilePath + "/lang/fonts", true); Directory.Delete($"{profilePath}/lang/fonts", true);
// Move Frameworks, Info.plist and PkgInfo over // Move Frameworks, Info.plist and PkgInfo over
HelperMethods.DirectoryCopy(Core.PatchDataPath + "/data/Frameworks", profilePath.Replace("Resources", "Frameworks")); HelperMethods.DirectoryCopy($"{Core.PatchDataPath}/data/Frameworks", profilePath.Replace("Resources", "Frameworks"));
File.Copy(dataPath + "/Info.plist", profilePath.Replace("Resources", "") + "/Info.plist", true); File.Copy($"{dataPath}/Info.plist", $"{profilePath.Replace("Resources", "")}/Info.plist", true);
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 = $"{profilesHomePath}/{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 // tldr; check if we're in PatchData or not
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 // Installed datafiles
progress.Report(100); progress.Report(100);
log.Info("Successfully installed profile " + profile.Name + "."); log.Info($"Successfully installed profile {profile.Name}.");
} }
/// <summary> /// <summary>
@ -535,11 +534,11 @@ public static class Profile
/// <returns><see langword="true"/> if yes, <see langword="false"/> if not.</returns> /// <returns><see langword="true"/> if yes, <see langword="false"/> if not.</returns>
public static bool IsProfileInstalled(ProfileXML profile) public static bool IsProfileInstalled(ProfileXML profile)
{ {
if (OS.IsWindows) return File.Exists(Core.ProfilesPath + "/" + profile.Name + "/AM2R.exe"); if (OS.IsWindows) return File.Exists($"{Core.ProfilesPath}/{profile.Name}/AM2R.exe");
if (OS.IsLinux) return File.Exists(Core.ProfilesPath + "/" + profile.Name + "/AM2R.AppImage"); if (OS.IsLinux) return File.Exists($"{Core.ProfilesPath}/{profile.Name}/AM2R.AppImage");
if (OS.IsMac) return Directory.Exists(Core.ProfilesPath + "/" + profile.Name + "/AM2R.app"); if (OS.IsMac) return Directory.Exists($"{Core.ProfilesPath}/{profile.Name}/AM2R.app");
log.Error(OS.Name + " can't have profiles installed!"); log.Error($"{OS.Name} can't have profiles installed!");
return false; return false;
} }
@ -558,12 +557,12 @@ public static class Profile
return; return;
} }
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", uberPath = $"{Core.PatchDataPath}/utilities/android/uber-apk-signer.jar",
tempDir = new DirectoryInfo(CrossPlatformOperations.CurrentPath + "/temp").FullName, tempDir = new DirectoryInfo($"{CrossPlatformOperations.CurrentPath}/temp").FullName,
dataPath = CrossPlatformOperations.CurrentPath + profile.DataPath; dataPath = CrossPlatformOperations.CurrentPath + profile.DataPath;
if (Directory.Exists(tempDir)) if (Directory.Exists(tempDir))
Directory.Delete(tempDir, true); Directory.Delete(tempDir, true);
@ -573,64 +572,64 @@ public static class Profile
progress.Report(14); progress.Report(14);
// Decompile AM2RWrapper.apk // Decompile AM2RWrapper.apk
CrossPlatformOperations.RunJavaJar("\"" + apktoolPath + "\" d \"" + dataPath + "/android/AM2RWrapper.apk\"", tempDir); CrossPlatformOperations.RunJavaJar($"\"{apktoolPath}\" d \"{dataPath}/android/AM2RWrapper.apk\"", tempDir);
log.Info("AM2RWrapper decompiled."); log.Info("AM2RWrapper decompiled.");
progress.Report(28); progress.Report(28);
// Add datafiles: 1.1, new datafiles, hq music, am2r.ini // Add datafiles: 1.1, new datafiles, hq music, am2r.ini
string workingDir = tempDir + "/AM2RWrapper/assets"; string workingDir = $"{tempDir}/AM2RWrapper/assets";
ZipFile.ExtractToDirectory(Core.AM2R11File, workingDir); ZipFile.ExtractToDirectory(Core.AM2R11File, workingDir);
HelperMethods.DirectoryCopy(dataPath + "/files_to_copy", workingDir); HelperMethods.DirectoryCopy($"{dataPath}/files_to_copy", workingDir);
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}");
log.Info("AM2R_11.zip extracted and datafiles copied into AM2RWrapper."); log.Info("AM2R_11.zip extracted and datafiles copied into AM2RWrapper.");
progress.Report(42); progress.Report(42);
// Patch data.win to game.droid // Patch data.win to game.droid
CrossPlatformOperations.ApplyXdeltaPatch(workingDir + "/data.win", dataPath + "/droid.xdelta", workingDir + "/game.droid"); CrossPlatformOperations.ApplyXdeltaPatch($"{workingDir}/data.win", $"{dataPath}/droid.xdelta", $"{workingDir}/game.droid");
log.Info("game.droid successfully patched."); log.Info("game.droid successfully patched.");
progress.Report(56); progress.Report(56);
// Delete unnecessary files // Delete unnecessary files
File.Delete(workingDir + "/AM2R.exe"); File.Delete($"{workingDir}/AM2R.exe");
File.Delete(workingDir + "/D3DX9_43.dll"); File.Delete($"{workingDir}/D3DX9_43.dll");
File.Delete(workingDir + "/explanations.txt"); File.Delete($"{workingDir}/explanations.txt");
File.Delete(workingDir + "/modifiers.ini"); File.Delete($"{workingDir}/modifiers.ini");
File.Delete(workingDir + "/readme.txt"); File.Delete($"{workingDir}/readme.txt");
File.Delete(workingDir + "/data.win"); File.Delete($"{workingDir}/data.win");
Directory.Delete(workingDir + "/mods", true); Directory.Delete($"{workingDir}/mods", true);
Directory.Delete(workingDir + "/lang/headers", true); Directory.Delete($"{workingDir}/lang/headers", true);
if (OS.IsLinux) File.Delete(workingDir + "/icon.png"); if (OS.IsLinux) File.Delete($"{workingDir}/icon.png");
// Modify apktool.yml to NOT compress ogg files // Modify apktool.yml to NOT compress ogg files
string apktoolText = File.ReadAllText(workingDir + "/../apktool.yml"); string apktoolText = File.ReadAllText($"{workingDir}/../apktool.yml");
apktoolText = apktoolText.Replace("doNotCompress:", "doNotCompress:\n- ogg"); apktoolText = apktoolText.Replace("doNotCompress:", "doNotCompress:\n- ogg");
File.WriteAllText(workingDir + "/../apktool.yml", apktoolText); File.WriteAllText($"{workingDir}/../apktool.yml", apktoolText);
log.Info("Unnecessary files removed, apktool.yml modified to prevent ogg compression."); log.Info("Unnecessary files removed, apktool.yml modified to prevent ogg compression.");
progress.Report(70); progress.Report(70);
// Rebuild APK // Rebuild APK
CrossPlatformOperations.RunJavaJar("\"" + apktoolPath + "\" b AM2RWrapper -o \"" + profile.Name + ".apk\"", tempDir); CrossPlatformOperations.RunJavaJar($"\"{apktoolPath}\" b AM2RWrapper -o \"{profile.Name}.apk\"", tempDir);
log.Info("AM2RWrapper rebuilt into " + profile.Name + ".apk."); log.Info($"AM2RWrapper rebuilt into {profile.Name}.apk.");
progress.Report(84); progress.Report(84);
// Debug-sign APK // Debug-sign APK
CrossPlatformOperations.RunJavaJar("\"" + uberPath + "\" -a \"" + profile.Name + ".apk\"", tempDir); CrossPlatformOperations.RunJavaJar($"\"{uberPath}\" -a \"{profile.Name}.apk\"", tempDir);
// Extra file cleanup // Extra file cleanup
File.Copy(tempDir + "/" + profile.Name + "-aligned-debugSigned.apk", CrossPlatformOperations.CurrentPath + "/" + profile.Name + ".apk", true); File.Copy($"{tempDir}/{profile.Name}-aligned-debugSigned.apk", $"{CrossPlatformOperations.CurrentPath}/{profile.Name}.apk", true);
log.Info(profile.Name + ".apk signed and moved to " + CrossPlatformOperations.CurrentPath + "/" + profile.Name + ".apk."); log.Info($"{profile.Name}.apk signed and moved to {CrossPlatformOperations.CurrentPath}/{profile.Name}.apk.");
HelperMethods.DeleteDirectory(tempDir); HelperMethods.DeleteDirectory(tempDir);
// Done // Done
progress.Report(100); progress.Report(100);
log.Info("Successfully created Android APK for profile " + profile.Name + "."); log.Info($"Successfully created Android APK for profile {profile.Name}.");
CrossPlatformOperations.OpenFolderAndSelectFile(CrossPlatformOperations.CurrentPath + "/" + profile.Name + ".apk"); CrossPlatformOperations.OpenFolderAndSelectFile($"{CrossPlatformOperations.CurrentPath}/{profile.Name}.apk");
} }
/// <summary> /// <summary>
@ -641,10 +640,10 @@ public static class Profile
// 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);
DirectoryInfo logDir = new DirectoryInfo(savePath + "/logs"); DirectoryInfo logDir = new DirectoryInfo($"{savePath}/logs");
string date = String.Join("-", DateTime.Now.ToString().Split(Path.GetInvalidFileNameChars(), StringSplitOptions.RemoveEmptyEntries)); string date = String.Join("-", DateTime.Now.ToString().Split(Path.GetInvalidFileNameChars(), StringSplitOptions.RemoveEmptyEntries));
log.Info("Launching game profile " + profile.Name + "."); log.Info($"Launching game profile {profile.Name}.");
if (OS.IsWindows) if (OS.IsWindows)
{ {
// Sets the arguments to empty, or to the profiles save path/logs and create time based logs. Creates the folder if necessary. // Sets the arguments to empty, or to the profiles save path/logs and create time based logs. Creates the folder if necessary.
@ -653,17 +652,17 @@ public static class Profile
// Game logging // Game logging
if (useLogging) if (useLogging)
{ {
log.Info("Performing logging setup for profile " + profile.Name + "."); log.Info($"Performing logging setup for profile {profile.Name}.");
if (!Directory.Exists(logDir.FullName)) if (!Directory.Exists(logDir.FullName))
Directory.CreateDirectory(logDir.FullName); Directory.CreateDirectory(logDir.FullName);
if (File.Exists(logDir.FullName + "/" + profile.Name + ".txt")) if (File.Exists($"{logDir.FullName}/{profile.Name}.txt"))
HelperMethods.RecursiveRollover(logDir.FullName + "/" + profile.Name + ".txt", 5); HelperMethods.RecursiveRollover($"{logDir.FullName}/{profile.Name}.txt", 5);
StreamWriter stream = File.AppendText(logDir.FullName + "/" + profile.Name + ".txt"); StreamWriter stream = File.AppendText($"{logDir.FullName}/{profile.Name}.txt");
stream.WriteLine("AM2RLauncher " + Core.Version + " log generated at " + date); stream.WriteLine($"AM2RLauncher {Core.Version} log generated at {date}");
if (Core.IsThisRunningFromWine) if (Core.IsThisRunningFromWine)
stream.WriteLine("Using WINE!"); stream.WriteLine("Using WINE!");
@ -672,16 +671,16 @@ public static class Profile
stream.Close(); stream.Close();
arguments = "-debugoutput \"" + logDir.FullName + "/" + profile.Name + ".txt\" -output \"" + logDir.FullName + "/" + profile.Name + ".txt\""; arguments = $"-debugoutput \"{logDir.FullName}/{profile.Name}.txt\" -output \"{logDir.FullName}/{profile.Name}.txt\"";
} }
ProcessStartInfo proc = new ProcessStartInfo(); ProcessStartInfo proc = new ProcessStartInfo();
proc.WorkingDirectory = Core.ProfilesPath + "/" + profile.Name; proc.WorkingDirectory = $"{Core.ProfilesPath}/{profile.Name}";
proc.FileName = proc.WorkingDirectory + "/AM2R.exe"; proc.FileName = $"{proc.WorkingDirectory}/AM2R.exe";
proc.Arguments = arguments; proc.Arguments = arguments;
log.Info("CWD of Profile is " + proc.WorkingDirectory); log.Info($"CWD of Profile is {proc.WorkingDirectory}");
using Process p = Process.Start(proc); using Process p = Process.Start(proc);
Core.SetForegroundWindow(p.MainWindowHandle); Core.SetForegroundWindow(p.MainWindowHandle);
@ -691,7 +690,7 @@ public static class Profile
{ {
ProcessStartInfo startInfo = new ProcessStartInfo(); ProcessStartInfo startInfo = new ProcessStartInfo();
log.Info("User does " + (String.IsNullOrWhiteSpace(envVars) ? "not" : "") + " have custom environment variables set."); log.Info($"User does {(String.IsNullOrWhiteSpace(envVars) ? "not" : "")} have custom environment variables set.");
//TODO: make this more readable at one day //TODO: make this more readable at one day
if (!String.IsNullOrWhiteSpace(envVars)) if (!String.IsNullOrWhiteSpace(envVars))
@ -700,7 +699,7 @@ public static class Profile
{ {
// Env var variable // Env var variable
string variable = envVars.Substring(0, envVars.IndexOf('=')); string variable = envVars.Substring(0, envVars.IndexOf('='));
envVars = envVars.Replace(variable + "=", ""); envVars = envVars.Replace($"{variable}=", "");
// This thing here is the value parser. Since values are sometimes in quotes, i need to compensate for them. // This thing here is the value parser. Since values are sometimes in quotes, i need to compensate for them.
int valueSubstringLength; int valueSubstringLength;
@ -723,7 +722,7 @@ public static class Profile
string value = envVars.Substring(0, valueSubstringLength); string value = envVars.Substring(0, valueSubstringLength);
envVars = envVars.Substring(value.Length); envVars = envVars.Substring(value.Length);
log.Info("Adding user variable \"" + variable + "\" with value \"" + value + "\""); log.Info($"Adding user variable \"{variable}\" with value \"{value}\"");
startInfo.EnvironmentVariables[variable] = value; startInfo.EnvironmentVariables[variable] = value;
} }
} }
@ -732,15 +731,15 @@ public static class Profile
string terminalOutput = null; string terminalOutput = null;
startInfo.UseShellExecute = false; startInfo.UseShellExecute = false;
startInfo.WorkingDirectory = Core.ProfilesPath + "/" + profile.Name; startInfo.WorkingDirectory = $"{Core.ProfilesPath}/{profile.Name}";
startInfo.FileName = startInfo.WorkingDirectory + "/AM2R.AppImage"; startInfo.FileName = $"{startInfo.WorkingDirectory}/AM2R.AppImage";
log.Info("CWD of Profile is " + startInfo.WorkingDirectory); log.Info($"CWD of Profile is {startInfo.WorkingDirectory}");
log.Debug("Launching game with following variables: "); log.Debug("Launching game with following variables: ");
foreach (System.Collections.DictionaryEntry item in startInfo.EnvironmentVariables) foreach (System.Collections.DictionaryEntry item in startInfo.EnvironmentVariables)
{ {
log.Debug("Key: \"" + item.Key + "\" Value: \"" + item.Value + "\""); log.Debug($"Key: \"{item.Key}\" Value: \"{item.Value}\"");
} }
using (Process p = new Process()) using (Process p = new Process())
@ -749,10 +748,10 @@ public static class Profile
if (useLogging) if (useLogging)
{ {
p.StartInfo.RedirectStandardOutput = true; p.StartInfo.RedirectStandardOutput = true;
p.OutputDataReceived += (_, e) => { terminalOutput += e.Data + "\n"; }; p.OutputDataReceived += (_, e) => { terminalOutput += $"{e.Data}\n"; };
p.StartInfo.RedirectStandardError = true; p.StartInfo.RedirectStandardError = true;
p.ErrorDataReceived += (_, e) => { terminalOutput += e.Data + "\n"; }; p.ErrorDataReceived += (_, e) => { terminalOutput += $"{e.Data}\n"; };
} }
p.Start(); p.Start();
@ -768,18 +767,18 @@ public static class Profile
if (terminalOutput != null) if (terminalOutput != null)
{ {
log.Info("Performed logging setup for profile " + profile.Name + "."); log.Info($"Performed logging setup for profile {profile.Name}.");
if (!Directory.Exists(logDir.FullName)) if (!Directory.Exists(logDir.FullName))
Directory.CreateDirectory(logDir.FullName); Directory.CreateDirectory(logDir.FullName);
if (File.Exists(logDir.FullName + "/" + profile.Name + ".txt")) if (File.Exists($"{logDir.FullName}/{profile.Name}.txt"))
HelperMethods.RecursiveRollover(logDir.FullName + "/" + profile.Name + ".txt", 5); HelperMethods.RecursiveRollover($"{logDir.FullName}/{profile.Name}.txt", 5);
StreamWriter stream = File.AppendText(logDir.FullName + "/" + profile.Name + ".txt"); StreamWriter stream = File.AppendText($"{logDir.FullName}/{profile.Name}.txt");
// Write general info // Write general info
stream.WriteLine("AM2RLauncher " + Core.Version + " log generated at " + date); stream.WriteLine($"AM2RLauncher {Core.Version} log generated at {date}");
// Write what was in the terminal // Write what was in the terminal
stream.WriteLine(terminalOutput); stream.WriteLine(terminalOutput);
@ -798,40 +797,40 @@ public static class Profile
// Game logging // Game logging
if (useLogging) if (useLogging)
{ {
log.Info("Performing logging setup for profile " + profile.Name + "."); log.Info($"Performing logging setup for profile {profile.Name}.");
if (!Directory.Exists(logDir.FullName)) if (!Directory.Exists(logDir.FullName))
Directory.CreateDirectory(logDir.FullName); Directory.CreateDirectory(logDir.FullName);
if (File.Exists(logDir.FullName + "/" + profile.Name + ".txt")) if (File.Exists($"{logDir.FullName}/{profile.Name}.txt"))
HelperMethods.RecursiveRollover(logDir.FullName + "/" + profile.Name + ".txt", 5); HelperMethods.RecursiveRollover($"{logDir.FullName}/{profile.Name}.txt", 5);
StreamWriter stream = File.AppendText(logDir.FullName + "/" + profile.Name + ".txt"); StreamWriter stream = File.AppendText($"{logDir.FullName}/{profile.Name}.txt");
stream.WriteLine("AM2RLauncher " + Core.Version + " log generated at " + date); stream.WriteLine($"AM2RLauncher {Core.Version} log generated at {date}");
stream.Flush(); stream.Flush();
stream.Close(); stream.Close();
arguments += " --stdout \"" + logDir.FullName + "/" + profile.Name + ".txt\" --stderr \"" + logDir.FullName + "/" + profile.Name + ".txt\""; arguments += $" --stdout \"{logDir.FullName}/{profile.Name}.txt\" --stderr \"{logDir.FullName}/{profile.Name}.txt\"";
} }
ProcessStartInfo proc = new ProcessStartInfo(); ProcessStartInfo proc = new ProcessStartInfo();
proc.WorkingDirectory = Core.ProfilesPath + "/" + profile.Name; proc.WorkingDirectory = $"{Core.ProfilesPath}/{profile.Name}";
proc.FileName = "open"; proc.FileName = "open";
proc.Arguments = arguments; proc.Arguments = arguments;
log.Info("CWD of Profile is " + proc.WorkingDirectory); log.Info($"CWD of Profile is {proc.WorkingDirectory}");
using Process p = Process.Start(proc); using Process p = Process.Start(proc);
p?.WaitForExit(); p?.WaitForExit();
} }
else else
log.Error(OS.Name + " cannot run games!"); log.Error($"{OS.Name} cannot run games!");
log.Info("Profile " + profile.Name + " process exited."); log.Info($"Profile {profile.Name} process exited.");
} }
/// <summary> /// <summary>
@ -841,6 +840,6 @@ public static class Profile
public static bool IsPatchDataCloned() public static bool IsPatchDataCloned()
{ {
// isValid seems to only check for a .git folder, and there are cases where that exists, but not the profile.xml // isValid seems to only check for a .git folder, and there are cases where that exists, but not the profile.xml
return File.Exists(Core.PatchDataPath + "/profile.xml") && Repository.IsValid(Core.PatchDataPath); return File.Exists($"{Core.PatchDataPath}/profile.xml") && Repository.IsValid(Core.PatchDataPath);
} }
} }
Loading…
Cancel
Save