/// <summary> /// Adds a file to the list of changed files and backups it. /// </summary> /// <param name="path"></param> public void Add(FileInfo file) { var relativePath = LoneFunctions.GetRelativePath(Environment.CurrentDirectory, file.FullName); var backupPath = new FileInfo(Path.Combine(_BackupPath.FullName, relativePath)); if (_Files.Contains(relativePath)) { Console.WriteLine("Skipping backup of {0}", relativePath); return; } // Copy over backupPath.Directory.Create(); if (file.Exists) { file.CopyTo(backupPath.FullName, true); } else { // Make empty file backupPath.Create().Close(); } // Add to list _Files.Add(relativePath); }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (value == null) { writer.WriteNull(); } else { if (!(value is byte[])) { throw new JsonSerializationException("Expected byte[] object value"); } writer.WriteValue(LoneFunctions.ByteArrayToString(value as byte[])); } }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) { return(null); } if (reader.TokenType == JsonToken.String) { try { return(LoneFunctions.StringToByteArray((string)reader.Value)); } catch (Exception ex) { throw new Exception(string.Format("Error parsing version string: {0}", reader.Value), ex); } } throw new Exception(string.Format("Unexpected token or value when parsing hex string. Token: {0}, Value: {1}", reader.TokenType, reader.Value)); }
/// <summary> /// Adds a file to the list of changed files and backups it. /// </summary> /// <param name="file"></param> public void Add(FileInfo file) { var relativePath = LoneFunctions.GetRelativePath(file.FullName, Environment.CurrentDirectory); var backupPath = new FileInfo(Path.Combine(_backupPath.FullName, relativePath)); // Copy over backupPath.Directory?.Create(); if (file.Exists) { if (File.Exists(backupPath.FullName)) { File.Delete(backupPath.FullName); } file.CopyTo(backupPath.FullName); } else { // Make empty file backupPath.Create().Close(); } if (_files.Contains(relativePath)) { return; } if (!File.Exists(_manifestFile.FullName)) { _manifestFile.Create().Close(); } var stream = _manifestFile.AppendText(); stream.WriteLine(relativePath); stream.Close(); // Add to list _files.Add(relativePath); }
private static void LoadPlugins() { string pluginDirectory = Path.Combine(Environment.CurrentDirectory, "Plugins"); // Process.GetCurrentProcess().MainModule crashes the game and Assembly.GetEntryAssembly() is NULL, // so we need to resort to P/Invoke string exeName = Path.GetFileNameWithoutExtension(AppInfo.StartupPath); Logger.log.Info(exeName); _bsPlugins = new List <BSPluginMeta>(); _ipaPlugins = new List <IPlugin>(); if (!Directory.Exists(pluginDirectory)) { return; } string cacheDir = Path.Combine(pluginDirectory, ".cache"); if (!Directory.Exists(cacheDir)) { Directory.CreateDirectory(cacheDir); } else { foreach (string plugin in Directory.GetFiles(cacheDir, "*")) { File.Delete(plugin); } } //Copy plugins to .cache string[] originalPlugins = Directory.GetFiles(pluginDirectory, "*.dll"); foreach (string s in originalPlugins) { string pluginCopy = Path.Combine(cacheDir, Path.GetFileName(s)); File.Copy(Path.Combine(pluginDirectory, s), pluginCopy); } var selfPlugin = new BSPluginMeta { Filename = Path.Combine(Environment.CurrentDirectory, "IPA.exe"), Plugin = new SelfPlugin() }; selfPlugin.ModsaberInfo = selfPlugin.Plugin.ModInfo; _bsPlugins.Add(selfPlugin); //Load copied plugins string[] copiedPlugins = Directory.GetFiles(cacheDir, "*.dll"); foreach (string s in copiedPlugins) { var result = LoadPluginsFromFile(s, exeName); _bsPlugins.AddRange(result.Item1); _ipaPlugins.AddRange(result.Item2); } // DEBUG Logger.log.Info($"Running on Unity {UnityEngine.Application.unityVersion}"); Logger.log.Info($"Game version {UnityEngine.Application.version}"); Logger.log.Info("-----------------------------"); Logger.log.Info($"Loading plugins from {LoneFunctions.GetRelativePath(pluginDirectory, Environment.CurrentDirectory)} and found {_bsPlugins.Count + _ipaPlugins.Count}"); Logger.log.Info("-----------------------------"); foreach (var plugin in _bsPlugins) { Logger.log.Info($"{plugin.Plugin.Name}: {plugin.Plugin.Version}"); } Logger.log.Info("-----------------------------"); foreach (var plugin in _ipaPlugins) { Logger.log.Info($"{plugin.Name}: {plugin.Version}"); } Logger.log.Info("-----------------------------"); }
private void ExtractPluginAsync(MemoryStream stream, DependencyObject item, ApiEndpoint.Mod.PlatformFile fileInfo) { // (3.3) Logger.updater.Debug($"Extracting ZIP file for {item.Name}"); var data = stream.GetBuffer(); SHA1 sha = new SHA1CryptoServiceProvider(); var hash = sha.ComputeHash(data); if (!LoneFunctions.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"); Directory.CreateDirectory(targetDir); var eventualOutput = Path.Combine(BeatSaber.InstallPath, "IPA", "Pending"); if (!Directory.Exists(eventualOutput)) { Directory.CreateDirectory(eventualOutput); } try { bool shouldDeleteOldFile = !(item.LocalPluginMeta?.Plugin is SelfPlugin); using (var zipFile = ZipFile.Read(stream)) { Logger.updater.Debug("Streams opened"); foreach (var entry in zipFile) { if (entry.IsDirectory) { Logger.updater.Debug($"Creating directory {entry.FileName}"); Directory.CreateDirectory(Path.Combine(targetDir, entry.FileName)); } else { using (var ostream = new MemoryStream((int)entry.UncompressedSize)) { entry.Extract(ostream); ostream.Seek(0, SeekOrigin.Begin); sha = new SHA1CryptoServiceProvider(); var fileHash = sha.ComputeHash(ostream); try { if (!LoneFunctions.UnsafeCompare(fileHash, fileInfo.FileHashes[entry.FileName])) { throw new Exception("The hash for the file doesn't match what is defined"); } } catch (KeyNotFoundException) { throw new ModsaberInterceptException("ModSaber did not send the hashes for the zip's content!"); } ostream.Seek(0, SeekOrigin.Begin); FileInfo targetFile = new FileInfo(Path.Combine(targetDir, entry.FileName)); Directory.CreateDirectory(targetFile.DirectoryName ?? throw new InvalidOperationException()); 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) * backup.Add(targetFile); * else * newFiles.Add(targetFile);*/ Logger.updater.Debug($"Extracting file {targetFile.FullName}"); targetFile.Delete(); using (var fstream = targetFile.Create()) ostream.CopyTo(fstream); } } } } if (shouldDeleteOldFile && item.LocalPluginMeta != null) { File.AppendAllLines(Path.Combine(targetDir, SpecialDeletionsFile), new[] { LoneFunctions.GetRelativePath(item.LocalPluginMeta.Filename, BeatSaber.InstallPath) }); } } catch (Exception) { // something failed; restore /*foreach (var file in newFiles) * file.Delete(); * backup.Restore(); * backup.Delete();*/ Directory.Delete(targetDir, true); // delete extraction site throw; } 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"); }
private static void LoadPlugins() { string pluginDirectory = Path.Combine(Environment.CurrentDirectory, "Plugins"); // Process.GetCurrentProcess().MainModule crashes the game and Assembly.GetEntryAssembly() is NULL, // so we need to resort to P/Invoke string exeName = Path.GetFileNameWithoutExtension(AppInfo.StartupPath); _bsPlugins = new List <PluginInfo>(); _ipaPlugins = new List <IPlugin>(); if (!Directory.Exists(pluginDirectory)) { return; } string cacheDir = Path.Combine(pluginDirectory, ".cache"); if (!Directory.Exists(cacheDir)) { Directory.CreateDirectory(cacheDir); } else { foreach (string plugin in Directory.GetFiles(cacheDir, "*")) { File.Delete(plugin); } } //Copy plugins to .cache string[] originalPlugins = Directory.GetFiles(pluginDirectory, "*.dll"); foreach (string s in originalPlugins) { string pluginCopy = Path.Combine(cacheDir, Path.GetFileName(s)); File.Copy(Path.Combine(pluginDirectory, s), pluginCopy); #region Fix assemblies for refactor var module = ModuleDefinition.ReadModule(Path.Combine(pluginDirectory, s)); foreach (var @ref in module.AssemblyReferences) { // fix assembly references if (@ref.Name == "IllusionPlugin" || @ref.Name == "IllusionInjector") { @ref.Name = "IPA.Loader"; } } foreach (var @ref in module.GetTypeReferences()) { // fix type references if (@ref.FullName == "IllusionPlugin.IPlugin") { @ref.Namespace = "IPA.Old"; //@ref.Name = ""; } if (@ref.FullName == "IllusionPlugin.IEnhancedPlugin") { @ref.Namespace = "IPA.Old"; //@ref.Name = ""; } if (@ref.FullName == "IllusionPlugin.IBeatSaberPlugin") { @ref.Namespace = "IPA"; //@ref.Name = ""; } if (@ref.FullName == "IllusionPlugin.IEnhancedBeatSaberPlugin") { @ref.Namespace = "IPA"; //@ref.Name = ""; } if (@ref.FullName == "IllusionPlugin.BeatSaber.ModsaberModInfo") { @ref.Namespace = "IPA"; //@ref.Name = ""; } if (@ref.FullName == "IllusionPlugin.IniFile") { @ref.Namespace = "IPA.Config"; //@ref.Name = ""; } if (@ref.FullName == "IllusionPlugin.IModPrefs") { @ref.Namespace = "IPA.Config"; //@ref.Name = ""; } if (@ref.FullName == "IllusionPlugin.ModPrefs") { @ref.Namespace = "IPA.Config"; //@ref.Name = ""; } if (@ref.FullName == "IllusionPlugin.Utils.ReflectionUtil") { @ref.Namespace = "IPA.Utilities"; //@ref.Name = ""; } if (@ref.FullName == "IllusionPlugin.Logging.Logger") { @ref.Namespace = "IPA.Logging"; //@ref.Name = ""; } if (@ref.FullName == "IllusionPlugin.Logging.LogPrinter") { @ref.Namespace = "IPA.Logging"; //@ref.Name = ""; } if (@ref.FullName == "IllusionInjector.PluginManager") { @ref.Namespace = "IPA.Loader"; //@ref.Name = ""; } if (@ref.FullName == "IllusionInjector.PluginComponent") { @ref.Namespace = "IPA.Loader"; //@ref.Name = ""; } if (@ref.FullName == "IllusionInjector.CompositeBSPlugin") { @ref.Namespace = "IPA.Loader.Composite"; //@ref.Name = ""; } if (@ref.FullName == "IllusionInjector.CompositeIPAPlugin") { @ref.Namespace = "IPA.Loader.Composite"; //@ref.Name = ""; } if (@ref.FullName == "IllusionInjector.Logging.UnityLogInterceptor") { @ref.Namespace = "IPA.Logging"; //@ref.Name = ""; } if (@ref.FullName == "IllusionInjector.Logging.StandardLogger") { @ref.Namespace = "IPA.Logging"; //@ref.Name = ""; } if (@ref.FullName == "IllusionInjector.Updating.SelfPlugin") { @ref.Namespace = "IPA.Updating"; //@ref.Name = ""; } if (@ref.FullName == "IllusionInjector.Updating.Backup.BackupUnit") { @ref.Namespace = "IPA.Updating.Backup"; //@ref.Name = ""; } if (@ref.Namespace == "IllusionInjector.Utilities") { @ref.Namespace = "IPA.Utilities"; //@ref.Name = ""; } if (@ref.Namespace == "IllusionInjector.Logging.Printers") { @ref.Namespace = "IPA.Logging.Printers"; //@ref.Name = ""; } if (@ref.Namespace == "IllusionInjector.Updating.ModsaberML") { @ref.Namespace = "IPA.Updating.ModSaber"; //@ref.Name = ""; } } module.Write(pluginCopy); #endregion } var selfPlugin = new PluginInfo { Filename = Path.Combine(Environment.CurrentDirectory, "IPA.exe"), Plugin = SelfPlugin.Instance }; selfPlugin.ModSaberInfo = selfPlugin.Plugin.ModInfo; _bsPlugins.Add(selfPlugin); configProviders.Add(new KeyValuePair <IConfigProvider, Ref <DateTime> >(SelfConfigProvider = new JsonConfigProvider { Filename = Path.Combine("UserData", SelfPlugin.IPA_Name) }, new Ref <DateTime>(SelfConfigProvider.LastModified))); SelfConfigProvider.Load(); //Load copied plugins string[] copiedPlugins = Directory.GetFiles(cacheDir, "*.dll"); foreach (string s in copiedPlugins) { var result = LoadPluginsFromFile(s, exeName); _bsPlugins.AddRange(result.Item1); _ipaPlugins.AddRange(result.Item2); } Logger.log.Info(exeName); Logger.log.Info($"Running on Unity {Application.unityVersion}"); Logger.log.Info($"Game version {BeatSaber.GameVersion}"); Logger.log.Info("-----------------------------"); Logger.log.Info($"Loading plugins from {LoneFunctions.GetRelativePath(pluginDirectory, Environment.CurrentDirectory)} and found {_bsPlugins.Count + _ipaPlugins.Count}"); Logger.log.Info("-----------------------------"); foreach (var plugin in _bsPlugins) { Logger.log.Info($"{plugin.Plugin.Name}: {plugin.Plugin.Version}"); } Logger.log.Info("-----------------------------"); foreach (var plugin in _ipaPlugins) { Logger.log.Info($"{plugin.Name}: {plugin.Version}"); } Logger.log.Info("-----------------------------"); }
public override string ToString() { return($"{LoneFunctions.ByteArrayToString(Hash)}@{DownloadPath}({string.Join(",",FileHashes.Select(o=>$"\"{o.Key}\":\"{LoneFunctions.ByteArrayToString(o.Value)}\""))})"); }
private void ExtractPluginAsync(MemoryStream stream, UpdateStruct item, ApiEndpoint.Mod.PlatformFile fileInfo, string tempDirectory) { Logger.log.Debug($"Extracting ZIP file for {item.plugin.Plugin.Name}"); var data = stream.GetBuffer(); SHA1 sha = new SHA1CryptoServiceProvider(); var hash = sha.ComputeHash(data); if (!LoneFunctions.UnsafeCompare(hash, fileInfo.Hash)) { throw new Exception("The hash for the file doesn't match what is defined"); } var newFiles = new List <FileInfo>(); var backup = new BackupUnit(tempDirectory, $"backup-{item.plugin.ModsaberInfo.InternalName}"); try { bool shouldDeleteOldFile = true; using (var zipFile = ZipFile.Read(stream)) { Logger.log.Debug("Streams opened"); foreach (var entry in zipFile) { if (entry.IsDirectory) { Logger.log.Debug($"Creating directory {entry.FileName}"); Directory.CreateDirectory(Path.Combine(Environment.CurrentDirectory, entry.FileName)); } else { using (var ostream = new MemoryStream((int)entry.UncompressedSize)) { entry.Extract(ostream); ostream.Seek(0, SeekOrigin.Begin); sha = new SHA1CryptoServiceProvider(); var fileHash = sha.ComputeHash(ostream); if (!LoneFunctions.UnsafeCompare(fileHash, fileInfo.FileHashes[entry.FileName])) { throw new Exception("The hash for the file doesn't match what is defined"); } ostream.Seek(0, SeekOrigin.Begin); FileInfo targetFile = new FileInfo(Path.Combine(Environment.CurrentDirectory, entry.FileName)); Directory.CreateDirectory(targetFile.DirectoryName); if (targetFile.FullName == item.plugin.Filename) { shouldDeleteOldFile = false; // overwriting old file, no need to delete } if (targetFile.Exists) { backup.Add(targetFile); } else { newFiles.Add(targetFile); } Logger.log.Debug($"Extracting file {targetFile.FullName}"); var fstream = targetFile.Create(); ostream.CopyTo(fstream); } } } } if (item.plugin.Plugin is SelfPlugin) { // currently updating self Process.Start(new ProcessStartInfo { FileName = item.plugin.Filename, Arguments = $"--waitfor={Process.GetCurrentProcess().Id} --nowait", UseShellExecute = false }); } else if (shouldDeleteOldFile) { File.Delete(item.plugin.Filename); } } catch (Exception) { // something failed; restore foreach (var file in newFiles) { file.Delete(); } backup.Restore(); backup.Delete(); throw; } backup.Delete(); Logger.log.Debug("Downloader exited"); }
public static void InstallPendingUpdates() { 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 if (Directory.Exists(Path.Combine(pendingDir, "IPA"))) { Directory.Delete(Path.Combine(pendingDir, "IPA"), true); } if (File.Exists(Path.Combine(pendingDir, "IPA.exe"))) { File.Delete(Path.Combine(pendingDir, "IPA.exe")); if (File.Exists(Path.Combine(pendingDir, "Mono.Cecil.dll"))) { File.Delete(Path.Combine(pendingDir, "Mono.Cecil.dll")); } } #endregion 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); }