Browse Source

Updated to use ReSharper

pull/46/head
Anairkoen Schno 5 years ago
parent
commit
6045e31267
64 changed files with 641 additions and 1029 deletions
  1. +0
    -24
      BSIPA.sln
  2. +12
    -0
      BSIPA.sln.DotSettings
  3. +7
    -8
      CollectDependencies/Program.cs
  4. +0
    -1
      CollectDependencies/Properties/AssemblyInfo.cs
  5. +14
    -18
      CollectDependencies/Virtualizer.cs
  6. +2
    -6
      IPA.Injector/Backups/BackupManager.cs
  7. +26
    -32
      IPA.Injector/Backups/BackupUnit.cs
  8. +8
    -8
      IPA.Injector/Bootstrapper.cs
  9. +23
    -25
      IPA.Injector/ConsoleWindow.cs
  10. +0
    -1
      IPA.Injector/IPA.Injector.csproj
  11. +34
    -31
      IPA.Injector/Injector.cs
  12. +22
    -27
      IPA.Injector/LibLoader.cs
  13. +3
    -5
      IPA.Injector/Properties/AssemblyInfo.cs
  14. +2
    -6
      IPA.Injector/Updates.cs
  15. +4
    -10
      IPA.Injector/Virtualizer.cs
  16. +0
    -21
      IPA.Injector/WtfThisDoesntNeedToExist.cs
  17. +16
    -18
      IPA.Loader/Config/ConfigProviders/JsonConfigProvider.cs
  18. +1
    -4
      IPA.Loader/Config/IConfigProvider.cs
  19. +12
    -14
      IPA.Loader/Config/IniFile.cs
  20. +28
    -35
      IPA.Loader/Config/ModPrefs.cs
  21. +4
    -6
      IPA.Loader/IPA.Loader.csproj
  22. +4
    -10
      IPA.Loader/Loader/Composite/CompositeBSPlugin.cs
  23. +7
    -16
      IPA.Loader/Loader/Composite/CompositeIPAPlugin.cs
  24. +7
    -8
      IPA.Loader/Loader/PluginComponent.cs
  25. +29
    -31
      IPA.Loader/Loader/PluginManager.cs
  26. +0
    -4
      IPA.Loader/Logging/LogPrinter.cs
  27. +2
    -5
      IPA.Loader/Logging/Logger.cs
  28. +2
    -8
      IPA.Loader/Logging/Printers/ColoredConsolePrinter.cs
  29. +12
    -16
      IPA.Loader/Logging/Printers/GZFilePrinter.cs
  30. +2
    -7
      IPA.Loader/Logging/Printers/GlobalLogFilePrinter.cs
  31. +3
    -8
      IPA.Loader/Logging/Printers/PluginLogFilePrinter.cs
  32. +3
    -8
      IPA.Loader/Logging/Printers/PluginSubLogPrinter.cs
  33. +25
    -29
      IPA.Loader/Logging/StandardLogger.cs
  34. +4
    -16
      IPA.Loader/Logging/UnityLogInterceptor.cs
  35. +3
    -5
      IPA.Loader/PluginInterfaces/BeatSaber/IBeatSaberPlugin.cs
  36. +2
    -4
      IPA.Loader/PluginInterfaces/BeatSaber/IEnhancedBeatSaberPlugin.cs
  37. +10
    -12
      IPA.Loader/PluginInterfaces/BeatSaber/ModsaberModInfo.cs
  38. +2
    -7
      IPA.Loader/PluginInterfaces/IGenericEnhancedPlugin.cs
  39. +2
    -2
      IPA.Loader/PluginInterfaces/IPA/IEnhancedPlugin.cs
  40. +1
    -2
      IPA.Loader/PluginInterfaces/IPA/IPlugin.cs
  41. +0
    -147
      IPA.Loader/Updating/Backup/BackupUnit.cs
  42. +9
    -13
      IPA.Loader/Updating/Converters/ModsaberDependencyConverter.cs
  43. +2
    -4
      IPA.Loader/Updating/Converters/SemverRangeConverter.cs
  44. +2
    -7
      IPA.Loader/Updating/Converters/SemverVersionConverter.cs
  45. +10
    -15
      IPA.Loader/Updating/ModSaber/ApiEndpoint.cs
  46. +65
    -73
      IPA.Loader/Updating/ModSaber/Updater.cs
  47. +2
    -8
      IPA.Loader/Updating/SelfPlugin.cs
  48. +8
    -12
      IPA.Loader/Utilities/BeatSaber.cs
  49. +1
    -9
      IPA.Loader/Utilities/Extensions.cs
  50. +4
    -7
      IPA.Loader/Utilities/LoneFunctions.cs
  51. +6
    -10
      IPA.Loader/Utilities/Ref.cs
  52. +10
    -19
      IPA.Loader/Utilities/ReflectionUtil.cs
  53. +34
    -39
      IPA/Arguments.cs
  54. +4
    -7
      IPA/PatchContext.cs
  55. +7
    -12
      IPA/Patcher/BackupManager.cs
  56. +28
    -31
      IPA/Patcher/BackupUnit.cs
  57. +32
    -27
      IPA/Patcher/Patcher.cs
  58. +11
    -15
      IPA/Patcher/Virtualizer.cs
  59. +50
    -50
      IPA/Program.cs
  60. +2
    -3
      IPA/Properties/AssemblyInfo.cs
  61. +8
    -7
      IPA/Shortcut.cs
  62. +6
    -13
      MSBuildTasks/AssemblyRenameTask.cs
  63. +0
    -1
      MSBuildTasks/Properties/AssemblyInfo.cs
  64. +2
    -2
      appveyor.yml

+ 0
- 24
BSIPA.sln View File

