299 lines
11 KiB
C#
299 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Windows.Forms;
|
|
using Mono.Cecil;
|
|
using Mono.Cecil.Cil;
|
|
using System.Threading;
|
|
|
|
namespace QuietLauncher
|
|
{
|
|
static class Program
|
|
{
|
|
private static string[] TABOO_NAMES = {
|
|
//"Start",
|
|
//"Update",
|
|
//"Awake",
|
|
//"OnDestroy"
|
|
};
|
|
private static string[] ENTRY_TYPES = { "Display" };
|
|
|
|
/// <summary>
|
|
/// The main entry point for the application.
|
|
/// </summary>
|
|
[STAThread]
|
|
static void Main()
|
|
{
|
|
try {
|
|
var execPath = Application.ExecutablePath;
|
|
var fileName = Path.GetFileNameWithoutExtension(execPath);
|
|
if (fileName.IndexOf("VR") == -1 && fileName.IndexOf("_") == -1) return;
|
|
|
|
bool vrMode = fileName.IndexOf("VR") > 0;
|
|
bool directMode = Application.ExecutablePath.EndsWith("_DirectToRift.exe");
|
|
string baseName = execPath.Substring(0, vrMode
|
|
? execPath.LastIndexOf("VR")
|
|
: execPath.LastIndexOf("_"));
|
|
|
|
string executable = baseName + ".exe";
|
|
var file = new FileInfo(executable);
|
|
if (file.Exists)
|
|
{
|
|
var args = Environment.GetCommandLineArgs().ToList();
|
|
bool created = false;
|
|
|
|
var dataFolder = Path.Combine(file.DirectoryName, Path.GetFileNameWithoutExtension(file.Name) + "_Data");
|
|
var assemblyPath = Path.Combine(Path.Combine(dataFolder, "Managed"), "Assembly-CSharp.dll");
|
|
var enginePath = Path.Combine(Path.Combine(dataFolder, "Managed"), "UnityEngine.dll");
|
|
var directToRiftPath = baseName + "_DirectToRift.exe";
|
|
|
|
try
|
|
{
|
|
if (directMode)
|
|
{
|
|
//args[Array.IndexOf(args, "--direct")] = "-force-d3d11";
|
|
|
|
|
|
if (!File.Exists(directToRiftPath))
|
|
{
|
|
File.WriteAllBytes(directToRiftPath, Resources.DirectToRift);
|
|
created = true;
|
|
}
|
|
|
|
file = new FileInfo(directToRiftPath);
|
|
}
|
|
|
|
|
|
if (vrMode) args.Add("--vr");
|
|
var arguments = string.Join(" ", args.ToArray());
|
|
|
|
try
|
|
{
|
|
Patch(assemblyPath, enginePath);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
|
|
Process.Start(file.FullName, arguments);
|
|
|
|
}
|
|
finally
|
|
{
|
|
if (created && directMode)
|
|
{
|
|
var thread = new Thread(new ThreadStart(delegate
|
|
{
|
|
int attempts = 0;
|
|
while (File.Exists(directToRiftPath) && attempts++ < 20)
|
|
{
|
|
Thread.Sleep(1000);
|
|
try
|
|
{
|
|
File.Delete(directToRiftPath);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
|
|
}
|
|
}
|
|
}));
|
|
thread.Start();
|
|
thread.Join();
|
|
// Clean up
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MessageBox.Show("Could not find: " + file.FullName, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
} catch(Exception globalException) {
|
|
MessageBox.Show(globalException.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
|
|
}
|
|
|
|
static string PrepareBackup(FileInfo file)
|
|
{
|
|
string backup = file.FullName + ".Original";
|
|
|
|
if (File.Exists(backup))
|
|
{
|
|
int i = 1;
|
|
string backupBase = backup;
|
|
while (File.Exists(backup))
|
|
{
|
|
backup = backupBase + i++;
|
|
}
|
|
}
|
|
return backup;
|
|
}
|
|
|
|
static void Patch(string assemblyFile, string engineFile)
|
|
{
|
|
|
|
var input = new FileInfo(assemblyFile);
|
|
var engineInput = new FileInfo(engineFile);
|
|
string assemblyBackup = PrepareBackup(input);
|
|
string engineBackup = PrepareBackup(engineInput);
|
|
|
|
if (!input.Exists) Fail("File does not exist.");
|
|
|
|
|
|
var directory = input.DirectoryName;
|
|
var injectorPath = Path.Combine(directory, "IllusionInjector.dll");
|
|
|
|
if (!File.Exists(injectorPath)) Fail("You're missing IllusionInjector.dll. Please make sure to extract all files correctly.");
|
|
|
|
var resolver = new DefaultAssemblyResolver();
|
|
resolver.AddSearchDirectory(directory);
|
|
|
|
var parameters = new ReaderParameters
|
|
{
|
|
// SymbolReaderProvider = GetSymbolReaderProvider(),
|
|
AssemblyResolver = resolver,
|
|
};
|
|
|
|
var assemblyModule = ModuleDefinition.ReadModule(input.FullName, parameters);
|
|
var engineModule = ModuleDefinition.ReadModule(engineInput.FullName, parameters);
|
|
|
|
if (!IsPatched(engineModule)) //|| !isVirtualized)
|
|
{
|
|
// Make backup
|
|
input.CopyTo(engineBackup);
|
|
// First, let's add the reference
|
|
var nameReference = new AssemblyNameReference("IllusionInjector", new Version(1, 0, 0, 0));
|
|
engineModule.AssemblyReferences.Add(nameReference);
|
|
var targetType = FindEntryType(engineModule);
|
|
|
|
if (targetType == null) Fail("Couldn't find entry class. Aborting.");
|
|
|
|
var awakeMethod = targetType.Methods.FirstOrDefault(m => m.IsConstructor && m.IsStatic);
|
|
if (awakeMethod == null)
|
|
{
|
|
Fail("Couldn't find awake method. Aborting.");
|
|
}
|
|
|
|
var injector = ModuleDefinition.ReadModule(injectorPath);
|
|
var methodReference = engineModule.Import(injector.GetType("IllusionInjector.Injector").Methods.First(m => m.Name == "Inject"));
|
|
//var methodReference = module.GetMemberReferences().FirstOrDefault(r => r.FullName == "IllusionInjector.Injector");
|
|
|
|
awakeMethod.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Call, methodReference));
|
|
engineModule.Write(engineInput.FullName);
|
|
}
|
|
if(!IsVirtualized(assemblyModule))
|
|
{
|
|
input.CopyTo(assemblyBackup);
|
|
Virtualize(assemblyModule);
|
|
assemblyModule.Write(input.FullName);
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// The forbidden deed of the gods -- make ALL methods virtual and public
|
|
/// </summary>
|
|
/// <param name="module"></param>
|
|
private static void Virtualize(ModuleDefinition module)
|
|
{
|
|
foreach (var type in module.Types)
|
|
{
|
|
VirtualizeType(type);
|
|
}
|
|
}
|
|
|
|
private static void VirtualizeType(TypeDefinition type)
|
|
{
|
|
if (type.IsSealed) return;
|
|
if (type.IsInterface) return;
|
|
if (type.IsAbstract) return;
|
|
|
|
// These two don't seem to work.
|
|
if (type.Name == "SceneControl" || type.Name == "ConfigUI") return;
|
|
|
|
//if (type.FullName.Contains("RootMotion")) return;
|
|
//if (type.Methods.Any(m => m.Body != null && m.Body.Variables.Any(v => v.VariableType.FullName.Contains("<")))) return;
|
|
//if (!type.FullName.Contains("H_VoiceControl")) return;
|
|
//if (!type.FullName.Contains("Human")) return;
|
|
//if (type.Namespace.Length > 1) return;
|
|
|
|
|
|
// Take care of sub types
|
|
foreach (var subType in type.NestedTypes)
|
|
{
|
|
VirtualizeType(subType);
|
|
}
|
|
|
|
foreach (var method in type.Methods)
|
|
{
|
|
Console.WriteLine(method.Name);
|
|
if (method.IsManaged
|
|
&& !TABOO_NAMES.Contains(method.Name)
|
|
&& method.IsIL
|
|
&& !method.IsStatic
|
|
&& !method.IsVirtual
|
|
&& !method.IsAbstract
|
|
&& !method.IsAddOn
|
|
&& !method.IsConstructor
|
|
&& !method.IsSpecialName
|
|
&& !method.IsGenericInstance
|
|
&& !method.HasOverrides)
|
|
{
|
|
method.IsVirtual = true;
|
|
method.IsPublic = true;
|
|
method.IsPrivate = false;
|
|
method.IsNewSlot = true;
|
|
method.IsHideBySig = true;
|
|
}
|
|
}
|
|
|
|
foreach (var field in type.Fields)
|
|
{
|
|
if (field.IsPrivate) field.IsFamily = true;
|
|
//field.IsPublic = true;
|
|
}
|
|
|
|
//foreach (var property in type.Properties)
|
|
//{
|
|
// property.GetMethod.IsVirtual = true;
|
|
// property.GetMethod.IsPublic = true;
|
|
// property.SetMethod.IsVirtual = true;
|
|
// property.SetMethod.IsPublic = true;
|
|
//}
|
|
|
|
}
|
|
|
|
private static bool IsPatched(ModuleDefinition module)
|
|
{
|
|
foreach (var @ref in module.AssemblyReferences)
|
|
{
|
|
if (@ref.Name == "IllusionInjector") return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static bool IsVirtualized(ModuleDefinition module)
|
|
{
|
|
return module.GetTypes().SelectMany(t => t.Methods.Where(m => m.Name == "Awake")).All(m => m.IsVirtual);
|
|
}
|
|
|
|
private static void Fail(string reason) {
|
|
throw new Exception(reason);
|
|
}
|
|
|
|
private static TypeDefinition FindEntryType(ModuleDefinition module)
|
|
{
|
|
return module.GetTypes().FirstOrDefault(IsEntryType);
|
|
}
|
|
|
|
private static bool IsEntryType(TypeDefinition type)
|
|
{
|
|
return ENTRY_TYPES.Contains(type.Name);
|
|
}
|
|
|
|
}
|
|
}
|