Browse Source

Updating now extracts to a 'Pending' directory and installs on the next game start

pull/46/head
Anairkoen Schno 6 years ago
parent
commit
65e89ed3fd
7 changed files with 144 additions and 34 deletions
  1. +1
    -0
      IPA.Injector/IPA.Injector.csproj
  2. +3
    -8
      IPA.Injector/Injector.cs
  3. +57
    -0
      IPA.Injector/Updates.cs
  4. +41
    -25
      IPA.Loader/Updating/ModsaberML/Updater.cs
  5. +5
    -1
      IPA.Loader/Utilities/BeatSaber.cs
  6. +37
    -0
      IPA.Loader/Utilities/LoneFunctions.cs
  7. BIN
      Refs/UnityEngine.CoreModule.dll

+ 1
- 0
IPA.Injector/IPA.Injector.csproj View File

@ -59,6 +59,7 @@
<Compile Include="Injector.cs" /> <Compile Include="Injector.cs" />
<Compile Include="LibLoader.cs" /> <Compile Include="LibLoader.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Updates.cs" />
<Compile Include="Virtualizer.cs" /> <Compile Include="Virtualizer.cs" />
<Compile Include="WtfThisDoesntNeedToExist.cs" /> <Compile Include="WtfThisDoesntNeedToExist.cs" />
</ItemGroup> </ItemGroup>


+ 3
- 8
IPA.Injector/Injector.cs View File

@ -11,7 +11,6 @@ using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using UnityEngine; using UnityEngine;
using static IPA.Logging.Logger; using static IPA.Logging.Logger;
using Logger = IPA.Logging.Logger;
using MethodAttributes = Mono.Cecil.MethodAttributes; using MethodAttributes = Mono.Cecil.MethodAttributes;
namespace IPA.Injector namespace IPA.Injector
@ -27,19 +26,15 @@ namespace IPA.Injector
try try
{ {
// This loads System.Runtime.InteropServices, and Microsoft.Win32.SafeHandles.
Windows.WinConsole.Initialize(); 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(); SetupLibraryLoading();
loader.Debug("Prepping bootstrapper"); loader.Debug("Prepping bootstrapper");
// // This will load Mono.Cecil
InstallBootstrapPatch(); InstallBootstrapPatch();
Updates.InstallPendingUpdates();
} }
catch (Exception e) catch (Exception e)
{ {


+ 57
- 0
IPA.Injector/Updates.cs View File

@ -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);
}
}
}
}

+ 41
- 25
IPA.Loader/Updating/ModsaberML/Updater.cs View File

@ -286,7 +286,10 @@ namespace IPA.Updating.ModsaberML
continue; 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) 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 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"); throw new Exception("The hash for the file doesn't match what is defined");
var newFiles = new List<FileInfo>(); var newFiles = new List<FileInfo>();
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 try
{ {
bool shouldDeleteOldFile = true;
bool shouldDeleteOldFile = !(item.LocalPluginMeta?.Plugin is SelfPlugin);
using (var zipFile = ZipFile.Read(stream)) using (var zipFile = ZipFile.Read(stream))
{ {
@ -484,7 +493,7 @@ namespace IPA.Updating.ModsaberML
if (entry.IsDirectory) if (entry.IsDirectory)
{ {
Logger.updater.Debug($"Creating directory {entry.FileName}"); Logger.updater.Debug($"Creating directory {entry.FileName}");
Directory.CreateDirectory(Path.Combine(Environment.CurrentDirectory, entry.FileName));
Directory.CreateDirectory(Path.Combine(targetDir, entry.FileName));
} }
else else
{ {
@ -507,53 +516,60 @@ namespace IPA.Updating.ModsaberML
} }
ostream.Seek(0, SeekOrigin.Begin); 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); 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 shouldDeleteOldFile = false; // overwriting old file, no need to delete
if (targetFile.Exists)
/*if (targetFile.Exists)
backup.Add(targetFile); backup.Add(targetFile);
else else
newFiles.Add(targetFile);
newFiles.Add(targetFile);*/
Logger.updater.Debug($"Extracting file {targetFile.FullName}"); Logger.updater.Debug($"Extracting file {targetFile.FullName}");
targetFile.Delete(); 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) catch (Exception)
{ // something failed; restore { // something failed; restore
foreach (var file in newFiles)
/*foreach (var file in newFiles)
file.Delete(); file.Delete();
backup.Restore(); backup.Restore();
backup.Delete();
backup.Delete();*/
Directory.Delete(targetDir, true); // delete extraction site
throw; 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"); Logger.updater.Debug("Extractor exited");
} }
internal const string _SpecialDeletionsFile = "$$delete";
} }
[Serializable] [Serializable]


+ 5
- 1
IPA.Loader/Utilities/BeatSaber.cs View File

@ -40,10 +40,14 @@ namespace IPA.Utilities
/// </summary> /// </summary>
public static Release ReleaseType => (_releaseCache ?? (_releaseCache = FindSteamVRAsset() ? Release.Steam : Release.Oculus)).Value; public static Release ReleaseType => (_releaseCache ?? (_releaseCache = FindSteamVRAsset() ? Release.Steam : Release.Oculus)).Value;
/// <summary>
/// The path to the Beat Saber install dir
/// </summary>
public static string InstallPath => Environment.CurrentDirectory;
/// <summary> /// <summary>
/// The path to the `Libs` folder. Use only if necessary. /// The path to the `Libs` folder. Use only if necessary.
/// </summary> /// </summary>
public static string LibraryPath => Path.Combine(Environment.CurrentDirectory, "Libs");
public static string LibraryPath => Path.Combine(InstallPath, "Libs");
/// <summary> /// <summary>
/// The path to the `Libs\Native` folder. Use only if necessary. /// The path to the `Libs\Native` folder. Use only if necessary.
/// </summary> /// </summary>


+ 37
- 0
IPA.Loader/Utilities/LoneFunctions.cs View File

@ -84,5 +84,42 @@ namespace IPA.Utilities
Uri folderUri = new Uri(folder); Uri folderUri = new Uri(folder);
return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar)); return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar));
} }
/// <summary>
/// Copies all files from <paramref name="source"/> to <paramref name="target"/>.
/// </summary>
/// <param name="source">the source directory</param>
/// <param name="target">the destination directory</param>
/// <param name="appendFileName"></param>
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);
}
}
} }
} }

BIN
Refs/UnityEngine.CoreModule.dll View File


Loading…
Cancel
Save