diff --git a/IPA.Injector/IPA.Injector.csproj b/IPA.Injector/IPA.Injector.csproj
index 83e9ebdd..44034094 100644
--- a/IPA.Injector/IPA.Injector.csproj
+++ b/IPA.Injector/IPA.Injector.csproj
@@ -59,6 +59,7 @@
+
diff --git a/IPA.Injector/Injector.cs b/IPA.Injector/Injector.cs
index a0feae57..f96b602a 100644
--- a/IPA.Injector/Injector.cs
+++ b/IPA.Injector/Injector.cs
@@ -11,7 +11,6 @@ using System.Reflection;
using System.Runtime.InteropServices;
using UnityEngine;
using static IPA.Logging.Logger;
-using Logger = IPA.Logging.Logger;
using MethodAttributes = Mono.Cecil.MethodAttributes;
namespace IPA.Injector
@@ -27,19 +26,15 @@ namespace IPA.Injector
try
{
- // This loads System.Runtime.InteropServices, and Microsoft.Win32.SafeHandles.
Windows.WinConsole.Initialize();
- // This loads AppDomain, System.IO, System.Collections.Generic, and System.Reflection.
- // If kernel32.dll is not already loaded, this will also load it.
- // This call also loads IPA.Loader and initializes the logging system. In the process
- // it loads Ionic.Zip.
SetupLibraryLoading();
loader.Debug("Prepping bootstrapper");
-
- // // This will load Mono.Cecil
+
InstallBootstrapPatch();
+
+ Updates.InstallPendingUpdates();
}
catch (Exception e)
{
diff --git a/IPA.Injector/Updates.cs b/IPA.Injector/Updates.cs
new file mode 100644
index 00000000..658483b8
--- /dev/null
+++ b/IPA.Injector/Updates.cs
@@ -0,0 +1,57 @@
+using IPA.Utilities;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using static IPA.Logging.Logger;
+
+namespace IPA.Injector
+{
+ class Updates
+ {
+ public const string DeleteFileName = Updating.ModsaberML.Updater._SpecialDeletionsFile;
+ public static void InstallPendingUpdates()
+ {
+ var pendingDir = Path.Combine(BeatSaber.InstallPath, "IPA", "Pending");
+ if (Directory.Exists(pendingDir))
+ { // there are pending updates, install
+ updater.Info("Installing pending updates");
+
+ var toDelete = new string[0];
+ var delFn = Path.Combine(pendingDir, DeleteFileName);
+ if (File.Exists(delFn))
+ {
+ toDelete = File.ReadAllLines(delFn);
+ File.Delete(delFn);
+ }
+
+ foreach (var file in toDelete)
+ {
+ try
+ {
+ File.Delete(Path.Combine(BeatSaber.InstallPath, file));
+ }
+ catch (Exception e)
+ {
+ updater.Error("While trying to install pending updates: Error deleting file marked for deletion");
+ updater.Error(e);
+ }
+ }
+
+ try
+ {
+ LoneFunctions.CopyAll(new DirectoryInfo(pendingDir), new DirectoryInfo(BeatSaber.InstallPath));
+ }
+ catch (Exception e)
+ {
+ updater.Error("While trying to install pending updates: Error copying files in");
+ updater.Error(e);
+ }
+
+ Directory.Delete(pendingDir, true);
+ }
+ }
+ }
+}
diff --git a/IPA.Loader/Updating/ModsaberML/Updater.cs b/IPA.Loader/Updating/ModsaberML/Updater.cs
index a1ae5500..0ab9c9d3 100644
--- a/IPA.Loader/Updating/ModsaberML/Updater.cs
+++ b/IPA.Loader/Updating/ModsaberML/Updater.cs
@@ -286,7 +286,10 @@ namespace IPA.Updating.ModsaberML
continue;
}
- var ver = modsMatching.Value.Where(val => val.GameVersion == BeatSaber.GameVersion && val.Approved && !dep.Conflicts.IsSatisfied(val.Version)).Select(mod => mod.Version).Max(); // (2.1)
+ var ver = modsMatching.Value.Where(nullCheck => nullCheck != null)
+ .Where(versionCheck => versionCheck.GameVersion == BeatSaber.GameVersion && versionCheck.Approved)
+ .Where(conflictsCheck => dep.Conflicts == null || !dep.Conflicts.IsSatisfied(conflictsCheck.Version))
+ .Select(mod => mod.Version).Max(); // (2.1)
if (dep.Resolved = ver != null) dep.ResolvedVersion = ver; // (2.2)
dep.Has = dep.Version == dep.ResolvedVersion && dep.Resolved; // dep.Version is only not null if its already installed
}
@@ -470,11 +473,17 @@ namespace IPA.Updating.ModsaberML
throw new Exception("The hash for the file doesn't match what is defined");
var newFiles = new List();
- var backup = new BackupUnit(tempDirectory, $"backup-{item.Name}");
+
+ var targetDir = Path.Combine(BeatSaber.InstallPath, "IPA", Path.GetRandomFileName() + "_Pending");
+ Directory.CreateDirectory(targetDir);
+
+ var eventualOutput = Path.Combine(BeatSaber.InstallPath, "IPA", "Pending");
+ if (!Directory.Exists(eventualOutput))
+ Directory.CreateDirectory(eventualOutput);
try
{
- bool shouldDeleteOldFile = true;
+ bool shouldDeleteOldFile = !(item.LocalPluginMeta?.Plugin is SelfPlugin);
using (var zipFile = ZipFile.Read(stream))
{
@@ -484,7 +493,7 @@ namespace IPA.Updating.ModsaberML
if (entry.IsDirectory)
{
Logger.updater.Debug($"Creating directory {entry.FileName}");
- Directory.CreateDirectory(Path.Combine(Environment.CurrentDirectory, entry.FileName));
+ Directory.CreateDirectory(Path.Combine(targetDir, entry.FileName));
}
else
{
@@ -507,53 +516,60 @@ namespace IPA.Updating.ModsaberML
}
ostream.Seek(0, SeekOrigin.Begin);
- FileInfo targetFile = new FileInfo(Path.Combine(Environment.CurrentDirectory, entry.FileName));
+ FileInfo targetFile = new FileInfo(Path.Combine(targetDir, entry.FileName));
Directory.CreateDirectory(targetFile.DirectoryName);
- if (targetFile.FullName == item.LocalPluginMeta?.Filename)
+ if (LoneFunctions.GetRelativePath(targetFile.FullName, targetDir) == LoneFunctions.GetRelativePath(item.LocalPluginMeta?.Filename, BeatSaber.InstallPath))
shouldDeleteOldFile = false; // overwriting old file, no need to delete
- if (targetFile.Exists)
+ /*if (targetFile.Exists)
backup.Add(targetFile);
else
- newFiles.Add(targetFile);
+ newFiles.Add(targetFile);*/
Logger.updater.Debug($"Extracting file {targetFile.FullName}");
targetFile.Delete();
- var fstream = targetFile.Create();
- ostream.CopyTo(fstream);
+ using (var fstream = targetFile.Create())
+ ostream.CopyTo(fstream);
}
}
}
}
-
- if (item.LocalPluginMeta?.Plugin is SelfPlugin)
- { // currently updating self
- Process.Start(new ProcessStartInfo
- {
- FileName = item.LocalPluginMeta.Filename,
- Arguments = $"-nw={Process.GetCurrentProcess().Id}",
- UseShellExecute = false
- });
- }
- else if (shouldDeleteOldFile && item.LocalPluginMeta != null)
- File.Delete(item.LocalPluginMeta.Filename);
+
+ if (shouldDeleteOldFile && item.LocalPluginMeta != null)
+ File.AppendAllLines(Path.Combine(targetDir, _SpecialDeletionsFile), new string[] { LoneFunctions.GetRelativePath(item.LocalPluginMeta.Filename, BeatSaber.InstallPath) });
}
catch (Exception)
{ // something failed; restore
- foreach (var file in newFiles)
+ /*foreach (var file in newFiles)
file.Delete();
backup.Restore();
- backup.Delete();
+ backup.Delete();*/
+ Directory.Delete(targetDir, true); // delete extraction site
throw;
}
- backup.Delete();
+ if (item.LocalPluginMeta?.Plugin is SelfPlugin)
+ { // currently updating self, so copy to working dir and update
+ LoneFunctions.CopyAll(new DirectoryInfo(targetDir), new DirectoryInfo(BeatSaber.InstallPath));
+ if (File.Exists(Path.Combine(BeatSaber.InstallPath, _SpecialDeletionsFile))) File.Delete(Path.Combine(BeatSaber.InstallPath, _SpecialDeletionsFile));
+ Process.Start(new ProcessStartInfo
+ {
+ FileName = item.LocalPluginMeta.Filename,
+ Arguments = $"-nw={Process.GetCurrentProcess().Id}",
+ UseShellExecute = false
+ });
+ }
+ else
+ LoneFunctions.CopyAll(new DirectoryInfo(targetDir), new DirectoryInfo(eventualOutput), _SpecialDeletionsFile);
+ Directory.Delete(targetDir, true); // delete extraction site
Logger.updater.Debug("Extractor exited");
}
+
+ internal const string _SpecialDeletionsFile = "$$delete";
}
[Serializable]
diff --git a/IPA.Loader/Utilities/BeatSaber.cs b/IPA.Loader/Utilities/BeatSaber.cs
index f93ce0e6..e7f6ec28 100644
--- a/IPA.Loader/Utilities/BeatSaber.cs
+++ b/IPA.Loader/Utilities/BeatSaber.cs
@@ -40,10 +40,14 @@ namespace IPA.Utilities
///
public static Release ReleaseType => (_releaseCache ?? (_releaseCache = FindSteamVRAsset() ? Release.Steam : Release.Oculus)).Value;
+ ///
+ /// The path to the Beat Saber install dir
+ ///
+ public static string InstallPath => Environment.CurrentDirectory;
///
/// The path to the `Libs` folder. Use only if necessary.
///
- public static string LibraryPath => Path.Combine(Environment.CurrentDirectory, "Libs");
+ public static string LibraryPath => Path.Combine(InstallPath, "Libs");
///
/// The path to the `Libs\Native` folder. Use only if necessary.
///
diff --git a/IPA.Loader/Utilities/LoneFunctions.cs b/IPA.Loader/Utilities/LoneFunctions.cs
index 15dc9263..eb44920c 100644
--- a/IPA.Loader/Utilities/LoneFunctions.cs
+++ b/IPA.Loader/Utilities/LoneFunctions.cs
@@ -84,5 +84,42 @@ namespace IPA.Utilities
Uri folderUri = new Uri(folder);
return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar));
}
+
+ ///
+ /// Copies all files from to .
+ ///
+ /// the source directory
+ /// the destination directory
+ ///
+ public static void CopyAll(DirectoryInfo source, DirectoryInfo target, string appendFileName = "")
+ {
+ if (source.FullName.ToLower() == target.FullName.ToLower())
+ {
+ return;
+ }
+
+ // Check if the target directory exists, if not, create it.
+ if (Directory.Exists(target.FullName) == false)
+ {
+ Directory.CreateDirectory(target.FullName);
+ }
+
+ // Copy each file into it's new directory.
+ foreach (FileInfo fi in source.GetFiles())
+ {
+ if (fi.Name == appendFileName)
+ File.AppendAllLines(Path.Combine(target.ToString(), fi.Name), File.ReadAllLines(fi.FullName));
+ else
+ fi.CopyTo(Path.Combine(target.ToString(), fi.Name), true);
+ }
+
+ // Copy each subdirectory using recursion.
+ foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
+ {
+ DirectoryInfo nextTargetSubDir =
+ target.CreateSubdirectory(diSourceSubDir.Name);
+ CopyAll(diSourceSubDir, nextTargetSubDir, appendFileName);
+ }
+ }
}
}
diff --git a/Refs/UnityEngine.CoreModule.dll b/Refs/UnityEngine.CoreModule.dll
index 06b84914..1ef24630 100644
Binary files a/Refs/UnityEngine.CoreModule.dll and b/Refs/UnityEngine.CoreModule.dll differ