From a52cd5eaf211f0b3e7697f8527baa5ccce091abb Mon Sep 17 00:00:00 2001 From: Anairkoen Schno Date: Fri, 10 Jan 2020 21:21:35 -0600 Subject: [PATCH] Renamed BeatSaber class to UnityGame --- IPA.Injector/GameVersionEarly.cs | 140 ++++---- IPA.Injector/Injector.cs | 2 +- IPA.Injector/Updates.cs | 322 +++++++++--------- IPA.Loader/Config/Config.cs | 2 +- IPA.Loader/IPA.Loader.csproj | 2 +- IPA.Loader/Loader/PluginLoader.cs | 22 +- IPA.Loader/Loader/PluginManager.cs | 4 +- IPA.Loader/Loader/PluginMetadata.cs | 2 +- IPA.Loader/Updating/BeatMods/Updater.cs | 18 +- .../Utilities/{BeatSaber.cs => UnityGame.cs} | 7 +- Net3-Proxy/Utils.cs | 59 +++- 11 files changed, 317 insertions(+), 263 deletions(-) rename IPA.Loader/Utilities/{BeatSaber.cs => UnityGame.cs} (96%) diff --git a/IPA.Injector/GameVersionEarly.cs b/IPA.Injector/GameVersionEarly.cs index 97dc264f..f3e69a88 100644 --- a/IPA.Injector/GameVersionEarly.cs +++ b/IPA.Injector/GameVersionEarly.cs @@ -1,70 +1,70 @@ -using IPA.Utilities; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -#if NET3 -using Net3_Proxy; -using Path = Net3_Proxy.Path; -using File = Net3_Proxy.File; -using Directory = Net3_Proxy.Directory; -#endif - -namespace IPA.Injector -{ - internal static class GameVersionEarly - { - internal static string ResolveDataPath(string installDir) => - Directory.EnumerateDirectories(installDir, "*_Data").First(); - - internal static string GlobalGameManagers(string installDir) => - Path.Combine(ResolveDataPath(installDir), "globalgamemanagers"); - - internal static string GetGameVersion() - { - var mgr = GlobalGameManagers(BeatSaber.InstallPath); - - using (var stream = File.OpenRead(mgr)) - using (var reader = new BinaryReader(stream, Encoding.UTF8)) - { - const string key = "public.app-category.games"; - int pos = 0; - - while (stream.Position < stream.Length && pos < key.Length) - { - if (reader.ReadByte() == key[pos]) pos++; - else pos = 0; - } - - if (stream.Position == stream.Length) // we went through the entire stream without finding the key - throw new KeyNotFoundException("Could not find key '" + key + "' in " + mgr); - - // otherwise pos == key.Length, which means we found it - int offset = 136 - key.Length - sizeof(int); - stream.Seek(offset, SeekOrigin.Current); // advance past junk to beginning of string - - int strlen = reader.ReadInt32(); // assumes LE - var strbytes = reader.ReadBytes(strlen); - - return Encoding.UTF8.GetString(strbytes); - } - } - - internal static SemVer.Version SafeParseVersion() => new SemVer.Version(GetGameVersion(), true); - - private static void _Load() - { - BeatSaber.SetEarlyGameVersion(SafeParseVersion()); - BeatSaber.CheckGameVersionBoundary(); - } - - internal static void Load() - { - // This exists for the same reason the wierdness in Injector.Main does - _ = Type.GetType("SemVer.Version, SemVer", false); - - _Load(); - } - } -} +using IPA.Utilities; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +#if NET3 +using Net3_Proxy; +using Path = Net3_Proxy.Path; +using File = Net3_Proxy.File; +using Directory = Net3_Proxy.Directory; +#endif + +namespace IPA.Injector +{ + internal static class GameVersionEarly + { + internal static string ResolveDataPath(string installDir) => + Directory.EnumerateDirectories(installDir, "*_Data").First(); + + internal static string GlobalGameManagers(string installDir) => + Path.Combine(ResolveDataPath(installDir), "globalgamemanagers"); + + internal static string GetGameVersion() + { + var mgr = GlobalGameManagers(UnityGame.InstallPath); + + using (var stream = File.OpenRead(mgr)) + using (var reader = new BinaryReader(stream, Encoding.UTF8)) + { + const string key = "public.app-category.games"; + int pos = 0; + + while (stream.Position < stream.Length && pos < key.Length) + { + if (reader.ReadByte() == key[pos]) pos++; + else pos = 0; + } + + if (stream.Position == stream.Length) // we went through the entire stream without finding the key + throw new KeyNotFoundException("Could not find key '" + key + "' in " + mgr); + + // otherwise pos == key.Length, which means we found it + int offset = 136 - key.Length - sizeof(int); + stream.Seek(offset, SeekOrigin.Current); // advance past junk to beginning of string + + int strlen = reader.ReadInt32(); // assumes LE + var strbytes = reader.ReadBytes(strlen); + + return Encoding.UTF8.GetString(strbytes); + } + } + + internal static SemVer.Version SafeParseVersion() => new SemVer.Version(GetGameVersion(), true); + + private static void _Load() + { + UnityGame.SetEarlyGameVersion(SafeParseVersion()); + UnityGame.CheckGameVersionBoundary(); + } + + internal static void Load() + { + // This exists for the same reason the wierdness in Injector.Main does + _ = Type.GetType("SemVer.Version, SemVer", false); + + _Load(); + } + } +} diff --git a/IPA.Injector/Injector.cs b/IPA.Injector/Injector.cs index 1d13abe4..f453492e 100644 --- a/IPA.Injector/Injector.cs +++ b/IPA.Injector/Injector.cs @@ -328,7 +328,7 @@ namespace IPA.Injector pluginAsyncLoadTask.Wait(); permissionFixTask.Wait(); - BeatSaber.EnsureRuntimeGameVersion(); + UnityGame.EnsureRuntimeGameVersion(); log.Debug("Plugins loaded"); log.Debug(string.Join(", ", PluginLoader.PluginsMetadata.StrJP())); diff --git a/IPA.Injector/Updates.cs b/IPA.Injector/Updates.cs index 13a71250..d66efedd 100644 --- a/IPA.Injector/Updates.cs +++ b/IPA.Injector/Updates.cs @@ -1,161 +1,161 @@ -using IPA.Utilities; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using static IPA.Logging.Logger; -#if NET3 -using Net3_Proxy; -using Path = Net3_Proxy.Path; -using File = Net3_Proxy.File; -using Directory = Net3_Proxy.Directory; -#endif - -namespace IPA.Injector -{ - internal static class Updates - { - private const string DeleteFileName = Updating.BeatMods.Updater.SpecialDeletionsFile; - - public static void InstallPendingUpdates() - { - InstallPendingSelfUpdates(); - InstallPendingModUpdates(); - } - - private static void InstallPendingSelfUpdates() - { - var path = Path.Combine(BeatSaber.InstallPath, "IPA.exe"); - if (!File.Exists(path)) return; - - var ipaVersion = new Version(FileVersionInfo.GetVersionInfo(path).FileVersion); - var selfVersion = Assembly.GetExecutingAssembly().GetName().Version; - - if (ipaVersion > selfVersion) - { - Process.Start(new ProcessStartInfo - { - FileName = path, - Arguments = $"\"-nw={Process.GetCurrentProcess().Id},s={string.Join(" ", Environment.GetCommandLineArgs().Skip(1).StrJP()).Replace("\\", "\\\\").Replace(",", "\\,")}\"", - UseShellExecute = false - }); - - updater.Info("Updating BSIPA..."); - Environment.Exit(0); - } - } - - private static void InstallPendingModUpdates() - { - var pendingDir = Path.Combine(BeatSaber.InstallPath, "IPA", "Pending"); - if (!Directory.Exists(pendingDir)) return; - - // 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); - } - } - - #region Self Protection - - string path; - if (Directory.Exists(path = Path.Combine(pendingDir, "IPA"))) - { - var dirs = new Stack(20); - - dirs.Push(path); - - while (dirs.Count > 0) - { - var currentDir = dirs.Pop(); - string[] subDirs; - string[] files; - try - { - subDirs = Directory.GetDirectories(currentDir); - files = Directory.GetFiles(currentDir); - } - catch (UnauthorizedAccessException e) - { - updater.Error(e); - continue; - } - catch (DirectoryNotFoundException e) - { - updater.Error(e); - continue; - } - - foreach (var file in files) - { - try - { - if (!Utils.GetRelativePath(file, path).Split(Path.PathSeparator).Contains("Pending")) - File.Delete(file); - } - catch (FileNotFoundException e) - { - updater.Error(e); - } - } - - foreach (var str in subDirs) - dirs.Push(str); - } - } - if (File.Exists(path = Path.Combine(pendingDir, "IPA.exe"))) - { - File.Delete(path); - if (File.Exists(path = Path.Combine(pendingDir, "Mono.Cecil.dll"))) - File.Delete(path); - } - - #endregion - - try - { - Utils.CopyAll(new DirectoryInfo(pendingDir), new DirectoryInfo(BeatSaber.InstallPath), onCopyException: (e, f) => - { - updater.Error($"Error copying file {Utils.GetRelativePath(f.FullName, pendingDir)} from Pending:"); - updater.Error(e); - return true; - }); - } - catch (Exception e) - { - updater.Error("While trying to install pending updates: Error copying files in"); - updater.Error(e); - } - - try - { - Directory.Delete(pendingDir, true); - } - catch (Exception e) - { - updater.Error("Something went wrong performing an operation that should never fail!"); - updater.Error(e); - } - } - } -} +using IPA.Utilities; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using static IPA.Logging.Logger; +#if NET3 +using Net3_Proxy; +using Path = Net3_Proxy.Path; +using File = Net3_Proxy.File; +using Directory = Net3_Proxy.Directory; +#endif + +namespace IPA.Injector +{ + internal static class Updates + { + private const string DeleteFileName = Updating.BeatMods.Updater.SpecialDeletionsFile; + + public static void InstallPendingUpdates() + { + InstallPendingSelfUpdates(); + InstallPendingModUpdates(); + } + + private static void InstallPendingSelfUpdates() + { + var path = Path.Combine(UnityGame.InstallPath, "IPA.exe"); + if (!File.Exists(path)) return; + + var ipaVersion = new Version(FileVersionInfo.GetVersionInfo(path).FileVersion); + var selfVersion = Assembly.GetExecutingAssembly().GetName().Version; + + if (ipaVersion > selfVersion) + { + Process.Start(new ProcessStartInfo + { + FileName = path, + Arguments = $"\"-nw={Process.GetCurrentProcess().Id},s={string.Join(" ", Environment.GetCommandLineArgs().Skip(1).StrJP()).Replace("\\", "\\\\").Replace(",", "\\,")}\"", + UseShellExecute = false + }); + + updater.Info("Updating BSIPA..."); + Environment.Exit(0); + } + } + + private static void InstallPendingModUpdates() + { + var pendingDir = Path.Combine(UnityGame.InstallPath, "IPA", "Pending"); + if (!Directory.Exists(pendingDir)) return; + + // 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(UnityGame.InstallPath, file)); + } + catch (Exception e) + { + updater.Error("While trying to install pending updates: Error deleting file marked for deletion"); + updater.Error(e); + } + } + + #region Self Protection + + string path; + if (Directory.Exists(path = Path.Combine(pendingDir, "IPA"))) + { + var dirs = new Stack(20); + + dirs.Push(path); + + while (dirs.Count > 0) + { + var currentDir = dirs.Pop(); + string[] subDirs; + string[] files; + try + { + subDirs = Directory.GetDirectories(currentDir); + files = Directory.GetFiles(currentDir); + } + catch (UnauthorizedAccessException e) + { + updater.Error(e); + continue; + } + catch (DirectoryNotFoundException e) + { + updater.Error(e); + continue; + } + + foreach (var file in files) + { + try + { + if (!Utils.GetRelativePath(file, path).Split(Path.PathSeparator).Contains("Pending")) + File.Delete(file); + } + catch (FileNotFoundException e) + { + updater.Error(e); + } + } + + foreach (var str in subDirs) + dirs.Push(str); + } + } + if (File.Exists(path = Path.Combine(pendingDir, "IPA.exe"))) + { + File.Delete(path); + if (File.Exists(path = Path.Combine(pendingDir, "Mono.Cecil.dll"))) + File.Delete(path); + } + + #endregion + + try + { + Utils.CopyAll(new DirectoryInfo(pendingDir), new DirectoryInfo(UnityGame.InstallPath), onCopyException: (e, f) => + { + updater.Error($"Error copying file {Utils.GetRelativePath(f.FullName, pendingDir)} from Pending:"); + updater.Error(e); + return true; + }); + } + catch (Exception e) + { + updater.Error("While trying to install pending updates: Error copying files in"); + updater.Error(e); + } + + try + { + Directory.Delete(pendingDir, true); + } + catch (Exception e) + { + updater.Error("Something went wrong performing an operation that should never fail!"); + updater.Error(e); + } + } + } +} diff --git a/IPA.Loader/Config/Config.cs b/IPA.Loader/Config/Config.cs index ecb77848..0e4c19f1 100644 --- a/IPA.Loader/Config/Config.cs +++ b/IPA.Loader/Config/Config.cs @@ -110,7 +110,7 @@ namespace IPA.Config var chosenExt = extensions.FirstOrDefault(s => registeredProviders.ContainsKey(s)) ?? "json"; var provider = registeredProviders[chosenExt]; - var filename = Path.Combine(BeatSaber.UserDataPath, configName + "." + provider.Extension); + var filename = Path.Combine(UnityGame.UserDataPath, configName + "." + provider.Extension); var config = new Config(configName, provider, new FileInfo(filename)); ConfigRuntime.RegisterConfig(config); diff --git a/IPA.Loader/IPA.Loader.csproj b/IPA.Loader/IPA.Loader.csproj index fabcff13..a0437ca5 100644 --- a/IPA.Loader/IPA.Loader.csproj +++ b/IPA.Loader/IPA.Loader.csproj @@ -137,7 +137,7 @@ - + diff --git a/IPA.Loader/Loader/PluginLoader.cs b/IPA.Loader/Loader/PluginLoader.cs index 5abbcd7f..8a128d10 100644 --- a/IPA.Loader/Loader/PluginLoader.cs +++ b/IPA.Loader/Loader/PluginLoader.cs @@ -46,12 +46,12 @@ namespace IPA.Loader internal static void YeetIfNeeded() { - string pluginDir = BeatSaber.PluginsPath; + string pluginDir = UnityGame.PluginsPath; - if (SelfConfig.YeetMods_ && BeatSaber.IsGameVersionBoundary) + if (SelfConfig.YeetMods_ && UnityGame.IsGameVersionBoundary) { - var oldPluginsName = Path.Combine(BeatSaber.InstallPath, $"Old {BeatSaber.OldVersion} Plugins"); - var newPluginsName = Path.Combine(BeatSaber.InstallPath, $"Old {BeatSaber.GameVersion} Plugins"); + var oldPluginsName = Path.Combine(UnityGame.InstallPath, $"Old {UnityGame.OldVersion} Plugins"); + var newPluginsName = Path.Combine(UnityGame.InstallPath, $"Old {UnityGame.GameVersion} Plugins"); if (Directory.Exists(oldPluginsName)) Directory.Delete(oldPluginsName, true); @@ -70,14 +70,14 @@ namespace IPA.Loader internal static void LoadMetadata() { - string[] plugins = Directory.GetFiles(BeatSaber.PluginsPath, "*.dll"); + string[] plugins = Directory.GetFiles(UnityGame.PluginsPath, "*.dll"); try { var selfMeta = new PluginMetadata { Assembly = Assembly.GetExecutingAssembly(), - File = new FileInfo(Path.Combine(BeatSaber.InstallPath, "IPA.exe")), + File = new FileInfo(Path.Combine(UnityGame.InstallPath, "IPA.exe")), PluginType = null, IsSelf = true }; @@ -103,7 +103,7 @@ namespace IPA.Loader { var metadata = new PluginMetadata { - File = new FileInfo(Path.Combine(BeatSaber.PluginsPath, plugin)), + File = new FileInfo(Path.Combine(UnityGame.PluginsPath, plugin)), IsSelf = false }; @@ -218,15 +218,15 @@ namespace IPA.Loader } } - IEnumerable bareManifests = Directory.GetFiles(BeatSaber.PluginsPath, "*.json"); - bareManifests = bareManifests.Concat(Directory.GetFiles(BeatSaber.PluginsPath, "*.manifest")); + IEnumerable bareManifests = Directory.GetFiles(UnityGame.PluginsPath, "*.json"); + bareManifests = bareManifests.Concat(Directory.GetFiles(UnityGame.PluginsPath, "*.manifest")); foreach (var manifest in bareManifests) { // TODO: maybe find a way to allow a bare manifest to specify an associated file try { var metadata = new PluginMetadata { - File = new FileInfo(Path.Combine(BeatSaber.PluginsPath, manifest)), + File = new FileInfo(Path.Combine(UnityGame.PluginsPath, manifest)), IsSelf = false, IsBare = true, }; @@ -601,7 +601,7 @@ namespace IPA.Loader internal static PluginExecutor InitPlugin(PluginMetadata meta, IEnumerable alreadyLoaded) { - if (meta.Manifest.GameVersion != BeatSaber.GameVersion) + if (meta.Manifest.GameVersion != UnityGame.GameVersion) Logger.loader.Warn($"Mod {meta.Name} developed for game version {meta.Manifest.GameVersion}, so it may not work properly."); if (!meta.IsAttributePlugin) diff --git a/IPA.Loader/Loader/PluginManager.cs b/IPA.Loader/Loader/PluginManager.cs index 91525b86..0ccdc406 100644 --- a/IPA.Loader/Loader/PluginManager.cs +++ b/IPA.Loader/Loader/PluginManager.cs @@ -283,7 +283,7 @@ namespace IPA.Loader internal static void Load() { - string pluginDirectory = BeatSaber.PluginsPath; + string pluginDirectory = UnityGame.PluginsPath; // Process.GetCurrentProcess().MainModule crashes the game and Assembly.GetEntryAssembly() is NULL, // so we need to resort to P/Invoke @@ -373,7 +373,7 @@ namespace IPA.Loader Logger.log.Info(exeName); Logger.log.Info($"Running on Unity {Application.unityVersion}"); - Logger.log.Info($"Game version {BeatSaber.GameVersion}"); + Logger.log.Info($"Game version {UnityGame.GameVersion}"); Logger.log.Info("-----------------------------"); Logger.log.Info($"Loading plugins from {Utils.GetRelativePath(pluginDirectory, Environment.CurrentDirectory)} and found {_bsPlugins.Count + _ipaPlugins.Count}"); Logger.log.Info("-----------------------------"); diff --git a/IPA.Loader/Loader/PluginMetadata.cs b/IPA.Loader/Loader/PluginMetadata.cs index 1f6deacd..930a8333 100644 --- a/IPA.Loader/Loader/PluginMetadata.cs +++ b/IPA.Loader/Loader/PluginMetadata.cs @@ -99,6 +99,6 @@ namespace IPA.Loader /// Gets all of the metadata as a readable string. /// /// the readable printable metadata string - public override string ToString() => $"{Name}({Id}@{Version})({PluginType?.FullName}) from '{Utils.GetRelativePath(File?.FullName, BeatSaber.InstallPath)}'"; + public override string ToString() => $"{Name}({Id}@{Version})({PluginType?.FullName}) from '{Utils.GetRelativePath(File?.FullName, UnityGame.InstallPath)}'"; } } \ No newline at end of file diff --git a/IPA.Loader/Updating/BeatMods/Updater.cs b/IPA.Loader/Updating/BeatMods/Updater.cs index 504f7b06..37909668 100644 --- a/IPA.Loader/Updating/BeatMods/Updater.cs +++ b/IPA.Loader/Updating/BeatMods/Updater.cs @@ -388,7 +388,7 @@ namespace IPA.Updating.BeatMods var ver = modsMatching.Value .NonNull() // entry is not null - .Where(versionCheck => versionCheck.GameVersion == BeatSaber.GameVersion) // game version matches + .Where(versionCheck => versionCheck.GameVersion == UnityGame.GameVersion) // game version matches .Where(approvalCheck => approvalCheck.Status == ApiEndpoint.Mod.ApprovedStatus) // version approved // TODO: fix; it seems wrong somehow .Where(conflictsCheck => dep.Conflicts == null || !dep.Conflicts.IsSatisfied(conflictsCheck.Version)) // not a conflicting version @@ -458,7 +458,7 @@ namespace IPA.Updating.BeatMods DownloadProgress progress, DownloadFailed dlFail, DownloadFinish finish, InstallFailed installFail, InstallFinish installFinish) { // (3.2) - Logger.updater.Debug($"Release: {BeatSaber.ReleaseType}"); + Logger.updater.Debug($"Release: {UnityGame.ReleaseType}"); var mod = new Ref(null); yield return GetModInfo(item.Name, item.ResolvedVersion.ToString(), mod); @@ -471,7 +471,7 @@ namespace IPA.Updating.BeatMods yield break; } - var releaseName = BeatSaber.ReleaseType == BeatSaber.Release.Steam + var releaseName = UnityGame.ReleaseType == UnityGame.Release.Steam ? ApiEndpoint.Mod.DownloadsObject.TypeSteam : ApiEndpoint.Mod.DownloadsObject.TypeOculus; var platformFile = mod.Value.Downloads.First(f => f.Type == ApiEndpoint.Mod.DownloadsObject.TypeUniversal || f.Type == releaseName); @@ -628,10 +628,10 @@ namespace IPA.Updating.BeatMods if (!Utils.UnsafeCompare(hash, fileInfo.Hash)) throw new Exception("The hash for the file doesn't match what is defined");*/ - var targetDir = Path.Combine(BeatSaber.InstallPath, "IPA", Path.GetRandomFileName() + "_Pending"); + var targetDir = Path.Combine(UnityGame.InstallPath, "IPA", Path.GetRandomFileName() + "_Pending"); Directory.CreateDirectory(targetDir); - var eventualOutput = Path.Combine(BeatSaber.InstallPath, "IPA", "Pending"); + var eventualOutput = Path.Combine(UnityGame.InstallPath, "IPA", "Pending"); if (!Directory.Exists(eventualOutput)) Directory.CreateDirectory(eventualOutput); @@ -674,7 +674,7 @@ namespace IPA.Updating.BeatMods Directory.CreateDirectory(targetFile.DirectoryName ?? throw new InvalidOperationException()); if (item.LocalPluginMeta != null && - Utils.GetRelativePath(targetFile.FullName, targetDir) == Utils.GetRelativePath(item.LocalPluginMeta?.File.FullName, BeatSaber.InstallPath)) + Utils.GetRelativePath(targetFile.FullName, targetDir) == Utils.GetRelativePath(item.LocalPluginMeta?.File.FullName, UnityGame.InstallPath)) shouldDeleteOldFile = false; // overwriting old file, no need to delete /*if (targetFile.Exists) @@ -693,7 +693,7 @@ namespace IPA.Updating.BeatMods } if (shouldDeleteOldFile && item.LocalPluginMeta != null) - File.AppendAllLines(Path.Combine(targetDir, SpecialDeletionsFile), new[] { Utils.GetRelativePath(item.LocalPluginMeta?.File.FullName, BeatSaber.InstallPath) }); + File.AppendAllLines(Path.Combine(targetDir, SpecialDeletionsFile), new[] { Utils.GetRelativePath(item.LocalPluginMeta?.File.FullName, UnityGame.InstallPath) }); } catch (Exception) { // something failed; restore @@ -705,8 +705,8 @@ namespace IPA.Updating.BeatMods if ((item.LocalPluginMeta?.IsSelf).Unwrap()) { // currently updating self, so copy to working dir and update NeedsManualRestart = true; // flag so that ModList keeps the restart button hidden - Utils.CopyAll(new DirectoryInfo(targetDir), new DirectoryInfo(BeatSaber.InstallPath)); - var deleteFile = Path.Combine(BeatSaber.InstallPath, SpecialDeletionsFile); + Utils.CopyAll(new DirectoryInfo(targetDir), new DirectoryInfo(UnityGame.InstallPath)); + var deleteFile = Path.Combine(UnityGame.InstallPath, SpecialDeletionsFile); if (File.Exists(deleteFile)) File.Delete(deleteFile); Process.Start(new ProcessStartInfo { diff --git a/IPA.Loader/Utilities/BeatSaber.cs b/IPA.Loader/Utilities/UnityGame.cs similarity index 96% rename from IPA.Loader/Utilities/BeatSaber.cs rename to IPA.Loader/Utilities/UnityGame.cs index 25a4a81a..ab9f4267 100644 --- a/IPA.Loader/Utilities/BeatSaber.cs +++ b/IPA.Loader/Utilities/UnityGame.cs @@ -12,7 +12,7 @@ namespace IPA.Utilities /// /// Provides some basic utility methods and properties of Beat Saber /// - public static class BeatSaber + public static class UnityGame { private static AlmostVersion _gameVersion; /// @@ -77,6 +77,9 @@ namespace IPA.Utilities /// /// Gets the type of this installation of Beat Saber /// + /// + /// This only gives a + /// /// the type of release this is public static Release ReleaseType => (_releaseCache ?? (_releaseCache = FindSteamVRAsset() ? Release.Steam : Release.Oculus)).Value; @@ -118,7 +121,7 @@ namespace IPA.Utilities private static bool FindSteamVRAsset() { - // these require assembly qualified names.... + // TODO: fix this so that it works more consistently and generally var steamUser = Type.GetType("Steamworks.SteamUser, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", false); return steamUser != null; diff --git a/Net3-Proxy/Utils.cs b/Net3-Proxy/Utils.cs index c8869a2c..10be31b0 100644 --- a/Net3-Proxy/Utils.cs +++ b/Net3-Proxy/Utils.cs @@ -23,6 +23,14 @@ namespace Net3_Proxy } return true; } + + /// + /// Adds a value to the beginning of the sequence. + /// + /// the type of the elements of + /// a sequence of values + /// the value to prepend to + /// a new sequence beginning with public static IEnumerable Prepend(this IEnumerable seq, T prep) => new PrependEnumerable(seq, prep); @@ -37,10 +45,53 @@ namespace Net3_Proxy this.first = first; } - public IEnumerator GetEnumerator() - { // TODO: a custom impl that is less garbage - yield return first; - foreach (var v in rest) yield return v; + public IEnumerator GetEnumerator() => new PrependEnumerator(this); + + private sealed class PrependEnumerator : IEnumerator + { + private readonly IEnumerator restEnum; + private readonly PrependEnumerable enumerable; + private int state = 0; + public PrependEnumerator(PrependEnumerable enumerable) + { + this.enumerable = enumerable; + restEnum = enumerable.rest.GetEnumerator(); + } + + public T Current { get; private set; } + + object IEnumerator.Current => Current; + + public void Dispose() => restEnum.Dispose(); + + public bool MoveNext() + { + switch (state) + { + case 0: + Current = enumerable.first; + state++; + return true; + case 1: + if (!restEnum.MoveNext()) + { + state = 2; + return false; + } + else + Current = restEnum.Current; + return true; + case 2: + default: + return false; + } + } + + public void Reset() + { + restEnum.Reset(); + state = 0; + } } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();