@ -8,8 +8,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IPA", "IPA\IPA.csproj", "{1
{2A1AF16B-27F1-46E0-9A95-181516BC1CB7} = {2A1AF16B-27F1-46E0-9A95-181516BC1CB7}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IPA.Tests", "IPA.Tests\IPA.Tests.csproj", "{C66092B0-5C1E-44E9-B524-E0E8E1425379}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSBuildTasks", "MSBuildTasks\MSBuildTasks.csproj", "{F08C3C7A-3221-432E-BAB8-32BCE58408C8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IPA.Loader", "IPA.Loader\IPA.Loader.csproj", "{5AD344F0-01A0-4CA8-92E5-9D095737744D}"
@ -79,28 +77,6 @@ Global
{14092533-98BB-40A4-9AFC-27BB75672A70}.Verbose|x64.Build.0 = Verbose|Any CPU
{14092533-98BB-40A4-9AFC-27BB75672A70}.Verbose|x86.ActiveCfg = Release|Any CPU
{14092533-98BB-40A4-9AFC-27BB75672A70}.Verbose|x86.Build.0 = Release|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Debug|x64.ActiveCfg = Debug|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Debug|x64.Build.0 = Debug|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Debug|x86.ActiveCfg = Debug|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Debug|x86.Build.0 = Debug|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Release|Any CPU.Build.0 = Release|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Release|x64.ActiveCfg = Release|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Release|x86.ActiveCfg = Release|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Release|x86.Build.0 = Release|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Verbose_Release|Any CPU.ActiveCfg = Release|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Verbose_Release|Any CPU.Build.0 = Release|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Verbose_Release|x64.ActiveCfg = Release|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Verbose_Release|x86.ActiveCfg = Release|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Verbose_Release|x86.Build.0 = Release|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Verbose|Any CPU.ActiveCfg = Release|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Verbose|Any CPU.Build.0 = Release|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Verbose|x64.ActiveCfg = Debug|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Verbose|x64.Build.0 = Debug|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Verbose|x86.ActiveCfg = Release|Any CPU
{C66092B0-5C1E-44E9-B524-E0E8E1425379}.Verbose|x86.Build.0 = Release|Any CPU
{F08C3C7A-3221-432E-BAB8-32BCE58408C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F08C3C7A-3221-432E-BAB8-32BCE58408C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F08C3C7A-3221-432E-BAB8-32BCE58408C8}.Debug|x64.ActiveCfg = Debug|Any CPU


+ 12
- 0
BSIPA.sln.DotSettings View File

@ -0,0 +1,12 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=BS/@EntryIndexedValue">BS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IPA/@EntryIndexedValue">IPA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Constants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb"&gt;&lt;ExtraRule Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=beatsaber/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Coroutine/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Modsaber/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Prefs/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=unpatch/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Virtualize/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=waitfor/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

+ 7
- 8
CollectDependencies/Program.cs View File

@ -3,12 +3,10 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CollectDependencies
{
class Program
static class Program
{
static void Main(string[] args)
{
@ -34,7 +32,7 @@ namespace CollectDependencies
return v2;
}
foreach (var line in depsfile.Split(new string[] { Environment.NewLine }, StringSplitOptions.None))
foreach (var line in depsfile.Split(new[] { Environment.NewLine }, StringSplitOptions.None))
{
var parts = line.Split('"');
var path = parts.Last();
@ -48,7 +46,7 @@ namespace CollectDependencies
var arglist = string.Join(" ", parts);
if (command == "from")
{ // an "import" type command
path = File.ReadAllText(Path.Combine(fdir, arglist));
path = File.ReadAllText(Path.Combine(fdir ?? throw new InvalidOperationException(), arglist));
}
else if (command == "prompt")
{
@ -85,16 +83,17 @@ namespace CollectDependencies
if (fname == "") continue;
var outp = Path.Combine(fdir, Path.GetFileName(fname));
var outp = Path.Combine(fdir ?? throw new InvalidOperationException(), Path.GetFileName(fname) ?? throw new InvalidOperationException());
Console.WriteLine($"Copying \"{fname}\" to \"{outp}\"");
if (File.Exists(outp)) File.Delete(outp);
if (Path.GetExtension(fname).ToLower() == ".dll")
if (Path.GetExtension(fname)?.ToLower() == ".dll")
{
// ReSharper disable once StringLiteralTypo
if (fparts.Length > 1 && fparts[1] == "virt")
{
var module = VirtualizedModule.Load(fname);
module.Virtualize(fname = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName(), Path.GetFileName(fname)));
module.Virtualize(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName(), Path.GetFileName(fname) ?? throw new InvalidOperationException()));
}
var modl = ModuleDefinition.ReadModule(fparts[0]);
foreach (var t in modl.Types)


+ 0
- 1
CollectDependencies/Properties/AssemblyInfo.cs View File

@ -1,5 +1,4 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following


+ 14
- 18
CollectDependencies/Virtualizer.cs View File

@ -1,18 +1,13 @@
using Mono.Cecil;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace CollectDependencies
{
class VirtualizedModule
{
private const string ENTRY_TYPE = "Display";
private FileInfo _File;
private ModuleDefinition _Module;
private readonly FileInfo _file;
private ModuleDefinition _module;
public static VirtualizedModule Load(string engineFile)
{
@ -21,7 +16,7 @@ namespace CollectDependencies
private VirtualizedModule(string assemblyFile)
{
_File = new FileInfo(assemblyFile);
_file = new FileInfo(assemblyFile);
LoadModules();
}
@ -29,29 +24,29 @@ namespace CollectDependencies
private void LoadModules()
{
var resolver = new DefaultAssemblyResolver();
resolver.AddSearchDirectory(_File.DirectoryName);
resolver.AddSearchDirectory(_file.DirectoryName);
var parameters = new ReaderParameters
{
AssemblyResolver = resolver,
};
_Module = ModuleDefinition.ReadModule(_File.FullName, parameters);
_module = ModuleDefinition.ReadModule(_file.FullName, parameters);
}
/// <summary>
///
/// </summary>
/// <param name="module"></param>
public void Virtualize(string targetfile)
/// <param name="targetFile"></param>
public void Virtualize(string targetFile)
{
foreach (var type in _Module.Types)
foreach (var type in _module.Types)
{
VirtualizeType(type);
}
_Module.Write(targetfile);
_module.Write(targetFile);
}
private void VirtualizeType(TypeDefinition type)
@ -105,10 +100,11 @@ namespace CollectDependencies
{
get
{
var awakeMethods = _Module.GetTypes().SelectMany(t => t.Methods.Where(m => m.Name == "Awake"));
if (awakeMethods.Count() == 0) return false;
var awakeMethods = _module.GetTypes().SelectMany(t => t.Methods.Where(m => m.Name == "Awake"));
var methodDefinitions = awakeMethods as MethodDefinition[] ?? awakeMethods.ToArray();
if (!methodDefinitions.Any()) return false;
return ((float)awakeMethods.Count(m => m.IsVirtual) / awakeMethods.Count()) > 0.5f;
return ((float)methodDefinitions.Count(m => m.IsVirtual) / methodDefinitions.Count()) > 0.5f;
}
}
}


+ 2
- 6
IPA.Injector/Backups/BackupManager.cs View File

@ -1,13 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace IPA.Injector.Backups
{
public class BackupManager
public static class BackupManager
{
public static BackupUnit FindLatestBackup(string dir)
{


+ 26
- 32
IPA.Injector/Backups/BackupUnit.cs View File

@ -1,11 +1,7 @@
using IPA.Logging;
using IPA.Utilities;
using IPA.Utilities;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Text;
namespace IPA.Injector.Backups
{
@ -16,11 +12,11 @@ namespace IPA.Injector.Backups
{
public string Name { get; private set; }
private DirectoryInfo _BackupPath;
private HashSet<string> _Files = new HashSet<string>();
private FileInfo _ManifestFile;
private static string _ManifestFileName = "$manifest$.txt";
private readonly DirectoryInfo _backupPath;
private readonly HashSet<string> _files = new HashSet<string>();
private readonly FileInfo _manifestFile;
private const string ManifestFileName = "$manifest$.txt";
public BackupUnit(string dir) : this(dir, DateTime.Now.ToString("yyyy-MM-dd_h-mm-ss"))
{
}
@ -28,8 +24,8 @@ namespace IPA.Injector.Backups
private BackupUnit(string dir, string name)
{
Name = name;
_BackupPath = new DirectoryInfo(Path.Combine(dir, Name));
_ManifestFile = new FileInfo(Path.Combine(_BackupPath.FullName, _ManifestFileName));
_backupPath = new DirectoryInfo(Path.Combine(dir, Name));
_manifestFile = new FileInfo(Path.Combine(_backupPath.FullName, ManifestFileName));
}
public static BackupUnit FromDirectory(DirectoryInfo directory, string dir)
@ -37,19 +33,19 @@ namespace IPA.Injector.Backups
var unit = new BackupUnit(dir, directory.Name);
// Read Manifest
if (unit._ManifestFile.Exists)
if (unit._manifestFile.Exists)
{
string manifest = File.ReadAllText(unit._ManifestFile.FullName);
foreach (var line in manifest.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
unit._Files.Add(line);
var manifest = File.ReadAllText(unit._manifestFile.FullName);
foreach (var line in manifest.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
unit._files.Add(line);
}
else
{
foreach (var file in directory.GetFiles("*", SearchOption.AllDirectories))
{
if (file.Name == _ManifestFileName) continue;
if (file.Name == ManifestFileName) continue;
var relativePath = file.FullName.Substring(directory.FullName.Length + 1);
unit._Files.Add(relativePath);
unit._files.Add(relativePath);
}
}
@ -63,20 +59,20 @@ namespace IPA.Injector.Backups
internal void Delete()
{
_BackupPath.Delete(true);
_backupPath.Delete(true);
}
/// <summary>
/// Adds a file to the list of changed files and backups it.
/// </summary>
/// <param name="path"></param>
/// <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));
var backupPath = new FileInfo(Path.Combine(_backupPath.FullName, relativePath));
// Copy over
backupPath.Directory.Create();
backupPath.Directory?.Create();
if (file.Exists)
{
if (File.Exists(backupPath.FullName))
@ -89,17 +85,15 @@ namespace IPA.Injector.Backups
backupPath.Create().Close();
}
if (!_Files.Contains(relativePath))
{
if (!File.Exists(_ManifestFile.FullName))
_ManifestFile.Create().Close();
var stream = _ManifestFile.AppendText();
stream.WriteLine(relativePath);
stream.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);
}
// Add to list
_files.Add(relativePath);
}
}


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

@ -1,24 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
// ReSharper disable ClassNeverInstantiated.Global
// ReSharper disable UnusedMember.Global
namespace IPA.Injector
{
class Bootstrapper : MonoBehaviour
internal class Bootstrapper : MonoBehaviour
{
public event Action Destroyed = delegate {};
void Awake()
public void Awake()
{
}
void Start()
public void Start()
{
Destroy(gameObject);
}
void OnDestroy()
public void OnDestroy()
{
Destroyed();
}


+ 23
- 25
IPA.Injector/ConsoleWindow.cs View File

@ -1,21 +1,19 @@
using System;
using System.Collections;
using System.Runtime.InteropServices;
using System.IO;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace IPA.Injector.Windows
namespace IPA.Injector
{
// https://stackoverflow.com/a/48864902/3117125
static class WinConsole
internal static class WinConsole
{
static public void Initialize(bool alwaysCreateNewConsole = true)
public static void Initialize(bool alwaysCreateNewConsole = true)
{
bool consoleAttached = true;
if (alwaysCreateNewConsole
|| (AttachConsole(ATTACH_PARRENT) == 0
&& Marshal.GetLastWin32Error() != ERROR_ACCESS_DENIED))
|| (AttachConsole(AttachParent) == 0
&& Marshal.GetLastWin32Error() != ErrorAccessDenied))
{
consoleAttached = AllocConsole() != 0;
}
@ -34,7 +32,7 @@ namespace IPA.Injector.Windows
private static void InitializeOutStream()
{
var fs = CreateFileStream("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, FileAccess.Write);
var fs = CreateFileStream("CONOUT$", GenericWrite, FileShareWrite, FileAccess.Write);
if (fs != null)
{
var writer = new StreamWriter(fs) { AutoFlush = true };
@ -45,7 +43,7 @@ namespace IPA.Injector.Windows
private static void InitializeInStream()
{
var fs = CreateFileStream("CONIN$", GENERIC_READ, FILE_SHARE_READ, FileAccess.Read);
var fs = CreateFileStream("CONIN$", GenericRead, FileShareRead, FileAccess.Read);
if (fs != null)
{
Console.SetIn(new StreamReader(fs));
@ -55,7 +53,7 @@ namespace IPA.Injector.Windows
private static FileStream CreateFileStream(string name, uint win32DesiredAccess, uint win32ShareMode,
FileAccess dotNetFileAccess)
{
var file = new SafeFileHandle(CreateFileW(name, win32DesiredAccess, win32ShareMode, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero), true);
var file = new SafeFileHandle(CreateFileW(name, win32DesiredAccess, win32ShareMode, IntPtr.Zero, OpenExisting, FileAttributeNormal, IntPtr.Zero), true);
if (!file.IsInvalid)
{
var fs = new FileStream(file, dotNetFileAccess);
@ -77,7 +75,7 @@ namespace IPA.Injector.Windows
SetLastError = true,
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern UInt32 AttachConsole(UInt32 dwProcessId);
private static extern uint AttachConsole(uint dwProcessId);
[DllImport("kernel32.dll",
EntryPoint = "CreateFileW",
@ -86,23 +84,23 @@ namespace IPA.Injector.Windows
CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr CreateFileW(
string lpFileName,
UInt32 dwDesiredAccess,
UInt32 dwShareMode,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
UInt32 dwCreationDisposition,
UInt32 dwFlagsAndAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile
);
private const UInt32 GENERIC_WRITE = 0x40000000;
private const UInt32 GENERIC_READ = 0x80000000;
private const UInt32 FILE_SHARE_READ = 0x00000001;
private const UInt32 FILE_SHARE_WRITE = 0x00000002;
private const UInt32 OPEN_EXISTING = 0x00000003;
private const UInt32 FILE_ATTRIBUTE_NORMAL = 0x80;
private const UInt32 ERROR_ACCESS_DENIED = 5;
private const UInt32 ATTACH_PARRENT = 0xFFFFFFFF;
private const uint GenericWrite = 0x40000000;
private const uint GenericRead = 0x80000000;
private const uint FileShareRead = 0x00000001;
private const uint FileShareWrite = 0x00000002;
private const uint OpenExisting = 0x00000003;
private const uint FileAttributeNormal = 0x80;
private const uint ErrorAccessDenied = 5;
private const uint AttachParent = 0xFFFFFFFF;
#endregion
}

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

@ -61,7 +61,6 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Updates.cs" />
<Compile Include="Virtualizer.cs" />
<Compile Include="WtfThisDoesntNeedToExist.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\IPA.Loader\IPA.Loader.csproj">


+ 34
- 31
IPA.Injector/Injector.cs View File

@ -1,23 +1,23 @@
using Harmony;
using IPA.Injector.Backups;
using IPA.Injector.Backups;
using IPA.Loader;
using IPA.Logging;
using Mono.Cecil;
using Mono.Cecil.Cil;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using UnityEngine;
using static IPA.Logging.Logger;
using MethodAttributes = Mono.Cecil.MethodAttributes;
namespace IPA.Injector
{
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public static class Injector
{
// ReSharper disable once UnusedParameter.Global
public static void Main(string[] args)
{ // entry point for doorstop
// At this point, literally nothing but mscorlib is loaded,
@ -28,7 +28,7 @@ namespace IPA.Injector
try
{
if (!Environment.GetCommandLineArgs().Contains("--no-console"))
Windows.WinConsole.Initialize();
WinConsole.Initialize();
SetupLibraryLoading();
@ -96,30 +96,30 @@ namespace IPA.Injector
else
{
var ilp = cctor.Body.GetILProcessor();
for (int i = 0; i < Math.Min(2, cctor.Body.Instructions.Count); i++)
for (var i = 0; i < Math.Min(2, cctor.Body.Instructions.Count); i++)
{
var ins = cctor.Body.Instructions[i];
if (i == 0)
switch (i)
{
if (ins.OpCode != OpCodes.Call)
{
case 0 when ins.OpCode != OpCodes.Call:
ilp.Replace(ins, ilp.Create(OpCodes.Call, cbs));
modified = true;
}
else
break;
case 0:
{
var mref = ins.Operand as MethodReference;
if (mref.FullName != cbs.FullName)
var methodRef = ins.Operand as MethodReference;
if (methodRef?.FullName != cbs.FullName)
{
ilp.Replace(ins, ilp.Create(OpCodes.Call, cbs));
modified = true;
}
break;
}
}
if (i == 1 && ins.OpCode != OpCodes.Ret)
{
ilp.Replace(ins, ilp.Create(OpCodes.Ret));
modified = true;
case 1 when ins.OpCode != OpCodes.Ret:
ilp.Replace(ins, ilp.Create(OpCodes.Ret));
modified = true;
break;
}
}
}
@ -140,11 +140,11 @@ namespace IPA.Injector
#endregion
}
private static bool bootstrapped = false;
private static bool _bootstrapped;
private static void CreateBootstrapper()
{
if (bootstrapped) return;
bootstrapped = true;
if (_bootstrapped) return;
_bootstrapped = true;
Application.logMessageReceived += delegate (string condition, string stackTrace, LogType type)
{
@ -154,30 +154,31 @@ namespace IPA.Injector
};
// need to reinit streams singe Unity seems to redirect stdout
Windows.WinConsole.InitializeStreams();
WinConsole.InitializeStreams();
var bootstrapper = new GameObject("NonDestructiveBootstrapper").AddComponent<Bootstrapper>();
bootstrapper.Destroyed += Bootstrapper_Destroyed;
}
private static bool injected = false;
private static bool _injected;
public static void Inject()
{
if (!injected)
if (!_injected)
{
injected = true;
Windows.WinConsole.Initialize();
_injected = true;
WinConsole.Initialize();
SetupLibraryLoading();
var bootstrapper = new GameObject("Bootstrapper").AddComponent<Bootstrapper>();
bootstrapper.Destroyed += Bootstrapper_Destroyed;
}
}
private static bool loadingDone = false;
public static void SetupLibraryLoading()
private static bool _loadingDone;
private static void SetupLibraryLoading()
{
if (loadingDone) return;
loadingDone = true;
if (_loadingDone) return;
_loadingDone = true;
#region Add Library load locations
AppDomain.CurrentDomain.AssemblyResolve += LibLoader.AssemblyLibLoader;
/*try
@ -191,9 +192,11 @@ namespace IPA.Injector
#endregion
}
/*
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetDllDirectory(string lpPathName);
*/
private static void Bootstrapper_Destroyed()
{


+ 22
- 27
IPA.Injector/LibLoader.cs View File

@ -1,20 +1,17 @@
using IPA.Logging;
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using IPA.Logging;
using static IPA.Logging.Logger;
namespace IPA.Injector
{
internal class LibLoader
internal static class LibLoader
{
public static string LibraryPath => Path.Combine(Environment.CurrentDirectory, "Libs");
public static string NativeLibraryPath => Path.Combine(LibraryPath, "Native");
private static Dictionary<string, string> filenameLocations = null;
private static string LibraryPath => Path.Combine(Environment.CurrentDirectory, "Libs");
private static string NativeLibraryPath => Path.Combine(LibraryPath, "Native");
private static Dictionary<string, string> filenameLocations;
public static Assembly AssemblyLibLoader(object source, ResolveEventArgs e)
{
@ -29,20 +26,18 @@ namespace IPA.Injector
filenameLocations.Add(fn.Name, fn.FullName);
}
var testFilen = $"{asmName.Name}.{asmName.Version}.dll";
Log(Level.Debug, $"Looking for file {testFilen}");
var testFile = $"{asmName.Name}.{asmName.Version}.dll";
Log(Level.Debug, $"Looking for file {testFile}");
if (filenameLocations.TryGetValue(testFilen, out string path))
if (filenameLocations.TryGetValue(testFile, out string path))
{
Log(Level.Debug, $"Found file {testFilen} as {path}");
Log(Level.Debug, $"Found file {testFile} as {path}");
if (File.Exists(path))
{
return Assembly.LoadFrom(path);
}
else
{
Log(Level.Critical, $"but {path} no longer exists!");
}
Log(Level.Critical, $"but {path} no longer exists!");
}
Log(Level.Critical, $"No library {asmName} found");
@ -67,13 +62,13 @@ namespace IPA.Injector
// https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/file-system/how-to-iterate-through-a-directory-tree
private static IEnumerable<FileInfo> TraverseTree(string root, Func<string, bool> dirValidator = null)
{
if (dirValidator == null) dirValidator = (s) => true;
if (dirValidator == null) dirValidator = s => true;
// Data structure to hold names of subfolders to be
// examined for files.
Stack<string> dirs = new Stack<string>(32);
if (!System.IO.Directory.Exists(root))
if (!Directory.Exists(root))
{
throw new ArgumentException();
}
@ -85,7 +80,7 @@ namespace IPA.Injector
string[] subDirs;
try
{
subDirs = System.IO.Directory.GetDirectories(currentDir);
subDirs = Directory.GetDirectories(currentDir);
}
// An UnauthorizedAccessException exception will be thrown if we do not have
// discovery permission on a folder or file. It may or may not be acceptable
@ -101,16 +96,16 @@ namespace IPA.Injector
//Console.WriteLine(e.Message);
continue;
}
catch (System.IO.DirectoryNotFoundException)
catch (DirectoryNotFoundException)
{
//Console.WriteLine(e.Message);
continue;
}
string[] files = null;
string[] files;
try
{
files = System.IO.Directory.GetFiles(currentDir);
files = Directory.GetFiles(currentDir);
}
catch (UnauthorizedAccessException)
@ -120,7 +115,7 @@ namespace IPA.Injector
continue;
}
catch (System.IO.DirectoryNotFoundException)
catch (DirectoryNotFoundException)
{
//Console.WriteLine(e.Message);
continue;
@ -135,14 +130,14 @@ namespace IPA.Injector
// Modify this block to perform your required task.
foreach (string file in files)
{
FileInfo nextValue = null;
FileInfo nextValue;
try
{
// Perform whatever action is required in your scenario.
nextValue = new System.IO.FileInfo(file);
nextValue = new FileInfo(file);
//Console.WriteLine("{0}: {1}, {2}", fi.Name, fi.Length, fi.CreationTime);
}
catch (System.IO.FileNotFoundException)
catch (FileNotFoundException)
{
// If file was deleted by a separate application
// or thread since the call to TraverseTree()


+ 3
- 5
IPA.Injector/Properties/AssemblyInfo.cs View File

@ -1,5 +1,4 @@
using IPA.Injector;
using System.Reflection;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -23,7 +22,6 @@ using System.Runtime.InteropServices;
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("2a1af16b-27f1-46e0-9a95-181516bc1cb7")]
[assembly: InternalsVisibleTo("IPA.Loader")]
[assembly: ForceAssemblyReference(typeof(SemVer.Version))]
// Version information for an assembly consists of the following four values:
//
@ -35,5 +33,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("3.11.1")] // will be patched by AppVeyor
[assembly: AssemblyFileVersion("3.11.1")] // will be patched by AppVeyor
[assembly: AssemblyVersion("3.11.2")]
[assembly: AssemblyFileVersion("3.11.2")]

+ 2
- 6
IPA.Injector/Updates.cs View File

@ -1,17 +1,13 @@
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
internal static class Updates
{
public const string DeleteFileName = Updating.ModsaberML.Updater._SpecialDeletionsFile;
private const string DeleteFileName = Updating.ModSaber.Updater.SpecialDeletionsFile;
public static void InstallPendingUpdates()
{
var pendingDir = Path.Combine(BeatSaber.InstallPath, "IPA", "Pending");


+ 4
- 10
IPA.Injector/Virtualizer.cs View File

@ -1,19 +1,14 @@
using Mono.Cecil;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
namespace IPA.Injector
{
internal class VirtualizedModule
{
private const string ENTRY_TYPE = "Display";
public FileInfo file;
public ModuleDefinition module;
private readonly FileInfo file;
private ModuleDefinition module;
public static VirtualizedModule Load(string engineFile)
{
@ -35,11 +30,10 @@ namespace IPA.Injector
/// <summary>
///
/// </summary>
/// <param name="module"></param>
public void Virtualize(AssemblyName selfName, Action beforeChangeCallback = null)
{
bool changed = false;
bool virtualize = true;
var changed = false;
var virtualize = true;
foreach (var r in module.AssemblyReferences)
{
if (r.Name == selfName.Name)


+ 0
- 21
IPA.Injector/WtfThisDoesntNeedToExist.cs View File

@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IPA.Injector
{
[AttributeUsage(AttributeTargets.Assembly)]
internal class ForceAssemblyReferenceAttribute : Attribute
{
public ForceAssemblyReferenceAttribute(Type forcedType)
{
//not sure if these two lines are required since
//the type is passed to constructor as parameter,
//thus effectively being used
Action<Type> noop = _ => { };
noop(forcedType);
}
}
}

+ 16
- 18
IPA.Loader/Config/ConfigProviders/JsonConfigProvider.cs View File

@ -1,12 +1,10 @@
using IPA.Logging;
using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.IO;
using IPA.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IPA.Config.ConfigProviders
{
@ -17,11 +15,11 @@ namespace IPA.Config.ConfigProviders
// TODO: create a wrapper that allows empty object creation
public dynamic Dynamic => jsonObj;
public bool HasChanged { get; private set; } = false;
public bool HasChanged { get; private set; }
public DateTime LastModified => File.GetLastWriteTime(Filename + ".json");
private string _filename = null;
private string _filename;
public string Filename
{
get => _filename;
@ -37,10 +35,10 @@ namespace IPA.Config.ConfigProviders
{
Logger.config.Debug($"Loading file {Filename}.json");
var finfo = new FileInfo(Filename + ".json");
if (finfo.Exists)
var fileInfo = new FileInfo(Filename + ".json");
if (fileInfo.Exists)
{
string json = finfo.OpenText().ReadToEnd();
var json = fileInfo.OpenText().ReadToEnd();
try
{
jsonObj = JObject.Parse(json);
@ -50,7 +48,7 @@ namespace IPA.Config.ConfigProviders
Logger.config.Error($"Error parsing JSON in file {Filename}.json; resetting to empty JSON");
Logger.config.Error(e);
jsonObj = new JObject();
File.WriteAllText(finfo.FullName, JsonConvert.SerializeObject(jsonObj, Formatting.Indented));
File.WriteAllText(fileInfo.FullName, JsonConvert.SerializeObject(jsonObj, Formatting.Indented));
}
}
else
@ -68,17 +66,17 @@ namespace IPA.Config.ConfigProviders
jsonObj.CollectionChanged += JsonObj_CollectionChanged;
}
private void JsonObj_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
private void JsonObj_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
HasChanged = true;
}
private void JsonObj_ListChanged(object sender, System.ComponentModel.ListChangedEventArgs e)
private void JsonObj_ListChanged(object sender, ListChangedEventArgs e)
{
HasChanged = true;
}
private void JsonObj_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
private void JsonObj_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
HasChanged = true;
}
@ -92,9 +90,9 @@ namespace IPA.Config.ConfigProviders
{
Logger.config.Debug($"Saving file {Filename}.json");
var finfo = new FileInfo(Filename + ".json");
var fileInfo = new FileInfo(Filename + ".json");
File.WriteAllText(finfo.FullName, JsonConvert.SerializeObject(jsonObj, Formatting.Indented));
File.WriteAllText(fileInfo.FullName, JsonConvert.SerializeObject(jsonObj, Formatting.Indented));
HasChanged = false;
}


+ 1
- 4
IPA.Loader/Config/IConfigProvider.cs View File

@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// ReSharper disable UnusedMember.Global
namespace IPA.Config
{


+ 12
- 14
IPA.Loader/Config/IniFile.cs View File

@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
@ -72,28 +70,28 @@ namespace IPA.Config
/// <summary>
/// Write Data to the INI File
/// </summary>
/// <PARAM name="Section"></PARAM>
/// <PARAM name="section"></PARAM>
/// Section name
/// <PARAM name="Key"></PARAM>
/// <PARAM name="key"></PARAM>
/// Key Name
/// <PARAM name="Value"></PARAM>
/// <PARAM name="value"></PARAM>
/// Value Name
public void IniWriteValue(string Section, string Key, string Value)
public void IniWriteValue(string section, string key, string value)
{
WritePrivateProfileString(Section, Key, Value, IniFileInfo.FullName);
WritePrivateProfileString(section, key, value, IniFileInfo.FullName);
}
/// <summary>
/// Read Data Value From the Ini File
/// </summary>
/// <PARAM name="Section"></PARAM>
/// <PARAM name="Key"></PARAM>
/// <PARAM name="section"></PARAM>
/// <PARAM name="key"></PARAM>
/// <returns></returns>
public string IniReadValue(string Section, string Key)
public string IniReadValue(string section, string key)
{
const int MAX_CHARS = 1023;
StringBuilder result = new StringBuilder(MAX_CHARS);
GetPrivateProfileString(Section, Key, "", result, MAX_CHARS, IniFileInfo.FullName);
const int maxChars = 1023;
StringBuilder result = new StringBuilder(maxChars);
GetPrivateProfileString(section, key, "", result, maxChars, IniFileInfo.FullName);
return result.ToString();
}
}


+ 28
- 35
IPA.Loader/Config/ModPrefs.cs View File

@ -1,9 +1,8 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
namespace IPA.Config
{
@ -85,43 +84,37 @@ namespace IPA.Config
void SetBool(string section, string name, bool value);
}
/// <inheritdoc />
/// <summary>
/// Allows to get and set preferences for your mod.
/// </summary>
public class ModPrefs : IModPrefs
{
private static ModPrefs _staticInstance = null;
private static IModPrefs StaticInstace
{
get
{
if (_staticInstance == null)
_staticInstance = new ModPrefs();
return _staticInstance;
}
}
private static ModPrefs _staticInstance;
private static IModPrefs StaticInstance => _staticInstance ?? (_staticInstance = new ModPrefs());
internal static Dictionary<IBeatSaberPlugin, ModPrefs> ModPrefses { get; set; } = new Dictionary<IBeatSaberPlugin, ModPrefs>();
// ReSharper disable once IdentifierTypo
internal static Dictionary<IBeatSaberPlugin, ModPrefs> ModPrefss { get; set; } = new Dictionary<IBeatSaberPlugin, ModPrefs>();
private IniFile Instance;
private readonly IniFile _instance;
/// <summary>
/// Constructs a ModPrefs object for the provide plugin.
/// </summary>
/// <param name="plugin">the plugin to get the preferences file for</param>
public ModPrefs(IBeatSaberPlugin plugin) {
Instance = new IniFile(Path.Combine(Environment.CurrentDirectory, "UserData", "ModPrefs", $"{plugin.Name}.ini"));
ModPrefses.Add(plugin, this);
_instance = new IniFile(Path.Combine(Environment.CurrentDirectory, "UserData", "ModPrefs", $"{plugin.Name}.ini"));
ModPrefss.Add(plugin, this);
}
private ModPrefs()
{
Instance = new IniFile(Path.Combine(Environment.CurrentDirectory, "UserData", "modprefs.ini"));
_instance = new IniFile(Path.Combine(Environment.CurrentDirectory, "UserData", "modprefs.ini"));
}
string IModPrefs.GetString(string section, string name, string defaultValue, bool autoSave)
{
var value = Instance.IniReadValue(section, name);
var value = _instance.IniReadValue(section, name);
if (value != "")
return value;
else if (autoSave)
@ -138,11 +131,11 @@ namespace IPA.Config
/// <param name="autoSave">Whether or not the default value should be written if no value is found.</param>
/// <returns></returns>
public static string GetString(string section, string name, string defaultValue = "", bool autoSave = false)
=> StaticInstace.GetString(section, name, defaultValue, autoSave);
=> StaticInstance.GetString(section, name, defaultValue, autoSave);
int IModPrefs.GetInt(string section, string name, int defaultValue, bool autoSave)
{
if (int.TryParse(Instance.IniReadValue(section, name), out var value))
if (int.TryParse(_instance.IniReadValue(section, name), out var value))
return value;
else if (autoSave)
(this as IModPrefs).SetInt(section, name, defaultValue);
@ -158,11 +151,11 @@ namespace IPA.Config
/// <param name="autoSave">Whether or not the default value should be written if no value is found.</param>
/// <returns></returns>
public static int GetInt(string section, string name, int defaultValue = 0, bool autoSave = false)
=> StaticInstace.GetInt(section, name, defaultValue, autoSave);
=> StaticInstance.GetInt(section, name, defaultValue, autoSave);
float IModPrefs.GetFloat(string section, string name, float defaultValue, bool autoSave)
{
if (float.TryParse(Instance.IniReadValue(section, name), out var value))
if (float.TryParse(_instance.IniReadValue(section, name), out var value))
return value;
else if (autoSave)
(this as IModPrefs).SetFloat(section, name, defaultValue);
@ -178,7 +171,7 @@ namespace IPA.Config
/// <param name="autoSave">Whether or not the default value should be written if no value is found.</param>
/// <returns></returns>
public static float GetFloat(string section, string name, float defaultValue = 0f, bool autoSave = false)
=> StaticInstace.GetFloat(section, name, defaultValue, autoSave);
=> StaticInstance.GetFloat(section, name, defaultValue, autoSave);
bool IModPrefs.GetBool(string section, string name, bool defaultValue, bool autoSave)
{
@ -203,11 +196,11 @@ namespace IPA.Config
/// <param name="autoSave">Whether or not the default value should be written if no value is found.</param>
/// <returns></returns>
public static bool GetBool(string section, string name, bool defaultValue = false, bool autoSave = false)
=> StaticInstace.GetBool(section, name, defaultValue, autoSave);
=> StaticInstance.GetBool(section, name, defaultValue, autoSave);
bool IModPrefs.HasKey(string section, string name)
{
return Instance.IniReadValue(section, name) != null;
return _instance.IniReadValue(section, name) != null;
}
/// <summary>
/// Checks whether or not a key exists in the ini.
@ -215,11 +208,11 @@ namespace IPA.Config
/// <param name="section">Section of the key.</param>
/// <param name="name">Name of the key.</param>
/// <returns></returns>
public static bool HasKey(string section, string name) => StaticInstace.HasKey(section, name);
public static bool HasKey(string section, string name) => StaticInstance.HasKey(section, name);
void IModPrefs.SetFloat(string section, string name, float value)
{
Instance.IniWriteValue(section, name, value.ToString());
_instance.IniWriteValue(section, name, value.ToString(CultureInfo.InvariantCulture));
}
/// <summary>
/// Sets a float in the ini.
@ -228,11 +221,11 @@ namespace IPA.Config
/// <param name="name">Name of the key.</param>
/// <param name="value">Value that should be written.</param>
public static void SetFloat(string section, string name, float value)
=> StaticInstace.SetFloat(section, name, value);
=> StaticInstance.SetFloat(section, name, value);
void IModPrefs.SetInt(string section, string name, int value)
{
Instance.IniWriteValue(section, name, value.ToString());
_instance.IniWriteValue(section, name, value.ToString());
}
/// <summary>
/// Sets an int in the ini.
@ -241,11 +234,11 @@ namespace IPA.Config
/// <param name="name">Name of the key.</param>
/// <param name="value">Value that should be written.</param>
public static void SetInt(string section, string name, int value)
=> StaticInstace.SetInt(section, name, value);
=> StaticInstance.SetInt(section, name, value);
void IModPrefs.SetString(string section, string name, string value)
{
Instance.IniWriteValue(section, name, value);
_instance.IniWriteValue(section, name, value);
}
/// <summary>
/// Sets a string in the ini.
@ -254,11 +247,11 @@ namespace IPA.Config
/// <param name="name">Name of the key.</param>
/// <param name="value">Value that should be written.</param>
public static void SetString(string section, string name, string value)
=> StaticInstace.SetString(section, name, value);
=> StaticInstance.SetString(section, name, value);
void IModPrefs.SetBool(string section, string name, bool value)
{
Instance.IniWriteValue(section, name, value ? "1" : "0");
_instance.IniWriteValue(section, name, value ? "1" : "0");
}
/// <summary>
/// Sets a bool in the ini.
@ -267,7 +260,7 @@ namespace IPA.Config
/// <param name="name">Name of the key.</param>
/// <param name="value">Value that should be written.</param>
public static void SetBool(string section, string name, bool value)
=> StaticInstace.SetBool(section, name, value);
=> StaticInstance.SetBool(section, name, value);
}
/// <summary>
@ -280,7 +273,7 @@ namespace IPA.Config
/// <param name="plugin">the plugin wanting the prefrences</param>
/// <returns>the ModPrefs object</returns>
public static IModPrefs GetModPrefs(this IBeatSaberPlugin plugin) {
return ModPrefs.ModPrefses.First(o => o.Key == plugin).Value;
return ModPrefs.ModPrefss.First(o => o.Key == plugin).Value;
}
}
}

+ 4
- 6
IPA.Loader/IPA.Loader.csproj View File

@ -70,7 +70,7 @@
<Compile Include="Logging\Logger.cs" />
<Compile Include="Logging\LogPrinter.cs" />
<Compile Include="Config\ModPrefs.cs" />
<Compile Include="Updating\Converters\ModsaberDependencyConverter.cs" />
<Compile Include="Updating\Converters\ModSaberDependencyConverter.cs" />
<Compile Include="Updating\Converters\SemverRangeConverter.cs" />
<Compile Include="Updating\Converters\SemverVersionConverter.cs" />
<Compile Include="Utilities\BeatSaber.cs" />
@ -86,9 +86,8 @@
<Compile Include="Loader\PluginComponent.cs" />
<Compile Include="Loader\PluginManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Updating\Backup\BackupUnit.cs" />
<Compile Include="Updating\ModsaberML\ApiEndpoint.cs" />
<Compile Include="Updating\ModsaberML\Updater.cs" />
<Compile Include="Updating\ModSaber\ApiEndpoint.cs" />
<Compile Include="Updating\ModSaber\Updater.cs" />
<Compile Include="Updating\SelfPlugin.cs" />
<Compile Include="Utilities\Extensions.cs" />
<Compile Include="Utilities\LoneFunctions.cs" />
@ -107,7 +106,6 @@
<Version>1.2.0</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

+ 4
- 10
IPA.Loader/Loader/Composite/CompositeBSPlugin.cs View File

@ -1,9 +1,5 @@
using IPA;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.SceneManagement;
using Logger = IPA.Logging.Logger;
@ -11,7 +7,7 @@ namespace IPA.Loader.Composite
{
internal class CompositeBSPlugin : IBeatSaberPlugin
{
IEnumerable<IBeatSaberPlugin> plugins;
private readonly IEnumerable<IBeatSaberPlugin> plugins;
private delegate void CompositeCall(IBeatSaberPlugin plugin);
@ -83,14 +79,12 @@ namespace IPA.Loader.Composite
public string Version => throw new NotImplementedException();
public Uri UpdateUri => throw new NotImplementedException();
public ModsaberModInfo ModInfo => throw new NotImplementedException();
public void OnLateUpdate() {
Invoke(plugin => {
if (plugin is IEnhancedBeatSaberPlugin)
((IEnhancedBeatSaberPlugin) plugin).OnLateUpdate();
if (plugin is IEnhancedBeatSaberPlugin saberPlugin)
saberPlugin.OnLateUpdate();
});
}
}

+ 7
- 16
IPA.Loader/Loader/Composite/CompositeIPAPlugin.cs View File

@ -1,11 +1,6 @@
using System;
using IPA.Old;
using System;
using System.Collections.Generic;
using IPA;
using IPA.Old;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.SceneManagement;
using Logger = IPA.Logging.Logger;
namespace IPA.Loader.Composite
@ -13,7 +8,7 @@ namespace IPA.Loader.Composite
#pragma warning disable CS0618 // Type or member is obsolete
internal class CompositeIPAPlugin : IPlugin
{
IEnumerable<IPlugin> plugins;
private readonly IEnumerable<IPlugin> plugins;
private delegate void CompositeCall(IPlugin plugin);
@ -48,18 +43,14 @@ namespace IPA.Loader.Composite
Invoke(plugin => plugin.OnFixedUpdate());
}
public string Name {
get { throw new NotImplementedException(); }
}
public string Name => throw new NotImplementedException();
public string Version {
get { throw new NotImplementedException(); }
}
public string Version => throw new NotImplementedException();
public void OnLateUpdate() {
Invoke(plugin => {
if (plugin is IEnhancedBeatSaberPlugin)
((IEnhancedBeatSaberPlugin) plugin).OnLateUpdate();
if (plugin is IEnhancedPlugin saberPlugin)
saberPlugin.OnLateUpdate();
});
}


+ 7
- 8
IPA.Loader/Loader/PluginComponent.cs View File

@ -1,19 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
using IPA.Loader.Composite;
using System;
using System.Diagnostics.CodeAnalysis;
using UnityEngine;
using UnityEngine.SceneManagement;
using IPA.Loader;
using IPA.Loader.Composite;
using IPA.Logging;
// ReSharper disable UnusedMember.Local
namespace IPA.Loader
{
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
internal class PluginComponent : MonoBehaviour
{
private CompositeBSPlugin bsPlugins;
private CompositeIPAPlugin ipaPlugins;
private bool quitting = false;
private bool quitting;
internal static PluginComponent Create()
{
@ -29,7 +28,7 @@ namespace IPA.Loader
ipaPlugins = new CompositeIPAPlugin(PluginManager.Plugins);
#pragma warning restore CS0618 // Type or member is obsolete
gameObject.AddComponent<Updating.ModsaberML.Updater>();
gameObject.AddComponent<Updating.ModSaber.Updater>();
bsPlugins.OnApplicationStart();
ipaPlugins.OnApplicationStart();


+ 29
- 31
IPA.Loader/Loader/PluginManager.cs View File

@ -1,22 +1,20 @@
using IPA;
using IPA.Config;
using IPA.Config.ConfigProviders;
using IPA.Logging;
using IPA.Old;
using IPA.Updating;
using IPA.Utilities;
using Mono.Cecil;
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using IPA.Config;
using IPA.Config.ConfigProviders;
using IPA.Logging;
using IPA.Old;
using IPA.Updating;
using IPA.Utilities;
using Mono.Cecil;
using UnityEngine;
using Logger = IPA.Logging.Logger;
namespace IPA.Loader
{
@ -35,9 +33,9 @@ namespace IPA.Loader
internal IBeatSaberPlugin Plugin { get; set; }
internal string Filename { get; set; }
/// <summary>
/// The Modsaber updating info for the mod, or null.
/// The ModSaber updating info for the mod, or null.
/// </summary>
public ModsaberModInfo ModsaberInfo { get; internal set; }
public ModsaberModInfo ModSaberInfo { get; internal set; }
}
/// <summary>
@ -51,10 +49,10 @@ namespace IPA.Loader
{
LoadPlugins();
}
return _bsPlugins.Select(p => p.Plugin);
return (_bsPlugins ?? throw new InvalidOperationException()).Select(p => p.Plugin);
}
}
private static List<PluginInfo> _bsPlugins = null;
private static List<PluginInfo> _bsPlugins;
internal static IEnumerable<PluginInfo> BSMetas
{
get
@ -74,17 +72,17 @@ namespace IPA.Loader
/// <returns>the plugin info for the requested plugin or null</returns>
public static PluginInfo GetPlugin(string name)
{
return BSMetas.Where(p => p.Plugin.Name == name).FirstOrDefault();
return BSMetas.FirstOrDefault(p => p.Plugin.Name == name);
}
/// <summary>
/// Gets info about the plugin with the specified modsaber name.
/// Gets info about the plugin with the specified ModSaber name.
/// </summary>
/// <param name="name">the modsaber name of the plugin to get (must be an exact match)</param>
/// <param name="name">the ModSaber name of the plugin to get (must be an exact match)</param>
/// <returns>the plugin info for the requested plugin or null</returns>
public static PluginInfo GetPluginFromModsaberName(string name)
public static PluginInfo GetPluginFromModSaberName(string name)
{
return BSMetas.Where(p => p.ModsaberInfo.InternalName == name).FirstOrDefault();
return BSMetas.FirstOrDefault(p => p.ModSaberInfo.InternalName == name);
}
/// <summary>
@ -102,11 +100,11 @@ namespace IPA.Loader
return _ipaPlugins;
}
}
private static List<IPlugin> _ipaPlugins = null;
private static List<IPlugin> _ipaPlugins;
internal static IConfigProvider SelfConfigProvider { get; set; } = null;
internal static IConfigProvider SelfConfigProvider { get; set; }
internal static List<KeyValuePair<IConfigProvider,Ref<DateTime>>> configProviders = new List<KeyValuePair<IConfigProvider, Ref<DateTime>>>();
internal static readonly List<KeyValuePair<IConfigProvider,Ref<DateTime>>> configProviders = new List<KeyValuePair<IConfigProvider, Ref<DateTime>>>();
private static void LoadPlugins()
{
@ -175,7 +173,7 @@ namespace IPA.Loader
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.ModsaberML"; //@ref.Name = "";
if (@ref.Namespace == "IllusionInjector.Updating.ModsaberML") @ref.Namespace = "IPA.Updating.ModSaber"; //@ref.Name = "";
}
module.Write(pluginCopy);
@ -187,11 +185,11 @@ namespace IPA.Loader
Filename = Path.Combine(Environment.CurrentDirectory, "IPA.exe"),
Plugin = SelfPlugin.Instance
};
selfPlugin.ModsaberInfo = selfPlugin.Plugin.ModInfo;
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)));
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
@ -204,7 +202,7 @@ namespace IPA.Loader
}
Logger.log.Info(exeName);
Logger.log.Info($"Running on Unity {UnityEngine.Application.unityVersion}");
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}");
@ -294,7 +292,7 @@ namespace IPA.Loader
{
if (cfgProvider == null)
{
cfgProvider = new JsonConfigProvider() { Filename = Path.Combine("UserData", $"{bsPlugin.Name}") };
cfgProvider = new JsonConfigProvider { Filename = Path.Combine("UserData", $"{bsPlugin.Name}") };
configProviders.Add(new KeyValuePair<IConfigProvider, Ref<DateTime>>(cfgProvider, new Ref<DateTime>(cfgProvider.LastModified)));
cfgProvider.Load();
}
@ -311,12 +309,12 @@ namespace IPA.Loader
{
Plugin = bsPlugin,
Filename = file.Replace("\\.cache", ""), // quick and dirty fix
ModsaberInfo = bsPlugin.ModInfo
ModSaberInfo = bsPlugin.ModInfo
});
}
catch (AmbiguousMatchException)
{
Logger.loader.Error($"Only one Init allowed per plugin");
Logger.loader.Error("Only one Init allowed per plugin");
}
}
else


+ 0
- 4
IPA.Loader/Logging/LogPrinter.cs View File

@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IPA.Logging
{


+ 2
- 5
IPA.Loader/Logging/Logger.cs View File

@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// ReSharper disable InconsistentNaming
namespace IPA.Logging
{
@ -25,7 +22,7 @@ namespace IPA.Logging
internal static Logger libLoader => log.GetChildLogger("LibraryLoader");
internal static Logger loader => log.GetChildLogger("Loader");
internal static Logger config => log.GetChildLogger("Config");
internal static bool LogCreated => _log != null || UnityLogInterceptor._logger != null;
internal static bool LogCreated => _log != null || UnityLogInterceptor.Logger != null;
/// <summary>
/// The standard format for log messages.


+ 2
- 8
IPA.Loader/Logging/Printers/ColoredConsolePrinter.cs View File

@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using IPA.Logging;
namespace IPA.Logging.Printers
{
@ -35,8 +29,8 @@ namespace IPA.Logging.Printers
{
if (((byte)level & (byte)StandardLogger.PrintFilter) == 0) return;
Console.ForegroundColor = Color;
foreach (var line in message.Split(new string[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
Console.WriteLine(string.Format(Logger.LogFormat, line, logName, time, level.ToString().ToUpper()));
foreach (var line in message.Split(new[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
Console.WriteLine(Logger.LogFormat, line, logName, time, level.ToString().ToUpper());
Console.ResetColor();
}
}


+ 12
- 16
IPA.Loader/Logging/Printers/GZFilePrinter.cs View File

@ -1,12 +1,8 @@
using IPA.Logging;
using Ionic.Zlib;
using Ionic.Zlib;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace IPA.Logging.Printers
{
@ -26,7 +22,7 @@ namespace IPA.Logging.Printers
/// <summary>
/// The <see cref="StreamWriter"/> that writes to the GZip file.
/// </summary>
protected StreamWriter fileWriter;
protected StreamWriter FileWriter;
private GZipStream zstream;
private FileStream fstream;
@ -47,7 +43,7 @@ namespace IPA.Logging.Printers
fileInfo = new FileInfo(fileInfo.FullName + ".gz");
fileInfo.Create().Close();
var symlink = new FileInfo(Path.Combine(fileInfo.DirectoryName, $"latest{ext}.gz"));
var symlink = new FileInfo(Path.Combine(fileInfo.DirectoryName ?? throw new InvalidOperationException(), $"latest{ext}.gz"));
if (symlink.Exists) symlink.Delete();
try
@ -75,7 +71,7 @@ namespace IPA.Logging.Printers
/// <summary>
/// Called at the start of any print session.
/// </summary>
public override sealed void StartPrint()
public sealed override void StartPrint()
{
InitLog();
@ -84,21 +80,21 @@ namespace IPA.Logging.Printers
{
FlushMode = FlushType.Full
};
fileWriter = new StreamWriter(zstream, new UTF8Encoding(false));
FileWriter = new StreamWriter(zstream, new UTF8Encoding(false));
}
/// <summary>
/// Called at the end of any print session.
/// </summary>
public override sealed void EndPrint()
public sealed override void EndPrint()
{
fileWriter.Flush();
FileWriter.Flush();
zstream.Flush();
fstream.Flush();
fileWriter.Close();
FileWriter.Close();
zstream.Close();
fstream.Close();
fileWriter.Dispose();
FileWriter.Dispose();
zstream.Dispose();
fstream.Dispose();
}
@ -120,13 +116,13 @@ namespace IPA.Logging.Printers
{
if (disposing)
{
fileWriter.Flush();
FileWriter.Flush();
zstream.Flush();
fstream.Flush();
fileWriter.Close();
FileWriter.Close();
zstream.Close();
fstream.Close();
fileWriter.Dispose();
FileWriter.Dispose();
zstream.Dispose();
fstream.Dispose();
}


+ 2
- 7
IPA.Loader/Logging/Printers/GlobalLogFilePrinter.cs View File

@ -1,10 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using IPA.Logging;
namespace IPA.Logging.Printers
{
@ -27,8 +22,8 @@ namespace IPA.Logging.Printers
/// <param name="message">the message to print</param>
public override void Print(Logger.Level level, DateTime time, string logName, string message)
{
foreach (var line in message.Split(new string[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
fileWriter.WriteLine(string.Format(Logger.LogFormat, line, logName, time, level.ToString().ToUpper()));
foreach (var line in message.Split(new[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
FileWriter.WriteLine(Logger.LogFormat, line, logName, time, level.ToString().ToUpper());
}
/// <summary>


+ 3
- 8
IPA.Loader/Logging/Printers/PluginLogFilePrinter.cs View File

@ -1,10 +1,5 @@
using IPA.Logging;
using System;
using System.Collections.Generic;
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IPA.Logging.Printers
{
@ -50,8 +45,8 @@ namespace IPA.Logging.Printers
/// <param name="message">the message to print</param>
public override void Print(Logger.Level level, DateTime time, string logName, string message)
{
foreach (var line in message.Split(new string[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
fileWriter.WriteLine(string.Format(Logger.LogFormat, line, logName, time, level.ToString().ToUpper()));
foreach (var line in message.Split(new[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
FileWriter.WriteLine(Logger.LogFormat, line, logName, time, level.ToString().ToUpper());
}
}
}

+ 3
- 8
IPA.Loader/Logging/Printers/PluginSubLogPrinter.cs View File

@ -1,10 +1,5 @@
using IPA.Logging;
using System;
using System.Collections.Generic;
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IPA.Logging.Printers
{
@ -53,8 +48,8 @@ namespace IPA.Logging.Printers
/// <param name="message">the message to print</param>
public override void Print(Logger.Level level, DateTime time, string logName, string message)
{
foreach (var line in message.Split(new string[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
fileWriter.WriteLine(string.Format("[{3} @ {2:HH:mm:ss}] {0}", line, logName, time, level.ToString().ToUpper()));
foreach (var line in message.Split(new[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
FileWriter.WriteLine("[{2} @ {1:HH:mm:ss}] {0}", line, time, level.ToString().ToUpper());
}
}
}

+ 25
- 29
IPA.Loader/Logging/StandardLogger.cs View File

@ -1,20 +1,15 @@
using IPA.Logging;
using IPA.Config;
using IPA.Logging.Printers;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using IPA;
using IPA.Logging.Printers;
using IPA.Config;
namespace IPA.Logging
{
/// <summary>
/// The default <see cref="Logger"/> implimentation.
/// The default <see cref="Logger"/> implementation.
/// </summary>
public class StandardLogger : Logger
{
@ -48,8 +43,8 @@ namespace IPA.Logging
new GlobalLogFilePrinter()
};
private string logName;
private static readonly bool showSourceClass = true;
private readonly string logName;
private static readonly bool showSourceClass;
/// <summary>
/// All levels defined by this filter will be sent to loggers. All others will be ignored.
/// </summary>
@ -122,32 +117,33 @@ namespace IPA.Logging
{
_logQueue.Add(new LogMessage
{
level = level,
message = message,
logger = this,
time = DateTime.Now
Level = level,
Message = message,
Logger = this,
Time = DateTime.Now
});
}
/// <inheritdoc />
/// <summary>
/// An override to <see cref="Logger.Debug(string)"/> which shows the method that called it.
/// An override to <see cref="M:IPA.Logging.Logger.Debug(System.String)" /> which shows the method that called it.
/// </summary>
/// <param name="message">the message to log</param>
public override void Debug(string message)
{ // add source to message
var stfm = new StackTrace().GetFrame(1).GetMethod();
if (showSourceClass)
base.Debug($"{{{stfm.DeclaringType.FullName}::{stfm.Name}}} {message}");
else
base.Debug(message);
{
// add source to message
var stackFrame = new StackTrace().GetFrame(1).GetMethod();
base.Debug(showSourceClass
? $"{{{stackFrame.DeclaringType?.FullName}::{stackFrame.Name}}} {message}"
: message);
}
internal struct LogMessage
private struct LogMessage
{
public Level level;
public StandardLogger logger;
public string message;
public DateTime time;
public Level Level;
public StandardLogger Logger;
public string Message;
public DateTime Time;
}
private static BlockingCollection<LogMessage> _logQueue = new BlockingCollection<LogMessage>();
@ -157,11 +153,11 @@ namespace IPA.Logging
{
HashSet<LogPrinter> started = new HashSet<LogPrinter>();
while (_logQueue.TryTake(out LogMessage msg, Timeout.Infinite)) {
foreach (var printer in msg.logger.printers)
foreach (var printer in msg.Logger.printers)
{
try
{
if (((byte)msg.level & (byte)printer.Filter) != 0)
if (((byte)msg.Level & (byte)printer.Filter) != 0)
{
if (!started.Contains(printer))
{
@ -169,7 +165,7 @@ namespace IPA.Logging
started.Add(printer);
}
printer.Print(msg.level, msg.time, msg.logger.logName, msg.message);
printer.Print(msg.Level, msg.Time, msg.Logger.logName, msg.Message);
}
}
catch (Exception e)


+ 4
- 16
IPA.Loader/Logging/UnityLogInterceptor.cs View File

@ -1,23 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine;
namespace IPA.Logging
{
internal class UnityLogInterceptor
internal static class UnityLogInterceptor
{
internal static Logger _logger;
public static Logger UnityLogger {
get
{
if (_logger == null)
_logger = new StandardLogger("UnityEngine");
return _logger;
}
}
internal static Logger Logger;
public static Logger UnityLogger => Logger ?? (Logger = new StandardLogger("UnityEngine"));
public static Logger.Level LogTypeToLevel(LogType type)
{


+ 3
- 5
IPA.Loader/PluginInterfaces/BeatSaber/IBeatSaberPlugin.cs View File

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine.SceneManagement;
using UnityEngine.SceneManagement;
// ReSharper disable CheckNamespace
namespace IPA
{
@ -23,7 +21,7 @@ namespace IPA
string Version { get; }
/// <summary>
/// Gets the info for the Modsaber release of this plugin. Return null if there is no Modsaber release.
/// Gets the info for the ModSaber release of this plugin. Return null if there is no ModSaber release.
/// </summary>
ModsaberModInfo ModInfo { get; }


+ 2
- 4
IPA.Loader/PluginInterfaces/BeatSaber/IEnhancedBeatSaberPlugin.cs View File

@ -1,9 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text;
// ReSharper disable CheckNamespace
namespace IPA
{
/// <inheritdoc cref="IBeatSaberPlugin" />
/// <summary>
/// An enhanced version of a standard BeatSaber plugin.
/// </summary>


+ 10
- 12
IPA.Loader/PluginInterfaces/BeatSaber/ModsaberModInfo.cs View File

@ -1,14 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// ReSharper disable CheckNamespace
namespace IPA
{
/// <summary>
/// A class to provide information about a mod on ModSaber.ML
/// </summary>
// ReSharper disable once IdentifierTypo
public class ModsaberModInfo
{
/// <summary>
@ -16,12 +14,12 @@ namespace IPA
/// </summary>
public string InternalName
{
get => _InternalName;
get => _internalName;
set
{
if (_InternalName == null)
if (_internalName == null)
{
_InternalName = value;
_internalName = value;
}
else
{
@ -29,19 +27,19 @@ namespace IPA
}
}
}
private string _InternalName = null;
private string _internalName;
/// <summary>
/// The version of the currently installed mod. Used to compare to the version on ModSaber. Should be a valid SemVer version.
/// </summary>
public string CurrentVersion
{
get => _CurrentVersion;
get => _currentVersion;
set
{
if (_CurrentVersion == null)
if (_currentVersion == null)
{
_CurrentVersion = value;
_currentVersion = value;
}
else
{
@ -49,6 +47,6 @@ namespace IPA
}
}
}
private string _CurrentVersion = null;
private string _currentVersion;
}
}

+ 2
- 7
IPA.Loader/PluginInterfaces/IGenericEnhancedPlugin.cs View File

@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// ReSharper disable CheckNamespace
namespace IPA
{
/// <summary>
@ -12,7 +7,7 @@ namespace IPA
public interface IGenericEnhancedPlugin
{
/// <summary>
/// Gets a list of executables this plugin should be excuted on (without the file ending)
/// Gets a list of executables this plugin should be executed on (without the file ending)
/// </summary>
/// <example>{ "PlayClub", "PlayClubStudio" }</example>
string[] Filter { get; }


+ 2
- 2
IPA.Loader/PluginInterfaces/IPA/IEnhancedPlugin.cs View File

@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using System.Text;
// ReSharper disable CheckNamespace
namespace IPA.Old
{
/// <inheritdoc cref="IPlugin" />
/// <summary>
/// An enhanced version of the standard IPA plugin.
/// </summary>


+ 1
- 2
IPA.Loader/PluginInterfaces/IPA/IPlugin.cs View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text;
// ReSharper disable CheckNamespace
namespace IPA.Old
{


+ 0
- 147
IPA.Loader/Updating/Backup/BackupUnit.cs View File

@ -1,147 +0,0 @@
using IPA.Logging;
using IPA.Utilities;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Text;
namespace IPA.Updating.Backup
{
/// <summary>
/// A unit for backup. WIP.
/// </summary>
internal class BackupUnit
{
public string Name { get; private set; }
private DirectoryInfo _BackupPath;
private List<string> _Files = new List<string>();
private FileInfo _ManifestFile;
private static string _ManifestFileName = "$manifest$.txt";
public BackupUnit(string path) : this(path, DateTime.Now.ToString("yyyy-MM-dd_h-mm-ss"))
{
}
internal BackupUnit(string path, string name)
{
Name = name;
_BackupPath = new DirectoryInfo(Path.Combine(path, Name));
_ManifestFile = new FileInfo(Path.Combine(_BackupPath.FullName, _ManifestFileName));
}
public static BackupUnit FromDirectory(DirectoryInfo directory, string backupPath)
{
var unit = new BackupUnit(backupPath, directory.Name);
// Read Manifest
if (unit._ManifestFile.Exists)
{
string manifest = File.ReadAllText(unit._ManifestFile.FullName);
foreach (var line in manifest.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
unit._Files.Add(line);
}
else
{
foreach (var file in directory.GetFiles("*", SearchOption.AllDirectories))
{
if (file.Name == _ManifestFileName) continue;
var relativePath = file.FullName.Substring(directory.FullName.Length + 1);
unit._Files.Add(relativePath);
}
}
return unit;
}
public void Add(string file)
{
Add(new FileInfo(file));
}
internal void Delete()
{
if (_BackupPath.Exists)
_BackupPath.Delete(true);
}
/// <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));
Logger.updater.Debug($"rp={relativePath}, bp={backupPath}");
if (_Files.Contains(relativePath))
{
Logger.updater.Debug($"Skipping backup of {relativePath}");
return;
}
// Copy over
backupPath.Directory.Create();
if (file.Exists)
{
file.CopyTo(backupPath.FullName);
}
else
{
// Make empty file
backupPath.Create().Close();
}
if (!File.Exists(_ManifestFile.FullName))
_ManifestFile.Create().Close();
var stream = _ManifestFile.AppendText();
stream.WriteLine(relativePath);
stream.Close();
// Add to list
_Files.Add(relativePath);
}
/// <summary>
/// Reverts the changes made in this unit.
/// </summary>
public void Restore()
{
foreach (var relativePath in _Files)
{
Logger.updater.Debug($"Restoring {relativePath}");
// Original version
var backupFile = new FileInfo(Path.Combine(_BackupPath.FullName, relativePath));
var target = new FileInfo(Path.Combine(Environment.CurrentDirectory, relativePath));
if (backupFile.Exists)
{
if (backupFile.Length > 0)
{
Logger.updater.Debug($" {backupFile.FullName} => {target.FullName}");
target.Directory.Create();
backupFile.CopyTo(target.FullName, true);
}
else
{
Logger.updater.Debug($" x {target.FullName}");
if (target.Exists)
{
target.Delete();
}
}
}
else
{
Logger.updater.Error("Backup not found!");
}
}
}
}
}

+ 9
- 13
IPA.Loader/Updating/Converters/ModsaberDependencyConverter.cs View File

@ -1,29 +1,25 @@
using IPA.Updating.ModsaberML;
using System;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static IPA.Updating.ModsaberML.ApiEndpoint.Mod;
using SemVer;
using static IPA.Updating.ModSaber.ApiEndpoint.Mod;
namespace IPA.Updating.Converters
{
internal class ModsaberDependencyConverter : JsonConverter<Dependency>
internal class ModSaberDependencyConverter : JsonConverter<Dependency>
{
public override Dependency ReadJson(JsonReader reader, Type objectType, Dependency existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var parts = (reader.Value as string).Split('@');
return new Dependency()
var parts = (reader.Value as string)?.Split('@');
return new Dependency
{
Name = parts[0],
VersionRange = new SemVer.Range(parts[1])
Name = parts?[0],
VersionRange = new Range(parts?[1])
};
}
public override void WriteJson(JsonWriter writer, Dependency value, JsonSerializer serializer)
{
writer.WriteValue($"{value.Name}@{value.VersionRange.ToString()}");
writer.WriteValue($"{value.Name}@{value.VersionRange}");
}
}
}

+ 2
- 4
IPA.Loader/Updating/Converters/SemverRangeConverter.cs View File

@ -1,13 +1,11 @@
using Newtonsoft.Json;
using SemVer;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics.CodeAnalysis;
namespace IPA.Updating.Converters
{
[SuppressMessage("ReSharper", "UnusedMember.Global")]
internal class SemverRangeConverter : JsonConverter<Range>
{
public override Range ReadJson(JsonReader reader, Type objectType, Range existingValue, bool hasExistingValue, JsonSerializer serializer) => new Range(reader.Value as string);


+ 2
- 7
IPA.Loader/Updating/Converters/SemverVersionConverter.cs View File

@ -1,10 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using SemVer;
using Newtonsoft.Json;
using System;
using Version = SemVer.Version;
namespace IPA.Updating.Converters


IPA.Loader/Updating/ModsaberML/ApiEndpoint.cs → IPA.Loader/Updating/ModSaber/ApiEndpoint.cs View File

@ -1,18 +1,13 @@
using IPA.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using IPA.Updating.Converters;
using IPA.Utilities;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using SemVer;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Version = SemVer.Version;
namespace IPA.Updating.ModsaberML
namespace IPA.Updating.ModSaber
{
class ApiEndpoint
{
@ -100,7 +95,7 @@ namespace IPA.Updating.ModsaberML
public Dictionary<string, byte[]> FileHashes = new Dictionary<string, byte[]>();
[JsonProperty("url")]
public string DownloadPath = null;
public string DownloadPath;
public override string ToString()
{
@ -112,14 +107,14 @@ namespace IPA.Updating.ModsaberML
public class FilesObject
{
[JsonProperty("steam")]
public PlatformFile Steam = null;
public PlatformFile Steam;
[JsonProperty("oculus")]
public PlatformFile Oculus = null;
public PlatformFile Oculus;
}
[JsonProperty("files")]
public FilesObject Files = null;
public FilesObject Files;
public class Dependency
{
@ -127,10 +122,10 @@ namespace IPA.Updating.ModsaberML
public Range VersionRange = null;
}
[JsonProperty("dependsOn", ItemConverterType = typeof(ModsaberDependencyConverter))]
[JsonProperty("dependsOn", ItemConverterType = typeof(ModSaberDependencyConverter))]
public Dependency[] Dependencies = new Dependency[0];
[JsonProperty("conflictsWith", ItemConverterType = typeof(ModsaberDependencyConverter))]
[JsonProperty("conflictsWith", ItemConverterType = typeof(ModSaberDependencyConverter))]
public Dependency[] Conflicts = new Dependency[0];
[JsonProperty("oldVersions", ItemConverterType = typeof(SemverVersionConverter))]

IPA.Loader/Updating/ModsaberML/Updater.cs → IPA.Loader/Updating/ModSaber/Updater.cs View File

@ -1,43 +1,40 @@
using IPA.Utilities;
using IPA.Loader;
using Ionic.Zip;
using Newtonsoft.Json;
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Ionic.Zip;
using IPA.Utilities;
using Newtonsoft.Json;
using SemVer;
using UnityEngine;
using UnityEngine.Networking;
using SemVer;
using static IPA.Loader.PluginManager;
using Logger = IPA.Logging.Logger;
using Version = SemVer.Version;
using IPA.Updating.Backup;
using System.Runtime.Serialization;
using System.Reflection;
using static IPA.Loader.PluginManager;
namespace IPA.Updating.ModsaberML
namespace IPA.Updating.ModSaber
{
class Updater : MonoBehaviour
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
internal class Updater : MonoBehaviour
{
public static Updater instance;
public static Updater Instance;
public void Awake()
{
try
{
if (instance != null)
if (Instance != null)
Destroy(this);
else
{
instance = this;
Instance = this;
CheckForUpdates();
}
}
@ -55,21 +52,21 @@ namespace IPA.Updating.ModsaberML
private class DependencyObject
{
public string Name { get; set; }
public Version Version { get; set; } = null;
public Version ResolvedVersion { get; set; } = null;
public Range Requirement { get; set; } = null;
public Range Conflicts { get; set; } = null;
public bool Resolved { get; set; } = false;
public bool Has { get; set; } = false;
public Version Version { get; set; }
public Version ResolvedVersion { get; set; }
public Range Requirement { get; set; }
public Range Conflicts { get; set; }
public bool Resolved { get; set; }
public bool Has { get; set; }
public HashSet<string> Consumers { get; set; } = new HashSet<string>();
public bool MetaRequestFailed { get; set; } = false;
public bool MetaRequestFailed { get; set; }
public PluginInfo LocalPluginMeta { get; set; } = null;
public PluginInfo LocalPluginMeta { get; set; }
public override string ToString()
{
return $"{Name}@{Version}{(Resolved ? $" -> {ResolvedVersion}" : "")} - ({Requirement} ! {Conflicts}) {(Has ? $" Already have" : "")}";
return $"{Name}@{Version}{(Resolved ? $" -> {ResolvedVersion}" : "")} - ({Requirement} ! {Conflicts}) {(Has ? " Already have" : "")}";
}
}
@ -79,7 +76,6 @@ namespace IPA.Updating.ModsaberML
if (requestCache.TryGetValue(url, out string value))
{
result.Value = value;
yield break;
}
else
{
@ -111,15 +107,14 @@ namespace IPA.Updating.ModsaberML
}
}
private Dictionary<string, ApiEndpoint.Mod> modCache = new Dictionary<string, ApiEndpoint.Mod>();
private IEnumerator GetModInfo(string name, string ver, Ref<ApiEndpoint.Mod> result)
private readonly Dictionary<string, ApiEndpoint.Mod> modCache = new Dictionary<string, ApiEndpoint.Mod>();
private IEnumerator GetModInfo(string modName, string ver, Ref<ApiEndpoint.Mod> result)
{
var uri = string.Format(ApiEndpoint.GetModInfoEndpoint, Uri.EscapeUriString(name), Uri.EscapeUriString(ver));
var uri = string.Format(ApiEndpoint.GetModInfoEndpoint, Uri.EscapeUriString(modName), Uri.EscapeUriString(ver));
if (modCache.TryGetValue(uri, out ApiEndpoint.Mod value))
{
result.Value = value;
yield break;
}
else
{
@ -136,20 +131,18 @@ namespace IPA.Updating.ModsaberML
catch (Exception e)
{
result.Error = new Exception("Error decoding response", e);
yield break;
}
}
}
private Dictionary<string, List<ApiEndpoint.Mod>> modVersionsCache = new Dictionary<string, List<ApiEndpoint.Mod>>();
private IEnumerator GetModVersionsMatching(string name, string range, Ref<List<ApiEndpoint.Mod>> result)
private readonly Dictionary<string, List<ApiEndpoint.Mod>> modVersionsCache = new Dictionary<string, List<ApiEndpoint.Mod>>();
private IEnumerator GetModVersionsMatching(string modName, string range, Ref<List<ApiEndpoint.Mod>> result)
{
var uri = string.Format(ApiEndpoint.GetModsWithSemver, Uri.EscapeUriString(name), Uri.EscapeUriString(range));
var uri = string.Format(ApiEndpoint.GetModsWithSemver, Uri.EscapeUriString(modName), Uri.EscapeUriString(range));
if (modVersionsCache.TryGetValue(uri, out List<ApiEndpoint.Mod> value))
{
result.Value = value;
yield break;
}
else
{
@ -166,7 +159,6 @@ namespace IPA.Updating.ModsaberML
catch (Exception e)
{
result.Error = new Exception("Error decoding response", e);
yield break;
}
}
}
@ -177,9 +169,9 @@ namespace IPA.Updating.ModsaberML
foreach (var plugin in BSMetas)
{ // initialize with data to resolve (1.1)
if (plugin.ModsaberInfo != null)
if (plugin.ModSaberInfo != null)
{ // updatable
var msinfo = plugin.ModsaberInfo;
var msinfo = plugin.ModSaberInfo;
depList.Value.Add(new DependencyObject {
Name = msinfo.InternalName,
Version = new Version(msinfo.CurrentVersion),
@ -190,17 +182,17 @@ namespace IPA.Updating.ModsaberML
}
foreach (var dep in depList.Value)
Logger.updater.Debug($"Phantom Dependency: {dep.ToString()}");
Logger.updater.Debug($"Phantom Dependency: {dep}");
yield return DependencyResolveFirstPass(depList);
foreach (var dep in depList.Value)
Logger.updater.Debug($"Dependency: {dep.ToString()}");
Logger.updater.Debug($"Dependency: {dep}");
yield return DependencyResolveSecondPass(depList);
foreach (var dep in depList.Value)
Logger.updater.Debug($"Dependency: {dep.ToString()}");
Logger.updater.Debug($"Dependency: {dep}");
DependendyResolveFinalPass(depList);
}
@ -226,8 +218,8 @@ namespace IPA.Updating.ModsaberML
continue;
}
list.Value.AddRange(mod.Value.Dependencies.Select(d => new DependencyObject { Name = d.Name, Requirement = d.VersionRange, Consumers = new HashSet<string>() { dep.Name } }));
list.Value.AddRange(mod.Value.Conflicts.Select(d => new DependencyObject { Name = d.Name, Conflicts = d.VersionRange, Consumers = new HashSet<string>() { dep.Name } }));
list.Value.AddRange(mod.Value.Dependencies.Select(d => new DependencyObject { Name = d.Name, Requirement = d.VersionRange, Consumers = new HashSet<string> { dep.Name } }));
list.Value.AddRange(mod.Value.Conflicts.Select(d => new DependencyObject { Name = d.Name, Conflicts = d.VersionRange, Consumers = new HashSet<string> { dep.Name } }));
}
var depNames = new HashSet<string>();
@ -290,6 +282,7 @@ namespace IPA.Updating.ModsaberML
.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)
// ReSharper disable once AssignmentInConditionalExpression
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
}
@ -303,10 +296,10 @@ namespace IPA.Updating.ModsaberML
{ // figure out which ones need to be downloaded (3.1)
if (dep.Resolved)
{
Logger.updater.Debug($"Resolved: {dep.ToString()}");
Logger.updater.Debug($"Resolved: {dep}");
if (!dep.Has)
{
Logger.updater.Debug($"To Download: {dep.ToString()}");
Logger.updater.Debug($"To Download: {dep}");
toDl.Add(dep);
}
}
@ -324,10 +317,10 @@ namespace IPA.Updating.ModsaberML
Logger.updater.Debug($"Temp directory: {tempDirectory}");
foreach (var item in toDl)
StartCoroutine(UpdateModCoroutine(item, tempDirectory));
StartCoroutine(UpdateModCoroutine(item));
}
private IEnumerator UpdateModCoroutine(DependencyObject item, string tempDirectory)
private IEnumerator UpdateModCoroutine(DependencyObject item)
{ // (3.2)
Logger.updater.Debug($"Release: {BeatSaber.ReleaseType}");
@ -351,12 +344,12 @@ namespace IPA.Updating.ModsaberML
Logger.updater.Debug($"URL = {url}");
const int MaxTries = 3;
int maxTries = MaxTries;
while (maxTries > 0)
const int maxTries = 3;
int tries = maxTries;
while (tries > 0)
{
if (maxTries-- != MaxTries)
Logger.updater.Debug($"Re-trying download...");
if (tries-- != maxTries)
Logger.updater.Debug("Re-trying download...");
using (var stream = new MemoryStream())
using (var request = UnityWebRequest.Get(url))
@ -379,7 +372,7 @@ namespace IPA.Updating.ModsaberML
}
if (request.isHttpError)
{
Logger.updater.Error($"Server returned an error code while trying to update mod");
Logger.updater.Error("Server returned an error code while trying to update mod");
Logger.updater.Error(request.error);
taskTokenSource.Cancel();
continue;
@ -388,16 +381,17 @@ namespace IPA.Updating.ModsaberML
stream.Seek(0, SeekOrigin.Begin); // reset to beginning
var downloadTask = Task.Run(() =>
{ // use slightly more multithreaded approach than coroutines
ExtractPluginAsync(stream, item, platformFile, tempDirectory);
{ // use slightly more multi threaded approach than co-routines
// ReSharper disable once AccessToDisposedClosure
ExtractPluginAsync(stream, item, platformFile);
}, taskTokenSource.Token);
while (!(downloadTask.IsCompleted || downloadTask.IsCanceled || downloadTask.IsFaulted))
yield return null; // pause coroutine until task is done
yield return null; // pause co-routine until task is done
if (downloadTask.IsFaulted)
{
if (downloadTask.Exception.InnerExceptions.Where(e => e is ModsaberInterceptException).Any())
if (downloadTask.Exception != null && downloadTask.Exception.InnerExceptions.Any(e => e is ModsaberInterceptException))
{ // any exception is an intercept exception
Logger.updater.Error($"Modsaber did not return expected data for {item.Name}");
}
@ -411,8 +405,8 @@ namespace IPA.Updating.ModsaberML
}
}
if (maxTries == 0)
Logger.updater.Warn($"Plugin download failed {MaxTries} times, not re-trying");
if (tries == 0)
Logger.updater.Warn($"Plugin download failed {maxTries} times, not re-trying");
else
Logger.updater.Debug("Download complete");
}
@ -421,7 +415,7 @@ namespace IPA.Updating.ModsaberML
{
public MemoryStream Stream { get; set; }
public StreamDownloadHandler(MemoryStream stream) : base()
public StreamDownloadHandler(MemoryStream stream)
{
Stream = stream;
}
@ -437,15 +431,15 @@ namespace IPA.Updating.ModsaberML
Logger.updater.Debug("Download complete");
}
protected override bool ReceiveData(byte[] data, int dataLength)
protected override bool ReceiveData(byte[] rData, int dataLength)
{
if (data == null || data.Length < 1)
if (rData == null || rData.Length < 1)
{
Logger.updater.Debug("CustomWebRequest :: ReceiveData - received a null/empty buffer");
return false;
}
Stream.Write(data, 0, dataLength);
Stream.Write(rData, 0, dataLength);
return true;
}
@ -458,11 +452,11 @@ namespace IPA.Updating.ModsaberML
public override string ToString()
{
return $"{base.ToString()} ({Stream?.ToString()})";
return $"{base.ToString()} ({Stream})";
}
}
private void ExtractPluginAsync(MemoryStream stream, DependencyObject item, ApiEndpoint.Mod.PlatformFile fileInfo, string tempDirectory)
private void ExtractPluginAsync(MemoryStream stream, DependencyObject item, ApiEndpoint.Mod.PlatformFile fileInfo)
{ // (3.3)
Logger.updater.Debug($"Extracting ZIP file for {item.Name}");
@ -472,8 +466,6 @@ namespace IPA.Updating.ModsaberML
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 targetDir = Path.Combine(BeatSaber.InstallPath, "IPA", Path.GetRandomFileName() + "_Pending");
Directory.CreateDirectory(targetDir);
@ -517,7 +509,7 @@ namespace IPA.Updating.ModsaberML
ostream.Seek(0, SeekOrigin.Begin);
FileInfo targetFile = new FileInfo(Path.Combine(targetDir, entry.FileName));
Directory.CreateDirectory(targetFile.DirectoryName);
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
@ -538,7 +530,7 @@ namespace IPA.Updating.ModsaberML
}
if (shouldDeleteOldFile && item.LocalPluginMeta != null)
File.AppendAllLines(Path.Combine(targetDir, _SpecialDeletionsFile), new string[] { LoneFunctions.GetRelativePath(item.LocalPluginMeta.Filename, BeatSaber.InstallPath) });
File.AppendAllLines(Path.Combine(targetDir, SpecialDeletionsFile), new[] { LoneFunctions.GetRelativePath(item.LocalPluginMeta.Filename, BeatSaber.InstallPath) });
}
catch (Exception)
{ // something failed; restore
@ -554,7 +546,7 @@ namespace IPA.Updating.ModsaberML
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));
if (File.Exists(Path.Combine(BeatSaber.InstallPath, SpecialDeletionsFile))) File.Delete(Path.Combine(BeatSaber.InstallPath, SpecialDeletionsFile));
Process.Start(new ProcessStartInfo
{
FileName = item.LocalPluginMeta.Filename,
@ -563,13 +555,13 @@ namespace IPA.Updating.ModsaberML
});
}
else
LoneFunctions.CopyAll(new DirectoryInfo(targetDir), new DirectoryInfo(eventualOutput), _SpecialDeletionsFile);
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";
internal const string SpecialDeletionsFile = "$$delete";
}
[Serializable]

+ 2
- 8
IPA.Loader/Updating/SelfPlugin.cs View File

@ -1,17 +1,11 @@
using IPA;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine.SceneManagement;
using UnityEngine.SceneManagement;
namespace IPA.Updating
{
internal class SelfPlugin : IBeatSaberPlugin
{
internal const string IPA_Name = "Beat Saber IPA";
internal const string IPA_Version = "3.11.1";
internal const string IPA_Version = "3.11.2";
public static SelfPlugin Instance { get; set; } = new SelfPlugin();


+ 8
- 12
IPA.Loader/Utilities/BeatSaber.cs View File

@ -1,10 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SemVer;
using UnityEngine;
using Version = SemVer.Version;
namespace IPA.Utilities
@ -14,11 +10,11 @@ namespace IPA.Utilities
/// </summary>
public static class BeatSaber
{
private static Version _gameVersion = null;
private static Version _gameVersion;
/// <summary>
/// Provides the current game version
/// </summary>
public static Version GameVersion => _gameVersion ?? (_gameVersion = new Version(UnityEngine.Application.version));
public static Version GameVersion => _gameVersion ?? (_gameVersion = new Version(Application.version));
/// <summary>
/// The different types of releases of the game.
@ -34,7 +30,7 @@ namespace IPA.Utilities
/// </summary>
Oculus
}
private static Release? _releaseCache = null;
private static Release? _releaseCache;
/// <summary>
/// Gets the <see cref="Release"/> type of this installation of Beat Saber
/// </summary>
@ -56,11 +52,11 @@ namespace IPA.Utilities
private static bool FindSteamVRAsset()
{
// these require assembly qualified names....
var SteamVRCamera = Type.GetType("SteamVR_Camera, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", false);
var SteamVRExternalCamera = Type.GetType("SteamVR_ExternalCamera, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", false);
var SteamVRFade = Type.GetType("SteamVR_Fade, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", false);
var steamVRCamera = Type.GetType("SteamVR_Camera, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", false);
var steamVRExternalCamera = Type.GetType("SteamVR_ExternalCamera, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", false);
var steamVRFade = Type.GetType("SteamVR_Fade, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", false);
return SteamVRCamera != null && SteamVRExternalCamera != null && SteamVRFade != null;
return steamVRCamera != null && steamVRExternalCamera != null && steamVRFade != null;
}
}
}

+ 1
- 9
IPA.Loader/Utilities/Extensions.cs View File

@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IPA.Utilities
{
@ -18,11 +14,7 @@ namespace IPA.Utilities
/// <returns>the default value of <paramref name="type"/></returns>
public static object GetDefault(this Type type)
{
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
return type.IsValueType ? Activator.CreateInstance(type) : null;
}
}
}

+ 4
- 7
IPA.Loader/Utilities/LoneFunctions.cs View File

@ -1,9 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IPA.Utilities
{
@ -19,9 +16,9 @@ namespace IPA.Utilities
/// <returns>the corresponding byte array</returns>
public static byte[] StringToByteArray(string hex)
{
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
int numberChars = hex.Length;
byte[] bytes = new byte[numberChars / 2];
for (int i = 0; i < numberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
@ -62,7 +59,7 @@ namespace IPA.Utilities
if (*((long*)x1) != *((long*)x2)) return false;
if ((l & 4) != 0) { if (*((int*)x1) != *((int*)x2)) return false; x1 += 4; x2 += 4; }
if ((l & 2) != 0) { if (*((short*)x1) != *((short*)x2)) return false; x1 += 2; x2 += 2; }
if ((l & 1) != 0) if (*((byte*)x1) != *((byte*)x2)) return false;
if ((l & 1) != 0) if (*x1 != *x2) return false;
return true;
}
}


+ 6
- 10
IPA.Loader/Utilities/Ref.cs View File

@ -1,10 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace IPA.Utilities
{
@ -28,7 +24,7 @@ namespace IPA.Utilities
set => _value = value;
}
private Exception _error = null;
private Exception _error;
/// <summary>
/// An exception that was generated while creating the value.
/// </summary>
@ -64,14 +60,14 @@ namespace IPA.Utilities
internal static class ExceptionUtilities
{
private static readonly FieldInfo STACK_TRACE_STRING_FI = typeof(Exception).GetField("_stackTraceString", BindingFlags.NonPublic | BindingFlags.Instance);
private static readonly Type TRACE_FORMAT_TI = Type.GetType("System.Diagnostics.StackTrace").GetNestedType("TraceFormat", BindingFlags.NonPublic);
private static readonly MethodInfo TRACE_TO_STRING_MI = typeof(StackTrace).GetMethod("ToString", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { TRACE_FORMAT_TI }, null);
private static readonly FieldInfo StackTraceStringFi = typeof(Exception).GetField("_stackTraceString", BindingFlags.NonPublic | BindingFlags.Instance);
private static readonly Type TraceFormatTi = Type.GetType("System.Diagnostics.StackTrace")?.GetNestedType("TraceFormat", BindingFlags.NonPublic);
private static readonly MethodInfo TraceToStringMi = typeof(StackTrace).GetMethod("ToString", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { TraceFormatTi }, null);
public static Exception SetStackTrace(this Exception target, StackTrace stack)
{
var getStackTraceString = TRACE_TO_STRING_MI.Invoke(stack, new object[] { Enum.GetValues(TRACE_FORMAT_TI).GetValue(0) });
STACK_TRACE_STRING_FI.SetValue(target, getStackTraceString);
var getStackTraceString = TraceToStringMi.Invoke(stack, new[] { Enum.GetValues(TraceFormatTi).GetValue(0) });
StackTraceStringFi.SetValue(target, getStackTraceString);
return target;
}
}


+ 10
- 19
IPA.Loader/Utilities/ReflectionUtil.cs View File

@ -18,7 +18,7 @@ namespace IPA.Utilities
public static void SetPrivateField(this object obj, string fieldName, object value)
{
var prop = obj.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
prop.SetValue(obj, value);
prop?.SetValue(obj, value);
}
/// <summary>
@ -31,7 +31,7 @@ namespace IPA.Utilities
public static T GetPrivateField<T>(this object obj, string fieldName)
{
var prop = obj.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
var value = prop.GetValue(obj);
var value = prop?.GetValue(obj);
return (T) value;
}
@ -45,7 +45,7 @@ namespace IPA.Utilities
{
var prop = obj.GetType()
.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
prop.SetValue(obj, value, null);
prop?.SetValue(obj, value, null);
}
/// <summary>
@ -58,7 +58,7 @@ namespace IPA.Utilities
public static object InvokePrivateMethod(this object obj, string methodName, params object[] methodParams)
{
MethodInfo dynMethod = obj.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
return dynMethod.Invoke(obj, methodParams);
return dynMethod?.Invoke(obj, methodParams);
}
/// <summary>
@ -91,7 +91,7 @@ namespace IPA.Utilities
while (type != typeof(MonoBehaviour))
{
CopyForType(type, original, copy);
type = type.BaseType;
type = type?.BaseType;
}
return copy;
@ -116,7 +116,7 @@ namespace IPA.Utilities
while (type != typeof(MonoBehaviour))
{
CopyForType(type, original, copy);
type = type.BaseType;
type = type?.BaseType;
}
return copy;
@ -161,7 +161,6 @@ namespace IPA.Utilities
if (type != null)
{
object instance = Activator.CreateInstance(type);
if (instance != null)
{
Type instType = instance.GetType();
MethodInfo methodInfo = instType.GetMethod(function, methodSig);
@ -169,20 +168,12 @@ namespace IPA.Utilities
{
return methodInfo.Invoke(instance, parameters);
}
else
{
throw new Exception("Method not found");
}
}
else
{
throw new Exception("Unable to instantiate object of type");
throw new Exception("Method not found");
}
}
else
{
throw new ArgumentNullException("type");
}
throw new ArgumentNullException(nameof(type));
}
/// <summary>


+ 34
- 39
IPA/Arguments.cs View File

@ -1,23 +1,20 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace IPA.ArgParsing
namespace IPA
{
public class Arguments
{
public static Arguments CmdLine = new Arguments(Environment.GetCommandLineArgs());
public static readonly Arguments CmdLine = new Arguments(Environment.GetCommandLineArgs());
private List<string> positional = new List<string>();
private Dictionary<string, string> longFlags = new Dictionary<string, string>();
private Dictionary<char, string> flags = new Dictionary<char, string>();
private List<ArgumentFlag> flagObjects = new List<ArgumentFlag>();
private readonly List<string> positional = new List<string>();
private readonly Dictionary<string, string> longFlags = new Dictionary<string, string>();
private readonly Dictionary<char, string> flags = new Dictionary<char, string>();
private readonly List<ArgumentFlag> flagObjects = new List<ArgumentFlag>();
private string[] toParse = null;
private string[] toParse;
private Arguments(string[] args)
{
@ -58,10 +55,10 @@ namespace IPA.ArgParsing
{ // parse as flags
var argument = arg.Substring(1); // cut off first char
StringBuilder subBuildState = new StringBuilder();
bool parsingValue = false;
char mainChar = ' ';
foreach (char chr in argument)
var subBuildState = new StringBuilder();
var parsingValue = false;
var mainChar = ' ';
foreach (var chr in argument)
{
if (!parsingValue)
{
@ -100,22 +97,20 @@ namespace IPA.ArgParsing
foreach (var flag in flagObjects)
{
foreach (var chflag in flag.shortFlags)
foreach (var charFlag in flag.ShortFlags)
{
if (flag.exists = HasFlag(chflag))
{
flag.value = GetFlagValue(chflag);
goto FoundValue; // continue to next flagObjects item
}
if (!(flag.exists_ = HasFlag(charFlag))) continue;
flag.value_ = GetFlagValue(charFlag);
goto FoundValue; // continue to next flagObjects item
}
foreach (var lflag in flag.longFlags)
foreach (var longFlag in flag.LongFlags)
{
if (flag.exists = HasLongFlag(lflag))
{
flag.value = GetLongFlagValue(lflag);
goto FoundValue; // continue to next flagObjects item
}
if (!(flag.exists_ = HasLongFlag(longFlag))) continue;
flag.value_ = GetLongFlagValue(longFlag);
goto FoundValue; // continue to next flagObjects item
}
FoundValue:;
@ -145,8 +140,8 @@ namespace IPA.ArgParsing
public void PrintHelp()
{
const string indent = " ";
string filename = Environment.GetCommandLineArgs()[0];
string format = @"usage:
var filename = Environment.GetCommandLineArgs()[0];
const string format = @"usage:
{2}{0} [FLAGS] [ARGUMENTS]
flags:
@ -155,12 +150,12 @@ flags:
foreach (var flag in flagObjects)
{
flagsBuilder.AppendFormat("{2}{0}{3}{1}",
string.Join(", ", flag.shortFlags.Select(s => $"-{s}").Concat( flag.longFlags.Select(s => $"--{s}")) ),
string.Join(", ", flag.ShortFlags.Select(s => $"-{s}").Concat( flag.LongFlags.Select(s => $"--{s}")) ),
Environment.NewLine, indent, flag.ValueString != null ? "=" + flag.ValueString : "");
flagsBuilder.AppendFormat("{2}{2}{0}{1}", flag.DocString, Environment.NewLine, indent);
}
Console.Write(string.Format(format, filename, flagsBuilder.ToString(), indent));
Console.Write(format, filename, flagsBuilder, indent);
}
public IReadOnlyList<string> PositionalArgs => positional;
@ -168,11 +163,11 @@ flags:
public class ArgumentFlag
{
internal List<char> shortFlags = new List<char>();
internal List<string> longFlags = new List<string>();
internal readonly List<char> ShortFlags = new List<char>();
internal readonly List<string> LongFlags = new List<string>();
internal string value = null;
internal bool exists = false;
internal string value_;
internal bool exists_;
public ArgumentFlag(params string[] flags)
{
@ -183,18 +178,18 @@ flags:
private void AddPart(string flagPart)
{
if (flagPart.StartsWith("--"))
longFlags.Add(flagPart.Substring(2));
LongFlags.Add(flagPart.Substring(2));
else if (flagPart.StartsWith("-"))
shortFlags.Add(flagPart[1]);
ShortFlags.Add(flagPart[1]);
}
public bool Exists => exists;
public string Value => value;
public bool Exists => exists_;
public string Value => value_;
public bool HasValue => Exists && Value != null;
public string DocString { get; set; } = "";
public string ValueString { get; set; } = null;
public string ValueString { get; set; }
public static implicit operator bool(ArgumentFlag f)
{


+ 4
- 7
IPA/PatchContext.cs View File

@ -1,9 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
namespace IPA
{
@ -37,12 +34,12 @@ namespace IPA
{
Executable = exe
};
context.ProjectRoot = new FileInfo(context.Executable).Directory.FullName;
context.IPARoot = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "IPA");
context.IPA = Assembly.GetExecutingAssembly()?.Location ?? Path.Combine(context.ProjectRoot, "IPA.exe");
context.ProjectRoot = new FileInfo(context.Executable).Directory?.FullName;
context.IPARoot = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) ?? throw new InvalidOperationException(), "IPA");
context.IPA = Assembly.GetExecutingAssembly().Location;
context.DataPathSrc = Path.Combine(context.IPARoot, "Data");
context.LibsPathSrc = Path.Combine(context.IPARoot, "Libs");
context.PluginsFolder = Path.Combine(context.ProjectRoot, "Plugins");
context.PluginsFolder = Path.Combine(context.ProjectRoot ?? throw new InvalidOperationException(), "Plugins");
context.ProjectName = Path.GetFileNameWithoutExtension(context.Executable);
context.DataPathDst = Path.Combine(context.ProjectRoot, context.ProjectName + "_Data");
context.LibsPathDst = Path.Combine(context.ProjectRoot, "Libs");


+ 7
- 12
IPA/Patcher/BackupManager.cs View File

@ -1,13 +1,10 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace IPA.Patcher
{
public class BackupManager
public static class BackupManager
{
public static BackupUnit FindLatestBackup(PatchContext context)
{
@ -42,7 +39,7 @@ namespace IPA.Patcher
if (string.IsNullOrEmpty(dir))
throw new ArgumentException(
"Starting directory is a null reference or an empty string",
"dir");
nameof(dir));
try
{
@ -53,15 +50,13 @@ namespace IPA.Patcher
var entries = Directory.EnumerateFileSystemEntries(dir);
if (!entries.Any())
if (entries.Any()) return;
try
{
try
{
Directory.Delete(dir);
}
catch (UnauthorizedAccessException) { }
catch (DirectoryNotFoundException) { }
Directory.Delete(dir);
}
catch (UnauthorizedAccessException) { }
catch (DirectoryNotFoundException) { }
}
catch (UnauthorizedAccessException) { }
}


+ 28
- 31
IPA/Patcher/BackupUnit.cs View File

@ -1,9 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Text;
namespace IPA.Patcher
{
@ -12,12 +9,12 @@ namespace IPA.Patcher
/// </summary>
public class BackupUnit
{
public string Name { get; private set; }
private string Name { get; }
private DirectoryInfo _BackupPath;
private PatchContext _Context;
private List<string> _Files = new List<string>();
private FileInfo _ManifestFile;
private readonly DirectoryInfo _backupPath;
private readonly PatchContext _context;
private readonly List<string> _files = new List<string>();
private readonly FileInfo _manifestFile;
private static string _ManifestFileName = "$manifest$.txt";
public BackupUnit(PatchContext context) : this(context, DateTime.Now.ToString("yyyy-MM-dd_h-mm-ss"))
@ -27,9 +24,9 @@ namespace IPA.Patcher
private BackupUnit(PatchContext context, string name)
{
Name = name;
_Context = context;
_BackupPath = new DirectoryInfo(Path.Combine(_Context.BackupPath, Name));
_ManifestFile = new FileInfo(Path.Combine(_BackupPath.FullName, _ManifestFileName));
_context = context;
_backupPath = new DirectoryInfo(Path.Combine(_context.BackupPath, Name));
_manifestFile = new FileInfo(Path.Combine(_backupPath.FullName, _ManifestFileName));
}
public static BackupUnit FromDirectory(DirectoryInfo directory, PatchContext context)
@ -37,11 +34,11 @@ namespace IPA.Patcher
var unit = new BackupUnit(context, directory.Name);
// Read Manifest
if (unit._ManifestFile.Exists)
if (unit._manifestFile.Exists)
{
string manifest = File.ReadAllText(unit._ManifestFile.FullName);
foreach (var line in manifest.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
unit._Files.Add(line);
var manifest = File.ReadAllText(unit._manifestFile.FullName);
foreach (var line in manifest.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
unit._files.Add(line);
}
else
{
@ -49,7 +46,7 @@ namespace IPA.Patcher
{
if (file.Name == _ManifestFileName) continue;
var relativePath = file.FullName.Substring(directory.FullName.Length + 1);
unit._Files.Add(relativePath);
unit._files.Add(relativePath);
}
}
@ -63,32 +60,32 @@ namespace IPA.Patcher
internal void Delete()
{
_BackupPath.Delete(true);
_backupPath.Delete(true);
}
/// <summary>
/// Adds a file to the list of changed files and backups it.
/// </summary>
/// <param name="path"></param>
/// <param name="file">the file to add</param>
public void Add(FileInfo file)
{
if(!file.FullName.StartsWith(_Context.ProjectRoot))
if(!file.FullName.StartsWith(_context.ProjectRoot))
{
Console.Error.WriteLine("Invalid file path for backup! {0}", file);
return;
}
var relativePath = file.FullName.Substring(_Context.ProjectRoot.Length + 1);
var backupPath = new FileInfo(Path.Combine(_BackupPath.FullName, relativePath));
var relativePath = file.FullName.Substring(_context.ProjectRoot.Length + 1);
var backupPath = new FileInfo(Path.Combine(_backupPath.FullName, relativePath));
if(_Files.Contains(relativePath))
if(_files.Contains(relativePath))
{
Console.WriteLine("Skipping backup of {0}", relativePath);
return;
}
// Copy over
backupPath.Directory.Create();
backupPath.Directory?.Create();
if (file.Exists)
{
file.CopyTo(backupPath.FullName);
@ -99,14 +96,14 @@ namespace IPA.Patcher
backupPath.Create().Close();
}
if (!File.Exists(_ManifestFile.FullName))
_ManifestFile.Create().Close();
var stream = _ManifestFile.AppendText();
if (!File.Exists(_manifestFile.FullName))
_manifestFile.Create().Close();
var stream = _manifestFile.AppendText();
stream.WriteLine(relativePath);
stream.Close();
// Add to list
_Files.Add(relativePath);
_files.Add(relativePath);
}
/// <summary>
@ -114,19 +111,19 @@ namespace IPA.Patcher
/// </summary>
public void Restore()
{
foreach(var relativePath in _Files)
foreach(var relativePath in _files)
{
Console.WriteLine("Restoring {0}", relativePath);
// Original version
var backupFile = new FileInfo(Path.Combine(_BackupPath.FullName, relativePath));
var target = new FileInfo(Path.Combine(_Context.ProjectRoot, relativePath));
var backupFile = new FileInfo(Path.Combine(_backupPath.FullName, relativePath));
var target = new FileInfo(Path.Combine(_context.ProjectRoot, relativePath));
if (backupFile.Exists)
{
if (backupFile.Length > 0)
{
Console.WriteLine(" {0} => {1}", backupFile.FullName, target.FullName);
target.Directory.Create();
target.Directory?.Create();
backupFile.CopyTo(target.FullName, true);
}
else


+ 32
- 27
IPA/Patcher/Patcher.cs View File

@ -4,16 +4,15 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace IPA.Patcher
{
class PatchedModule
internal class PatchedModule
{
private static readonly string[] ENTRY_TYPES = { "Input", "Display" };
private static readonly string[] EntryTypes = { "Input", "Display" };
private FileInfo _File;
private ModuleDefinition _Module;
private readonly FileInfo _file;
private ModuleDefinition _module;
internal struct PatchData {
public bool IsPatched;
@ -27,7 +26,7 @@ namespace IPA.Patcher
private PatchedModule(string engineFile)
{
_File = new FileInfo(engineFile);
_file = new FileInfo(engineFile);
LoadModules();
}
@ -35,27 +34,33 @@ namespace IPA.Patcher
private void LoadModules()
{
var resolver = new DefaultAssemblyResolver();
resolver.AddSearchDirectory(_File.DirectoryName);
resolver.AddSearchDirectory(_file.DirectoryName);
var parameters = new ReaderParameters
{
AssemblyResolver = resolver,
};
_Module = ModuleDefinition.ReadModule(_File.FullName, parameters);
_module = ModuleDefinition.ReadModule(_file.FullName, parameters);
}
public PatchData Data
{
get
{
var IIdata = new PatchData { IsPatched = false, Version = null };
foreach (var @ref in _Module.AssemblyReferences) {
if (@ref.Name == "IllusionInjector") IIdata = new PatchData { IsPatched = true, Version = new Version(0, 0, 0, 0) };
if (@ref.Name == "IllusionPlugin") IIdata = new PatchData { IsPatched = true, Version = new Version(0, 0, 0, 0) };
if (@ref.Name == "IPA.Injector") return new PatchData { IsPatched = true, Version = @ref.Version };
var data = new PatchData { IsPatched = false, Version = null };
foreach (var @ref in _module.AssemblyReferences) {
switch (@ref.Name)
{
case "IllusionInjector":
case "IllusionPlugin":
data = new PatchData { IsPatched = true, Version = new Version(0, 0, 0, 0) };
break;
case "IPA.Injector":
return new PatchData { IsPatched = true, Version = @ref.Version };
}
}
return IIdata;
return data;
}
}
@ -63,26 +68,26 @@ namespace IPA.Patcher
{
// First, let's add the reference
var nameReference = new AssemblyNameReference("IPA.Injector", v);
var injectorPath = Path.Combine(_File.DirectoryName, "IPA.Injector.dll");
var injectorPath = Path.Combine(_file.DirectoryName ?? throw new InvalidOperationException(), "IPA.Injector.dll");
var injector = ModuleDefinition.ReadModule(injectorPath);
bool hasIPAInjector = false;
for (int i = 0; i < _Module.AssemblyReferences.Count; i++)
for (int i = 0; i < _module.AssemblyReferences.Count; i++)
{
if (_Module.AssemblyReferences[i].Name == "IllusionInjector")
_Module.AssemblyReferences.RemoveAt(i--);
if (_Module.AssemblyReferences[i].Name == "IllusionPlugin")
_Module.AssemblyReferences.RemoveAt(i--);
if (_Module.AssemblyReferences[i].Name == "IPA.Injector")
if (_module.AssemblyReferences[i].Name == "IllusionInjector")
_module.AssemblyReferences.RemoveAt(i--);
if (_module.AssemblyReferences[i].Name == "IllusionPlugin")
_module.AssemblyReferences.RemoveAt(i--);
if (_module.AssemblyReferences[i].Name == "IPA.Injector")
{
hasIPAInjector = true;
_Module.AssemblyReferences[i].Version = v;
_module.AssemblyReferences[i].Version = v;
}
}
if (!hasIPAInjector)
{
_Module.AssemblyReferences.Add(nameReference);
_module.AssemblyReferences.Add(nameReference);
int patched = 0;
foreach (var type in FindEntryTypes())
@ -95,7 +100,7 @@ namespace IPA.Patcher
if (patched > 0)
{
_Module.Write(_File.FullName);
_module.Write(_file.FullName);
}
else
{
@ -104,7 +109,7 @@ namespace IPA.Patcher
}
else
{
_Module.Write(_File.FullName);
_module.Write(_file.FullName);
}
}
@ -113,7 +118,7 @@ namespace IPA.Patcher
var targetMethod = targetType.Methods.FirstOrDefault(m => m.IsConstructor && m.IsStatic);
if (targetMethod != null)
{
var methodReference = _Module.Import(injector.GetType("IPA.Injector.Injector").Methods.First(m => m.Name == "Inject"));
var methodReference = _module.Import(injector.GetType("IPA.Injector.Injector").Methods.First(m => m.Name == "Inject"));
targetMethod.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Call, methodReference));
return true;
}
@ -123,7 +128,7 @@ namespace IPA.Patcher
private IEnumerable<TypeDefinition> FindEntryTypes()
{
return _Module.GetTypes().Where(m => ENTRY_TYPES.Contains(m.Name));
return _module.GetTypes().Where(m => EntryTypes.Contains(m.Name));
}
}
}

+ 11
- 15
IPA/Patcher/Virtualizer.cs View File

@ -1,18 +1,14 @@
using Mono.Cecil;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace IPA.Patcher
{
class VirtualizedModule
{
private const string ENTRY_TYPE = "Display";
private FileInfo _File;
private ModuleDefinition _Module;
private readonly FileInfo _file;
private ModuleDefinition _module;
public static VirtualizedModule Load(string engineFile)
{
@ -21,7 +17,7 @@ namespace IPA.Patcher
private VirtualizedModule(string assemblyFile)
{
_File = new FileInfo(assemblyFile);
_file = new FileInfo(assemblyFile);
LoadModules();
}
@ -29,30 +25,29 @@ namespace IPA.Patcher
private void LoadModules()
{
var resolver = new DefaultAssemblyResolver();
resolver.AddSearchDirectory(_File.DirectoryName);
resolver.AddSearchDirectory(_file.DirectoryName);
var parameters = new ReaderParameters
{
AssemblyResolver = resolver,
};
_Module = ModuleDefinition.ReadModule(_File.FullName, parameters);
_module = ModuleDefinition.ReadModule(_file.FullName, parameters);
}
/// <summary>
///
/// </summary>
/// <param name="module"></param>
public void Virtualize()
{
foreach (var type in _Module.Types)
foreach (var type in _module.Types)
{
VirtualizeType(type);
}
Console.WriteLine();
_Module.Write(_File.FullName);
_module.Write(_file.FullName);
}
private void VirtualizeType(TypeDefinition type)
@ -110,10 +105,11 @@ namespace IPA.Patcher
{
get
{
var awakeMethods = _Module.GetTypes().SelectMany(t => t.Methods.Where(m => m.Name == "Awake"));
if (awakeMethods.Count() == 0) return false;
var awakeMethods = _module.GetTypes().SelectMany(t => t.Methods.Where(m => m.Name == "Awake"));
var methodDefinitions = awakeMethods as MethodDefinition[] ?? awakeMethods.ToArray();
if (!methodDefinitions.Any()) return false;
return ((float)awakeMethods.Count(m => m.IsVirtual) / awakeMethods.Count()) > 0.5f;
return ((float)methodDefinitions.Count(m => m.IsVirtual) / methodDefinitions.Count()) > 0.5f;
}
}
}


+ 50
- 50
IPA/Program.cs View File

@ -1,20 +1,21 @@
using IPA.Patcher;
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using IPA.ArgParsing;
using IPA.Patcher;
namespace IPA
{
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
public class Program
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum Architecture
{
x86,
@ -24,17 +25,17 @@ namespace IPA
public static Version Version => Assembly.GetEntryAssembly().GetName().Version;
public static ArgumentFlag ArgHelp = new ArgumentFlag("--help", "-h") { DocString = "prints this message" };
public static ArgumentFlag ArgWaitFor = new ArgumentFlag("--waitfor", "-w") { DocString = "waits for the specified PID to exit", ValueString = "PID" };
public static ArgumentFlag ArgForce = new ArgumentFlag("--force", "-f") { DocString = "forces the operation to go through" };
public static ArgumentFlag ArgRevert = new ArgumentFlag("--revert", "-r") { DocString = "reverts the IPA installation" };
public static ArgumentFlag ArgNoWait = new ArgumentFlag("--nowait", "-n") { DocString = "doesn't wait for user input after the operation" };
public static ArgumentFlag ArgStart = new ArgumentFlag("--start", "-s") { DocString = "uses value as arguments to start the game after the patch/unpatch", ValueString = "ARGUMENTS" };
public static ArgumentFlag ArgLaunch = new ArgumentFlag("--launch", "-l") { DocString = "uses positional parameters as arguments to start the game after patch/unpatch" };
public static ArgumentFlag ArgDestructive = new ArgumentFlag("--destructive", "-d") { DocString = "patches the game using the now outdated destructive methods" };
public static readonly ArgumentFlag ArgHelp = new ArgumentFlag("--help", "-h") { DocString = "prints this message" };
public static readonly ArgumentFlag ArgWaitFor = new ArgumentFlag("--waitfor", "-w") { DocString = "waits for the specified PID to exit", ValueString = "PID" };
public static readonly ArgumentFlag ArgForce = new ArgumentFlag("--force", "-f") { DocString = "forces the operation to go through" };
public static readonly ArgumentFlag ArgRevert = new ArgumentFlag("--revert", "-r") { DocString = "reverts the IPA installation" };
public static readonly ArgumentFlag ArgNoWait = new ArgumentFlag("--nowait", "-n") { DocString = "doesn't wait for user input after the operation" };
public static readonly ArgumentFlag ArgStart = new ArgumentFlag("--start", "-s") { DocString = "uses value_ as arguments to start the game after the patch/unpatch", ValueString = "ARGUMENTS" };
public static readonly ArgumentFlag ArgLaunch = new ArgumentFlag("--launch", "-l") { DocString = "uses positional parameters as arguments to start the game after patch/unpatch" };
public static readonly ArgumentFlag ArgDestructive = new ArgumentFlag("--destructive", "-d") { DocString = "patches the game using the now outdated destructive methods" };
[STAThread]
static void Main(string[] args)
public static void Main(string[] args)
{
Arguments.CmdLine.Flags(ArgHelp, ArgWaitFor, ArgForce, ArgRevert, ArgNoWait, ArgStart, ArgLaunch, ArgDestructive).Process();
@ -48,7 +49,7 @@ namespace IPA
{
if (ArgWaitFor.HasValue)
{ // wait for process if necessary
int pid = int.Parse(ArgWaitFor.Value);
var pid = int.Parse(ArgWaitFor.Value);
try
{ // wait for beat saber to exit (ensures we can modify the file)
@ -58,20 +59,26 @@ namespace IPA
parent.WaitForExit();
}
catch (Exception) { }
catch (Exception)
{
// ignored
}
}
PatchContext context = null;
Assembly AssemblyLibLoader(object source, ResolveEventArgs e)
{
// ReSharper disable AccessToModifiedClosure
if (context == null) return null;
var libsDir = context.LibsPathSrc;
// ReSharper enable AccessToModifiedClosure
var asmName = new AssemblyName(e.Name);
var testFilen = Path.Combine(libsDir, $"{asmName.Name}.{asmName.Version}.dll");
var testFile = Path.Combine(libsDir, $"{asmName.Name}.{asmName.Version}.dll");
if (File.Exists(testFilen))
return Assembly.LoadFile(testFilen);
if (File.Exists(testFile))
return Assembly.LoadFile(testFile);
Console.WriteLine($"Could not load library {asmName}");
@ -369,11 +376,6 @@ namespace IPA
yield return new FileInfo(Path.Combine(nativePluginFolder.FullName,
relevantBit.Substring("x86_64".Length + 1)));
}
else
{
// Throw away
yield break;
}
}
else if (!isFlat && isFileFlat)
{
@ -416,34 +418,35 @@ namespace IPA
}
// Copy each file into the new directory.
foreach (FileInfo fi in source.GetFiles())
foreach (var fi in source.GetFiles())
{
foreach (var targetFile in interceptor(fi, new FileInfo(Path.Combine(target.FullName, fi.Name))))
{
if (!targetFile.Exists || targetFile.LastWriteTimeUtc < fi.LastWriteTimeUtc || aggressive)
{
targetFile.Directory.Create();
if (targetFile.Exists && targetFile.LastWriteTimeUtc >= fi.LastWriteTimeUtc && !aggressive)
continue;
Console.CursorTop--;
ClearLine();
Console.WriteLine(@"Copying {0}", targetFile.FullName);
backup.Add(targetFile);
fi.CopyTo(targetFile.FullName, true);
}
Debug.Assert(targetFile.Directory != null, "targetFile.Directory != null");
targetFile.Directory.Create();
Console.CursorTop--;
ClearLine();
Console.WriteLine(@"Copying {0}", targetFile.FullName);
backup.Add(targetFile);
fi.CopyTo(targetFile.FullName, true);
}
}
// Copy each subdirectory using recursion.
if (recurse)
foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
{
DirectoryInfo nextTargetSubDir = new DirectoryInfo(Path.Combine(target.FullName, diSourceSubDir.Name));
CopyAll(diSourceSubDir, nextTargetSubDir, aggressive, backup, interceptor, recurse);
}
if (!recurse) return;
foreach (var diSourceSubDir in source.GetDirectories())
{
var nextTargetSubDir = new DirectoryInfo(Path.Combine(target.FullName, diSourceSubDir.Name));
CopyAll(diSourceSubDir, nextTargetSubDir, aggressive, backup, interceptor);
}
}
static void Fail(string message)
private static void Fail(string message)
{
Console.Error.WriteLine("ERROR: " + message);
@ -460,8 +463,8 @@ namespace IPA
/// <summary>
/// Encodes an argument for passing into a program
/// </summary>
/// <param name="original">The value that should be received by the program</param>
/// <returns>The value which needs to be passed to the program for the original value
/// <param name="original">The value_ that should be received by the program</param>
/// <returns>The value_ which needs to be passed to the program for the original value_
/// to come through</returns>
public static string EncodeParameterArgument(string original)
{
@ -487,18 +490,15 @@ namespace IPA
if (machine == 0x8664) // IMAGE_FILE_MACHINE_AMD64
return Architecture.x64;
else if (machine == 0x014c) // IMAGE_FILE_MACHINE_I386
if (machine == 0x014c) // IMAGE_FILE_MACHINE_I386
return Architecture.x86;
else if (machine == 0x0200) // IMAGE_FILE_MACHINE_IA64
if (machine == 0x0200) // IMAGE_FILE_MACHINE_IA64
return Architecture.x64;
else
return Architecture.Unknown;
}
else
{
// Not a supported binary
return Architecture.Unknown;
}
// Not a supported binary
return Architecture.Unknown;
}
}


+ 2
- 3
IPA/Properties/AssemblyInfo.cs View File

@ -1,5 +1,4 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@ -32,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("3.11.1")] // will be patched by AppVeyor
[assembly: AssemblyFileVersion("3.11.1")] // will be patched by AppVeyor
[assembly: AssemblyVersion("3.11.2")]
[assembly: AssemblyFileVersion("3.11.2")]

+ 8
- 7
IPA/Shortcut.cs View File

@ -1,14 +1,15 @@
using System;
using System.Reflection;
using System.Runtime.InteropServices;
namespace IPA
{
public class Shortcut
public static class Shortcut
{
private static Type m_type = Type.GetTypeFromProgID("WScript.Shell");
private static object m_shell = Activator.CreateInstance(m_type);
private static readonly Type MType = Type.GetTypeFromProgID("WScript.Shell");
private static readonly object MShell = Activator.CreateInstance(MType);
[ComImport, TypeLibType((short)0x1040), Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B")]
[ComImport, TypeLibType(0x1040), Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B")]
private interface IWshShortcut
{
[DispId(0)]
@ -38,8 +39,8 @@ namespace IPA
[DispId(0x3ef)]
string WorkingDirectory { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0x3ef)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3ef)] set; }
[TypeLibFunc((short)0x40), DispId(0x7d0)]
void Load([In, MarshalAs(UnmanagedType.BStr)] string PathLink);
[TypeLibFunc(0x40), DispId(0x7d0)]
void Load([In, MarshalAs(UnmanagedType.BStr)] string pathLink);
[DispId(0x7d1)]
void Save();
@ -47,7 +48,7 @@ namespace IPA
public static void Create(string fileName, string targetPath, string arguments, string workingDirectory, string description, string hotkey, string iconPath)
{
IWshShortcut shortcut = (IWshShortcut)m_type.InvokeMember("CreateShortcut", System.Reflection.BindingFlags.InvokeMethod, null, m_shell, new object[] { fileName });
IWshShortcut shortcut = (IWshShortcut)MType.InvokeMember("CreateShortcut", BindingFlags.InvokeMethod, null, MShell, new object[] { fileName });
shortcut.Description = description;
shortcut.Hotkey = hotkey;
shortcut.TargetPath = targetPath;


+ 6
- 13
MSBuildTasks/AssemblyRenameTask.cs View File

@ -1,24 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mono.Cecil;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using Mono.Cecil;
using System;
using System.IO;
namespace MSBuildTasks
{
public class AssemblyRename : Task
{
private ITaskItem[] assemblies;
[Required]
public ITaskItem[] Assemblies
{
get => assemblies;
set => assemblies = value;
}
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public ITaskItem[] Assemblies { get; set; }
public override bool Execute()
{
@ -47,7 +40,7 @@ namespace MSBuildTasks
var name = asmName.Name;
var version = asmName.Version;
var newFilen = $"{name}.{version}.dll";
var newFilePath = Path.Combine(Path.GetDirectoryName(assembly.ItemSpec), newFilen);
var newFilePath = Path.Combine(Path.GetDirectoryName(assembly.ItemSpec) ?? throw new InvalidOperationException(), newFilen);
Log.LogMessage(MessageImportance.Normal, $"Old file: {assembly.ItemSpec}, new file: {newFilePath}");


+ 0
- 1
MSBuildTasks/Properties/AssemblyInfo.cs View File

@ -1,5 +1,4 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following


+ 2
- 2
appveyor.yml View File

@ -1,6 +1,6 @@
version: 'BSIPA-{branch}-{build}'
environment:
bsipa_version: '3.11.1'
bsipa_version: '3.11.2'
pull_requests:
do_not_increment_build_number: true
install:
@ -20,7 +20,7 @@ skip_tags: true
deploy:
- provider: GitHub
release: BSIPA $(bsipa_version)-draft
tag: $(bsipa_version)
tag: $(bsipa_version)-d
description: |
**Build:** [$(bsipa_version)-$(APPVEYOR_BUILD_NUMBER)](https://ci.appveyor.com/project/nike4613/beatsaber-ipa-reloaded/builds/$(APPVEYOR_BUILD_ID)) [$(configuration)|$(platform)]
**Latest Commit:** $(APPVEYOR_REPO_COMMIT)


Loading…
Cancel
Save